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.
Click to expand IoC.Configuration.ConfigurationFile.ICustomAutoServiceCodeGenerator
1// Copyright (c) IoC.Configuration Project. All rights reserved.
2// Licensed under the MIT License. See LICENSE in the solution root for license information.
3
4using JetBrains.Annotations;
5using OROptimizer.DynamicCode;
6using OROptimizer.ServiceResolver;
7using System;
8
9namespace IoC.Configuration.ConfigurationFile
10{
11 /// <summary>
12 /// Generates an implementation class for interfaces specified in autoServiceCustom element in configuration file.
13 /// </summary>
14 public interface ICustomAutoServiceCodeGenerator
15 {
16 /// <summary>
17 /// Validates the configuration of "autoServiceCustom" element.
18 /// Throws an exception if the configuration is invalid.
19 /// </summary>
20 /// <param name="customAutoGeneratedServiceInfo"></param>
21 void Validate([NotNull] ICustomAutoGeneratedServiceInfo customAutoGeneratedServiceInfo);
22
23 /// <summary>
24 /// Generates a C# code for the auto-implemented interface specified in "autoServiceCustom" element.
25 /// </summary>
26 /// <param name="customAutoGeneratedServiceInfo">Configuration of auto-implemented service configuration.</param>
27 /// <param name="dynamicAssemblyBuilder">
28 /// An instance of <see cref="IDynamicAssemblyBuilder"/>.
29 /// Use methods <see cref="IDynamicAssemblyBuilder.StartDynamicallyGeneratedClass(string, string)"/>,
30 /// <see cref="IDynamicAssemblyBuilder.StartDynamicallyGeneratedClass(string, System.Collections.Generic.IEnumerable{string}, string)"/>,
31 /// <see cref="IDynamicAssemblyBuilder.AddCSharpFile(string)"/>, etc., to generate the C# code for the implementation.
32 /// </param>
33 /// <param name="generatedClassNamespace">The generated class namespace.</param>
34 /// <param name="generatedClassName">The generated class name without namespace.</param>
35 void GenerateCSharp([NotNull] ICustomAutoGeneratedServiceInfo customAutoGeneratedServiceInfo,
36 [NotNull] IDynamicAssemblyBuilder dynamicAssemblyBuilder,
37 [NotNull] string generatedClassNamespace, [NotNull] string generatedClassName);
38
39 /// <summary>
40 /// Validates the configuration of "autoServiceCustom" element after the IoC container is loaded.
41 /// Throws an exception if the configuration is invalid.
42 /// </summary>
43 /// <param name="diContainer">The IoC container. Use IDiContainer.<see cref="IServiceResolver.Resolve(Type)"/> to resolve types.</param>
44 /// <param name="customAutoGeneratedServiceInfo">Information about auto-implemented interface.</param>
45 void ValidateOnIoCContainerLoaded([NotNull] DiContainer.IDiContainer diContainer, [NotNull] ICustomAutoGeneratedServiceInfo customAutoGeneratedServiceInfo);
46 }
47}
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 IoCConfiguration_autoServiceCustom.xml 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.
1<dependencyInjection>
2 <modules>
3 </modules>
4
5 <services>
6 </services>
7
8 <autoGeneratedServices>
9
10 <!--Interface specified in autoServiceCustom is auto-implemented by implementation of
11 IoC.Configuration.ConfigurationFile.ICustomAutoServiceCodeGenerator IoC.Configuration.Tests.AutoServiceCustom.SimpleDataRepository.RepositoryInterfaceImplementationGenerator
12 that is specified in autoServiceCodeGenerator element.-->
13
14 <autoServiceCustom interface="IoC.Configuration.Tests.DocumentationTests.AutoServiceCustom.ISimpleAutoImplementedInterface1">
15 <autoServiceCodeGenerator>
16 <constructedValue typeRef="DemoCustomAutoServiceCodeGenerator">
17 <parameters>
18 <classMember name="connectionString"
19 classRef="ConnectionStrings"
20 memberName="ConnectionString1" />
21 </parameters>
22 </constructedValue>
23 </autoServiceCodeGenerator>
24 </autoServiceCustom>
25
26 <autoServiceCustom interface="IoC.Configuration.Tests.DocumentationTests.AutoServiceCustom.ISimpleAutoImplementedInterface2">
27 <autoServiceCodeGenerator>
28 <constructedValue typeRef="DemoCustomAutoServiceCodeGenerator">
29 <parameters>
30 <classMember name="connectionString"
31 classRef="ConnectionStrings"
32 memberName="ConnectionString1" />
33 </parameters>
34 </constructedValue>
35 </autoServiceCodeGenerator>
36 </autoServiceCustom>
37 </autoGeneratedServices>
38</dependencyInjection>
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.
1using System;
2using System.IO;
3using System.Linq;
4using System.Reflection;
5using IoC.Configuration.ConfigurationFile;
6using IoC.Configuration.DiContainer;
7using IoC.Configuration.Tests.AutoServiceCustom.SimpleDataRepository;
8using NUnit.Framework;
9using OROptimizer;
10using OROptimizer.DynamicCode;
11using SharedServices.Interfaces;
12
13namespace IoC.Configuration.Tests.DocumentationTests.AutoServiceCustom;
14
15/// <summary>
16/// This is a simple demo to demonstrate an implementation of <see cref="ICustomAutoServiceCodeGenerator"/>.
17/// For a better example reference <see cref="RepositoryInterfaceImplementationGenerator"/> used in tests
18/// in AutoServiceCustom.
19/// The best use of <see cref="ICustomAutoServiceCodeGenerator"/> is to generate interface implementation based
20/// on attributes applied to interface and interface methods (such as auto-generating entity framework interfaces based on
21/// table names, and column metadata attributes).
22/// </summary>
23public class DemoCustomAutoServiceCodeGenerator : ICustomAutoServiceCodeGenerator
24{
25 public DemoCustomAutoServiceCodeGenerator(string connectionString)
26 {
27 // Demo passing parameters to ICustomAutoServiceCodeGenerator in configuration file.
28 Assert.AreEqual(ConnectionStrings.ConnectionString1, connectionString);
29 }
30
31 /// <inheritdoc />
32 public void Validate(ICustomAutoGeneratedServiceInfo customAutoGeneratedServiceInfo)
33 {
34 var implementedInterfaceType = customAutoGeneratedServiceInfo.ImplementedInterface;
35
36 if (!implementedInterfaceType.IsInterface ||
37 implementedInterfaceType.GetInterfaces().Length > 0 ||
38 implementedInterfaceType.GetMethods().Length != 1 ||
39 implementedInterfaceType.GetProperties().Length != 0)
40 throw new Exception($"The demo auto-implemented interface should not have a parent interfaces and should have exactly one method.");
41
42 var methodInfo = implementedInterfaceType.GetMethods().First();
43
44 if (methodInfo.GetParameters().Length != 0 || methodInfo.ReturnType != typeof(int))
45 throw new Exception($"The demo auto-implemented method should be veryyy simple to be short!!.");
46
47 if (methodInfo.GetCustomAttributes().FirstOrDefault(x => x is SimpleMethodMetadataAttribute) == null)
48 throw new Exception($"Method should have an attribute of type '{typeof(SimpleMethodMetadataAttribute)}'.");
49 }
50
51 /// <inheritdoc />
52 public void GenerateCSharp(ICustomAutoGeneratedServiceInfo customAutoGeneratedServiceInfo, IDynamicAssemblyBuilder dynamicAssemblyBuilder, string generatedClassNamespace, string generatedClassName)
53 {
54 // Use IDynamicAssemblyBuilder.AddReferencedAssembly(string assemblyPath) or
55 // IDynamicAssemblyBuilder.AddReferencedAssembly(Type typeInAssembly) to add assemblies that will be
56 // referenced by auto-generated assembly if types in these assemblies are used in auto-generated code.
57 dynamicAssemblyBuilder.AddReferencedAssembly(Path.Combine(Helpers.GetTestFilesFolderPath(), @"DynamicallyLoadedDlls\TestProjects.DynamicallyLoadedAssembly1.dll"));
58 dynamicAssemblyBuilder.AddReferencedAssembly(typeof(IInterface1));
59
60 // By now Validate(ICustomAutoGeneratedServiceInfo customAutoGeneratedServiceInfo) already validated
61 // that a single method with attribute is present in interface.
62 var methodInfo = customAutoGeneratedServiceInfo.ImplementedInterface.GetMethods().First();
63
64 var attribute = (SimpleMethodMetadataAttribute)methodInfo.GetCustomAttributes().FirstOrDefault(x => x is SimpleMethodMetadataAttribute);
65
66 var dynamicClass = dynamicAssemblyBuilder.StartDynamicallyGeneratedClass(generatedClassName,
67 new[]
68 {
69 customAutoGeneratedServiceInfo.ImplementedInterface.GetTypeNameInCSharpClass()
70 },
71 generatedClassNamespace);
72
73 var methodData = dynamicClass.StartInterfaceImplementationMethod(methodInfo, false);
74
75 methodData.AddCodeLine("{");
76
77 methodData.AddCodeLine("var testReferencedAssembly = new DynamicallyLoadedAssembly1.Dog(40);");
78
79 methodData.AddCodeLine($"return {attribute.ReturnedValue};");
80 methodData.AddCodeLine("}");
81 }
82
83 /// <inheritdoc />
84 public void ValidateOnIoCContainerLoaded(IDiContainer diContainer, ICustomAutoGeneratedServiceInfo customAutoGeneratedServiceInfo)
85 {
86 // At this point the DI container diContainer is loaded. Do validation using some services in container
87 // and throw an exception if necessary
88 //diContainer.Resolve()
89 }
90}
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.
1// Copyright (c) IoC.Configuration Project. All rights reserved.
2// Licensed under the MIT License. See LICENSE in the solution root for license information.
3
4using System.IO;
5using IoC.Configuration.DiContainerBuilder;
6using IoC.Configuration.DiContainerBuilder.FileBased;
7using NUnit.Framework;
8using OROptimizer.Utilities.Xml;
9using TestsSharedLibrary;
10
11namespace IoC.Configuration.Tests.DocumentationTests.AutoServiceCustom;
12
13[TestFixture]
14public class DemoAutoServiceCustom
15{
16 private static IContainerInfo _containerInfo;
17
18 [SetUp]
19 public static void TestSetUp()
20 {
21 TestsHelper.SetupLogger();
22
23 var fileBasedConfigurationParameters = new FileBasedConfigurationParameters(
24 new FileBasedConfigurationFileContentsProvider(
25 Path.Combine(Helpers.TestsEntryAssemblyFolder, @"DocumentationTests\AutoServiceCustom\DemoIoCConfiguration_autoServiceCustom.xml")),
26 Helpers.TestsEntryAssemblyFolder,
27 // LoadedAssembliesForTests is an implementation of ILoadedAssemblies that has a method
28 // "IEnumerable<Assembly> GetAssemblies()" that returns list of assemblies to add as references to
29 // generate dynamic assembly.
30 new LoadedAssembliesForTests())
31 {
32 AdditionalReferencedAssemblies = new []
33 {
34 // List additional assemblies that should be added to dynamically generated assembly as references
35 Path.Combine(Helpers.GetTestFilesFolderPath(), @"DynamicallyLoadedDlls\TestProjects.DynamicallyLoadedAssembly1.dll"),
36 Path.Combine(Helpers.GetTestFilesFolderPath(), @"DynamicallyLoadedDlls\TestProjects.DynamicallyLoadedAssembly2.dll")
37 },
38 AttributeValueTransformers = new[] {new FileFolderPathAttributeValueTransformer()},
39 ConfigurationFileXmlDocumentLoaded = (sender, e) =>
40 Helpers.EnsureConfigurationDirectoryExistsOrThrow(e.XmlDocument.SelectElement("/iocConfiguration/appDataDir").GetAttribute("path"))
41 };
42
43 _containerInfo = new DiContainerBuilder.DiContainerBuilder()
44 .StartFileBasedDi(fileBasedConfigurationParameters)
45 .WithoutPresetDiContainer()
46 .RegisterModules().Start();
47 }
48
49 [Test]
50 public void Demo()
51 {
52 var simpleAutoImplementedInterface1 = _containerInfo.DiContainer.Resolve<ISimpleAutoImplementedInterface1>();
53 Assert.AreEqual(10, simpleAutoImplementedInterface1.GetValue());
54
55 var simpleAutoImplementedInterface2 = _containerInfo.DiContainer.Resolve<ISimpleAutoImplementedInterface2>();
56 Assert.AreEqual(20, simpleAutoImplementedInterface2.GetSomeOtherValue());
57 }
58
59 [TearDown]
60 public static void TestTeaDown()
61 {
62 _containerInfo.Dispose();
63 }
64}