============================= Element **autoServiceCustom** ============================= The XML configuration file has an element **autoServiceCustom** that can appear under "iocConfiguration/dependencyInjection/autoGeneratedServices" and "iocConfiguration/pluginsSetup/pluginSetup/dependencyInjection/autoGeneratedServices" elements. The **autoServiceCustom** element specifies the interface that needs to be auto-implemented and has a child element **autoServiceCodeGenerator** that specifies an implementation of interface `IoC.Configuration.ConfigurationFile.ICustomAutoServiceCodeGenerator `_. The implementation of interface `IoC.Configuration.ConfigurationFile.ICustomAutoServiceCodeGenerator `_ is responsible for validating the auto-generated interface and for providing a code for the auto-generating the interface implementation. See the example below or in test project for more details. Below is the declaration of `IoC.Configuration.ConfigurationFile.ICustomAutoServiceCodeGenerator `_. .. raw:: html
Click to expand IoC.Configuration.ConfigurationFile.ICustomAutoServiceCodeGenerator .. code-block:: csharp :linenos: // Copyright (c) IoC.Configuration Project. All rights reserved. // Licensed under the MIT License. See LICENSE in the solution root for license information. using JetBrains.Annotations; using OROptimizer.DynamicCode; using OROptimizer.ServiceResolver; using System; namespace IoC.Configuration.ConfigurationFile { /// /// Generates an implementation class for interfaces specified in autoServiceCustom element in configuration file. /// public interface ICustomAutoServiceCodeGenerator { /// /// Validates the configuration of "autoServiceCustom" element. /// Throws an exception if the configuration is invalid. /// /// void Validate([NotNull] ICustomAutoGeneratedServiceInfo customAutoGeneratedServiceInfo); /// /// Generates a C# code for the auto-implemented interface specified in "autoServiceCustom" element. /// /// Configuration of auto-implemented service configuration. /// /// An instance of . /// Use methods , /// , /// , etc., to generate the C# code for the implementation. /// /// The generated class namespace. /// The generated class name without namespace. void GenerateCSharp([NotNull] ICustomAutoGeneratedServiceInfo customAutoGeneratedServiceInfo, [NotNull] IDynamicAssemblyBuilder dynamicAssemblyBuilder, [NotNull] string generatedClassNamespace, [NotNull] string generatedClassName); /// /// Validates the configuration of "autoServiceCustom" element after the IoC container is loaded. /// Throws an exception if the configuration is invalid. /// /// The IoC container. Use IDiContainer. to resolve types. /// Information about auto-implemented interface. void ValidateOnIoCContainerLoaded([NotNull] DiContainer.IDiContainer diContainer, [NotNull] ICustomAutoGeneratedServiceInfo customAutoGeneratedServiceInfo); } } .. raw:: html
The motivation for adding **autoServiceCustom** was to provide an auto-implemented interface implementation based on **C#** attributes applied to the interface and in interface and interface methods and properties. For example one such scenario when **autoServiceCustom** might be handy is when we want to auto-implement interfaces for object relational mapping, when interfaces represent database tables, and are decorated with metadata attributes that describe the database schema. In this case, an implementation of `IoC.Configuration.ConfigurationFile.ICustomAutoServiceCodeGenerator `_ can scan the attributes applied to an interface and interface methods and properties, and generate an implementation of an interface based on metadata attributes. The tests in `IoC.Configuration.Tests.AutoServiceCustom.AutoServiceCustomSuccessfulLoadTests.cs `_ demonstrate an example of auto-generated repository interface implementations based on attributes applied to the interface. .. note:: Refer to :doc:`../../sample-files/IoCConfiguration_autoServiceCustom.generated` and tests in `IoC.Configuration.Tests.AutoServiceCustom.AutoServiceCustomSuccessfulLoadTests.cs `_ for more examples on **autoServiceCustom** element. .. note:: This wiki uses simpler examples in `IoC.Configuration.Tests.DocumentationTests.AutoServiceCustom.DemoAutoServiceCustom.cs `_. Below is a simple example of setting up custom auto-service in configuration file (a segment copied from configuration file `DemoIoCConfiguration_autoServiceCustom.xml `_. .. code-block:: xml :linenos: This configuration instructs **IoC.Configuration** to generate an implementation of interfaces `IoC.Configuration.Tests.DocumentationTests.AutoServiceCustom.ISimpleAutoImplementedInterface1.cs `_ and `IoC.Configuration.Tests.DocumentationTests.AutoServiceCustom.ISimpleAutoImplementedInterface2.cs `_ using class `IoC.Configuration.Tests.DocumentationTests.AutoServiceCustom.DemoCustomAutoServiceCodeGenerator.cs `_ specified in child element **autoServiceCodeGenerator**. Below is the code in `IoC.Configuration.Tests.DocumentationTests.AutoServiceCustom.DemoCustomAutoServiceCodeGenerator `_. .. code-block:: csharp :linenos: using System; using System.IO; using System.Linq; using System.Reflection; using IoC.Configuration.ConfigurationFile; using IoC.Configuration.DiContainer; using IoC.Configuration.Tests.AutoServiceCustom.SimpleDataRepository; using NUnit.Framework; using OROptimizer; using OROptimizer.DynamicCode; using SharedServices.Interfaces; namespace IoC.Configuration.Tests.DocumentationTests.AutoServiceCustom; /// /// This is a simple demo to demonstrate an implementation of . /// For a better example reference used in tests /// in AutoServiceCustom. /// The best use of is to generate interface implementation based /// on attributes applied to interface and interface methods (such as auto-generating entity framework interfaces based on /// table names, and column metadata attributes). /// public class DemoCustomAutoServiceCodeGenerator : ICustomAutoServiceCodeGenerator { public DemoCustomAutoServiceCodeGenerator(string connectionString) { // Demo passing parameters to ICustomAutoServiceCodeGenerator in configuration file. Assert.AreEqual(ConnectionStrings.ConnectionString1, connectionString); } /// public void Validate(ICustomAutoGeneratedServiceInfo customAutoGeneratedServiceInfo) { var implementedInterfaceType = customAutoGeneratedServiceInfo.ImplementedInterface; if (!implementedInterfaceType.IsInterface || implementedInterfaceType.GetInterfaces().Length > 0 || implementedInterfaceType.GetMethods().Length != 1 || implementedInterfaceType.GetProperties().Length != 0) throw new Exception($"The demo auto-implemented interface should not have a parent interfaces and should have exactly one method."); var methodInfo = implementedInterfaceType.GetMethods().First(); if (methodInfo.GetParameters().Length != 0 || methodInfo.ReturnType != typeof(int)) throw new Exception($"The demo auto-implemented method should be veryyy simple to be short!!."); if (methodInfo.GetCustomAttributes().FirstOrDefault(x => x is SimpleMethodMetadataAttribute) == null) throw new Exception($"Method should have an attribute of type '{typeof(SimpleMethodMetadataAttribute)}'."); } /// public void GenerateCSharp(ICustomAutoGeneratedServiceInfo customAutoGeneratedServiceInfo, IDynamicAssemblyBuilder dynamicAssemblyBuilder, string generatedClassNamespace, string generatedClassName) { // Use IDynamicAssemblyBuilder.AddReferencedAssembly(string assemblyPath) or // IDynamicAssemblyBuilder.AddReferencedAssembly(Type typeInAssembly) to add assemblies that will be // referenced by auto-generated assembly if types in these assemblies are used in auto-generated code. dynamicAssemblyBuilder.AddReferencedAssembly(Path.Combine(Helpers.GetTestFilesFolderPath(), @"DynamicallyLoadedDlls\TestProjects.DynamicallyLoadedAssembly1.dll")); dynamicAssemblyBuilder.AddReferencedAssembly(typeof(IInterface1)); // By now Validate(ICustomAutoGeneratedServiceInfo customAutoGeneratedServiceInfo) already validated // that a single method with attribute is present in interface. var methodInfo = customAutoGeneratedServiceInfo.ImplementedInterface.GetMethods().First(); var attribute = (SimpleMethodMetadataAttribute)methodInfo.GetCustomAttributes().FirstOrDefault(x => x is SimpleMethodMetadataAttribute); var dynamicClass = dynamicAssemblyBuilder.StartDynamicallyGeneratedClass(generatedClassName, new[] { customAutoGeneratedServiceInfo.ImplementedInterface.GetTypeNameInCSharpClass() }, generatedClassNamespace); var methodData = dynamicClass.StartInterfaceImplementationMethod(methodInfo, false); methodData.AddCodeLine("{"); methodData.AddCodeLine("var testReferencedAssembly = new DynamicallyLoadedAssembly1.Dog(40);"); methodData.AddCodeLine($"return {attribute.ReturnedValue};"); methodData.AddCodeLine("}"); } /// public void ValidateOnIoCContainerLoaded(IDiContainer diContainer, ICustomAutoGeneratedServiceInfo customAutoGeneratedServiceInfo) { // At this point the DI container diContainer is loaded. Do validation using some services in container // and throw an exception if necessary //diContainer.Resolve() } } Look at test class below for an example of setting up and initializing the DI container from configuration file, and resolving and using auto-generated interfaces `IoC.Configuration.Tests.DocumentationTests.AutoServiceCustom.ISimpleAutoImplementedInterface1.cs `_ and `IoC.Configuration.Tests.DocumentationTests.AutoServiceCustom.ISimpleAutoImplementedInterface2.cs `_. .. code-block:: csharp :linenos: // Copyright (c) IoC.Configuration Project. All rights reserved. // Licensed under the MIT License. See LICENSE in the solution root for license information. using System.IO; using IoC.Configuration.DiContainerBuilder; using IoC.Configuration.DiContainerBuilder.FileBased; using NUnit.Framework; using OROptimizer.Utilities.Xml; using TestsSharedLibrary; namespace IoC.Configuration.Tests.DocumentationTests.AutoServiceCustom; [TestFixture] public class DemoAutoServiceCustom { private static IContainerInfo _containerInfo; [SetUp] public static void TestSetUp() { TestsHelper.SetupLogger(); var fileBasedConfigurationParameters = new FileBasedConfigurationParameters( new FileBasedConfigurationFileContentsProvider( Path.Combine(Helpers.TestsEntryAssemblyFolder, @"DocumentationTests\AutoServiceCustom\DemoIoCConfiguration_autoServiceCustom.xml")), Helpers.TestsEntryAssemblyFolder, // LoadedAssembliesForTests is an implementation of ILoadedAssemblies that has a method // "IEnumerable GetAssemblies()" that returns list of assemblies to add as references to // generate dynamic assembly. new LoadedAssembliesForTests()) { AdditionalReferencedAssemblies = new [] { // List additional assemblies that should be added to dynamically generated assembly as references Path.Combine(Helpers.GetTestFilesFolderPath(), @"DynamicallyLoadedDlls\TestProjects.DynamicallyLoadedAssembly1.dll"), Path.Combine(Helpers.GetTestFilesFolderPath(), @"DynamicallyLoadedDlls\TestProjects.DynamicallyLoadedAssembly2.dll") }, AttributeValueTransformers = new[] {new FileFolderPathAttributeValueTransformer()}, ConfigurationFileXmlDocumentLoaded = (sender, e) => Helpers.EnsureConfigurationDirectoryExistsOrThrow(e.XmlDocument.SelectElement("/iocConfiguration/appDataDir").GetAttribute("path")) }; _containerInfo = new DiContainerBuilder.DiContainerBuilder() .StartFileBasedDi(fileBasedConfigurationParameters) .WithoutPresetDiContainer() .RegisterModules().Start(); } [Test] public void Demo() { var simpleAutoImplementedInterface1 = _containerInfo.DiContainer.Resolve(); Assert.AreEqual(10, simpleAutoImplementedInterface1.GetValue()); var simpleAutoImplementedInterface2 = _containerInfo.DiContainer.Resolve(); Assert.AreEqual(20, simpleAutoImplementedInterface2.GetSomeOtherValue()); } [TearDown] public static void TestTeaDown() { _containerInfo.Dispose(); } }