After having read the MSDN article Adaptive Access Layers + Dependency Injection = Productivity and having Ulrik come tell us about AAL, I felt it could be a good exercise to create the smallest possible AAL sample. This is what I could come up with, using Roslyn as code-generator, and the LogAccessLayerAttribute class as the source code generator.
Adding a new method to the ISampleLog interface will automatically create a new concrete method in the generated SampleLog.cs class.
- namespace HelloWorldAdaptiveAccessLayerSample
- {
- class Program
- {
- static void Main(string[] args)
- {
- var logger = MyContainer.GetService<ISampleLog>();
- logger.ThisIsAWarning(100);
- logger.ThisIsAnError(99);
- logger.ThisIsAWarning(101);
- }
- }
- }
- namespace HelloWorldAdaptiveAccessLayerSample
- {
- using System.Diagnostics;
- [LogAccessLayer]
- public interface ISampleLog
- {
- [LogEntry(1, TraceEventType.Warning, “HelloWorld warning {0}”)]
- void ThisIsAWarning(int warningNumber);
- [LogEntry(2, TraceEventType.Error, “HelloWorld error {0}”)]
- void ThisIsAnError(int errorNumber);
- }
- }
- namespace HelloWorldAdaptiveAccessLayerSample
- {
- using System;
- using System.IO;
- using System.Linq;
- using System.Reflection;
- using Roslyn.Compilers;
- using Roslyn.Compilers.CSharp;
- public static class MyContainer
- {
- public static T GetService<T>()
- {
- var factory = GetServiceFactory<T>();
- var sourceText = factory.GetSourceText(typeof(T).GetMethods());
- var co = new CompilationOptions(OutputKind.DynamicallyLinkedLibrary);
- SyntaxTree tree = SyntaxTree.ParseText(sourceText);
- MetadataReference mscorlib = MetadataReference.CreateAssemblyReference(“mscorlib”);
- var helloWorld = new MetadataFileReference(typeof(MyContainer).Assembly.Location);
- Compilation compilation = Compilation.Create(factory.GetTypeName(), co)
- .AddReferences(mscorlib)
- .AddReferences(helloWorld)
- .AddSyntaxTrees(tree);
- var sampleAssemblyName = Path.Combine(Path.GetDirectoryName(typeof(MyContainer).Assembly.Location), “SampleLog.dll”);
- using (var stream = new FileStream(sampleAssemblyName, FileMode.Create))
- {
- var result = compilation.Emit(stream);
- }
- var asm = Assembly.LoadFile(sampleAssemblyName);
- var type = asm.GetTypes().Where(p => p.Name.Equals(factory.GetTypeName())).SingleOrDefault();
- return (T)(object)Activator.CreateInstance(type, false);
- }
- privatestaticIServiceFactory GetServiceFactory<T>()
- {
- var t = typeof(T);
- var logAccessLayer = t.GetCustomAttribute<LogAccessLayerAttribute>();
- if (logAccessLayer != null)
- {
- var instance = Activator.CreateInstance<LogAccessLayerAttribute>() asIServiceFactory;
- return instance;
- }
- return null;
- }
- }
- }
- namespace HelloWorldAdaptiveAccessLayerSample
- {
- using System.Reflection;
- public interface IServiceFactory
- {
- string GetSourceText(MethodInfo[] methods);
- string GetTypeName();
- }
- }
- namespace HelloWorldAdaptiveAccessLayerSample
- {
- using System;
- using System.Reflection;
- using System.Text;
- class LogAccessLayerAttribute : Attribute, IServiceFactory
- {
- public static LogAccessLayerAttribute GetFactory()
- {
- return new LogAccessLayerAttribute();
- }
- publicstring GetSourceText(MethodInfo[] methods)
- {
- var sourceText =
- @”namespace HelloWorldAdaptiveAccessLayerSample
- {
- using System;
- public class SampleLog : ISampleLog
- {
- public SampleLog() { }
- // ##Methods
- }
- }”;
- var sb = newStringBuilder();
- foreach(var method in methods)
- {
- var arguments = GetArguments(method);
- var signature = string.Format(“{0} {1} {2}({3})”,
- method.IsPublic ? “public” : “private”,
- GetReturnType(method.ReturnType),
- method.Name,
- arguments);
- sb.Append(signature);
- var logEntry = method.GetCustomAttribute<LogEntryAttribute>();
- if (logEntry != null)
- {
- sb.AppendLine(“{“);
- if (logEntry.TraceEventType == System.Diagnostics.TraceEventType.Error)
- {
- sb.AppendLine(“Console.ForegroundColor = ConsoleColor.DarkRed;”);
- }
- sb.AppendLine(string.Format(” Console.WriteLine(\”{0} {1}\”, {2});”, logEntry.FormatString, logEntry.EventId, method.GetParameters()[0].Name));
- if (logEntry.TraceEventType == System.Diagnostics.TraceEventType.Error)
- {
- sb.AppendLine(“Console.ForegroundColor = ConsoleColor.White;”);
- }
- sb.AppendLine(“}”);
- }
- }
- return sourceText.Replace(“// ##Methods”, sb.ToString());
- }
- privatestring GetReturnType(Type type)
- {
- switch(type.ToString())
- {
- case“System.Void”:
- return“void”;
- default:
- return type.ToString();
- }
- }
- publicstring GetTypeName()
- {
- return“SampleLog”;
- }
- privatestring GetArguments(MethodInfo method)
- {
- var sb = newStringBuilder();
- foreach (var parameter in method.GetParameters())
- {
- sb.Append(string.Format(“{0} {1},”, parameter.ParameterType.ToString(), parameter.Name));
- }
- return sb.ToString().Substring(0, sb.Length – 1);
- }
- }
- }
- namespace HelloWorldAdaptiveAccessLayerSample
- {
- using System;
- using System.Diagnostics;
- public class LogEntryAttribute : Attribute
- {
- public int EventId { get; privateset; }
- public TraceEventType TraceEventType { get; privateset; }
- public string FormatString { get; privateset; }
- public LogEntryAttribute(int eventId, TraceEventType traceType, string formatString)
- {
- this.EventId = eventId;
- this.TraceEventType = traceType;
- this.FormatString = formatString;
- }
- }
- }