I'll try to make this as clear as possible.
A Plugin architecture using reflection and 2 Attributes and an abstract class:
PluginEntryAttribute(Targets.Assembly, typeof(MyPlugin))
PluginImplAttribute(Targets.Class, ...)
abstract class Plugin
Commands are routed to a plugin via an interface and a delegate:
Ex: public delegate TTarget Command<TTarget>(object obj);
Using extension methods with Command<> as the target, a CommandRouter executes the delegate on the correct target interface:
Ex:
public static TResult Execute<TTarget, TResult>(this Command<TTarget> target, Func<TTarget, TResult> func) {
return CommandRouter.Default.Execute(func);
}
Putting this together, I have a class hard-coded with the command delegates like so:
public class Repositories {
public static Command<IDispatchingRepository> Dispatching = (o) => { return (IDispatchingRepository)o; };
public static Command<IPositioningRepository> Positioning = (o) => { return (IPositioningRepository)o; };
public static Command<ISchedulingRepository> Scheduling = (o) => { return (ISchedulingRepository)o; };
public static Command<IHistographyRepository> Histography = (o) => { return (IHistographyRepository)o; };
}
When an object wants to query from the repository, practical execution looks like this:
var expBob = Dispatching.Execute(repo => repo.AddCustomer("Bob"));
var actBob = Dispatching.Execute(repo => repo.GetCustomer("Bob"));
My question is this: how can I create such a class as Repositories dynamically from the plugins?
I can see the possibility that another attribute might be necessary. Something along the lines of:
[RoutedCommand("Dispatching", typeof(IDispatchingRepository)")]
public Command<IDispatchingRepository> Dispatching = (o) => { return (IDispatchingRepository)o; };
This is just an idea, but I'm at a loss as to how I'd still create a dynamic menu of sorts like the Repositories class.
For completeness, the CommandRouter.Execute(...) method and related Dictionary<,>:
private readonly Dictionary<Type, object> commandTargets;
internal TResult Execute<TTarget, TResult>(Func<TTarget, TResult> func) {
var result = default(TResult);
if (commandTargets.TryGetValue(typeof(TTarget), out object target)) {
result = func((TTarget)target);
}
return result;
}
OK, i am not sure if this is what you are looking for. I am assuming that each plugin contains field of the following definition:
public Command<T> {Name} = (o) => { return (T)o; };
example from code provided by you:
public Command<IDispatchingRepository> Dispatching = (o) => { return (IDispatchingRepository)o; };
One way to dynamically create class in .NET Core is by using the Microsoft.CodeAnalysis.CSharp nuget - this is Roslyn.
The result is compiled assembly with class called DynamicRepositories having all command fields from all plugins from all loaded dlls into the current AppDomain represented as static public fields.
The code has 3 main components: DynamicRepositoriesBuildInfo class, GetDynamicRepositoriesBuildInfo method and LoadDynamicRepositortyIntoAppDomain method.
DynamicRepositoriesBuildInfo - information for the command fields from the plugins and all assemblies needed to be loaded during the dynamic complication. This will be the assemblies which defines the Command type and the generic arguments of the Command type (ex: IDispatchingRepository)
GetDynamicRepositoriesBuildInfo method - creates DynamicRepositoriesBuildInfo using reflection by scanning loaded assemblies for the PluginEntryAttribute and PluginImplAttribute.
LoadDynamicRepositortyIntoAppDomain method - DynamicRepositoriesBuildInfo it creates assembly called DynamicRepository.dll with single public class App.Dynamic.DynamicRepositories
Here is the code
public class DynamicRepositoriesBuildInfo
{
public IReadOnlyCollection<Assembly> ReferencesAssemblies { get; }
public IReadOnlyCollection<FieldInfo> PluginCommandFieldInfos { get; }
public DynamicRepositoriesBuildInfo(
IReadOnlyCollection<Assembly> referencesAssemblies,
IReadOnlyCollection<FieldInfo> pluginCommandFieldInfos)
{
this.ReferencesAssemblies = referencesAssemblies;
this.PluginCommandFieldInfos = pluginCommandFieldInfos;
}
}
private static DynamicRepositoriesBuildInfo GetDynamicRepositoriesBuildInfo()
{
var pluginCommandProperties = (from a in AppDomain.CurrentDomain.GetAssemblies()
let entryAttr = a.GetCustomAttribute<PluginEntryAttribute>()
where entryAttr != null
from t in a.DefinedTypes
where t == entryAttr.PluginType
from p in t.GetFields(BindingFlags.Public | BindingFlags.Instance)
where p.FieldType.GetGenericTypeDefinition() == typeof(Command<>)
select p).ToList();
var referenceAssemblies = pluginCommandProperties
.Select(x => x.DeclaringType.Assembly)
.ToList();
referenceAssemblies.AddRange(
pluginCommandProperties
.SelectMany(x => x.FieldType.GetGenericArguments())
.Select(x => x.Assembly)
);
var buildInfo = new DynamicRepositoriesBuildInfo(
pluginCommandFieldInfos: pluginCommandProperties,
referencesAssemblies: referenceAssemblies.Distinct().ToList()
);
return buildInfo;
}
private static Assembly LoadDynamicRepositortyIntoAppDomain()
{
var buildInfo = GetDynamicRepositoriesBuildInfo();
var csScriptBuilder = new StringBuilder();
csScriptBuilder.AppendLine("using System;");
csScriptBuilder.AppendLine("namespace App.Dynamic");
csScriptBuilder.AppendLine("{");
csScriptBuilder.AppendLine(" public class DynamicRepositories");
csScriptBuilder.AppendLine(" {");
foreach (var commandFieldInfo in buildInfo.PluginCommandFieldInfos)
{
var commandNamespaceStr = commandFieldInfo.FieldType.Namespace;
var commandTypeStr = commandFieldInfo.FieldType.Name.Split('`')[0];
var commandGenericArgStr = commandFieldInfo.FieldType.GetGenericArguments().Single().FullName;
var commandFieldNameStr = commandFieldInfo.Name;
csScriptBuilder.AppendLine($"public {commandNamespaceStr}.{commandTypeStr}<{commandGenericArgStr}> {commandFieldNameStr} => (o) => ({commandGenericArgStr})o;");
}
csScriptBuilder.AppendLine(" }");
csScriptBuilder.AppendLine("}");
var sourceText = SourceText.From(csScriptBuilder.ToString());
var parseOpt = CSharpParseOptions.Default.WithLanguageVersion(LanguageVersion.CSharp7_3);
var syntaxTree = SyntaxFactory.ParseSyntaxTree(sourceText, parseOpt);
var references = new List<MetadataReference>
{
MetadataReference.CreateFromFile(typeof(object).Assembly.Location),
MetadataReference.CreateFromFile(typeof(System.Runtime.AssemblyTargetedPatchBandAttribute).Assembly.Location),
};
references.AddRange(buildInfo.ReferencesAssemblies.Select(a => MetadataReference.CreateFromFile(a.Location)));
var compileOpt = new CSharpCompilationOptions(OutputKind.DynamicallyLinkedLibrary,
optimizationLevel: OptimizationLevel.Release,
assemblyIdentityComparer: DesktopAssemblyIdentityComparer.Default);
var compilation = CSharpCompilation.Create(
"DynamicRepository.dll",
new[] { syntaxTree },
references: references,
options: compileOpt);
using (var memStream = new MemoryStream())
{
var result = compilation.Emit(memStream);
if (result.Success)
{
var assembly = AppDomain.CurrentDomain.Load(memStream.ToArray());
return assembly;
}
else
{
throw new ArgumentException();
}
}
}
This is how to execute the code
var assembly = LoadDynamicRepositortyIntoAppDomain();
var type = assembly.GetType("App.Dynamic.DynamicRepositories");
The type variable represents the compiled class which has all the plugin commands as public static fields. You are loosing all type safety once you start using dynamic code compilation / building. If you need to execute some code from the type variable you will need reflection.
So if you have
PluginA
{
public Command<IDispatchingRepository> Dispatching= (o) => ....
}
PluginB
{
public Command<IDispatchingRepository> Scheduling = (o) => ....
}
the dynamically create type will look like this
public class DynamicRepositories
{
public static Command<IDispatchingRepository> Dispatching= (o) => ....
public static Command<IDispatchingRepository> Scheduling = (o) => ....
}
Here's another take, which does not require building code dynamically.
I'm assuming the following code for the plugin framework. Note that I did not make any assumptions regarding the abstract Plugin class, because I had no further information.
#region Plugin Framework
public delegate TTarget Command<out TTarget>(object obj);
/// <summary>
/// Abstract base class for plugins.
/// </summary>
public abstract class Plugin
{
}
#endregion
Next, here are two sample plugins. Note the DynamicTarget custom attributes, which I will describe in the next step.
#region Sample Plugin: ICustomerRepository
/// <summary>
/// Sample model class, representing a customer.
/// </summary>
public class Customer
{
public Customer(string name)
{
Name = name;
}
public string Name { get; }
}
/// <summary>
/// Sample target interface.
/// </summary>
public interface ICustomerRepository
{
Customer AddCustomer(string name);
Customer GetCustomer(string name);
}
/// <summary>
/// Sample plugin.
/// </summary>
[DynamicTarget(typeof(ICustomerRepository))]
public class CustomerRepositoryPlugin : Plugin, ICustomerRepository
{
private readonly Dictionary<string, Customer> _customers = new Dictionary<string, Customer>();
public Customer AddCustomer(string name)
{
var customer = new Customer(name);
_customers[name] = customer;
return customer;
}
public Customer GetCustomer(string name)
{
return _customers[name];
}
}
#endregion
#region Sample Plugin: IProductRepository
/// <summary>
/// Sample model class, representing a product.
/// </summary>
public class Product
{
public Product(string name)
{
Name = name;
}
public string Name { get; }
}
/// <summary>
/// Sample target interface.
/// </summary>
public interface IProductRepository
{
Product AddProduct(string name);
Product GetProduct(string name);
}
/// <summary>
/// Sample plugin.
/// </summary>
[DynamicTarget(typeof(IProductRepository))]
public class ProductRepositoryPlugin : Plugin, IProductRepository
{
private readonly Dictionary<string, Product> _products = new Dictionary<string, Product>();
public Product AddProduct(string name)
{
var product = new Product(name);
_products[name] = product;
return product;
}
public Product GetProduct(string name)
{
return _products[name];
}
}
#endregion
Here's what your static Repositories class would look like with the two sample plugins:
#region Static Repositories Example Class from Question
public static class Repositories
{
public static readonly Command<ICustomerRepository> CustomerRepositoryCommand = o => (ICustomerRepository) o;
public static readonly Command<IProductRepository> ProductRepositoryCommand = o => (IProductRepository) o;
}
#endregion
To begin the actual answer to your question here's the custom attribute used to mark the plugins. This custom attribute has been used on the two example plugins shown above.
/// <summary>
/// Marks a plugin as the target of a <see cref="Command{TTarget}" />, specifying
/// the type to be registered with the <see cref="DynamicCommands" />.
/// </summary>
[AttributeUsage(AttributeTargets.Class, Inherited = false, AllowMultiple = true)]
public class DynamicTargetAttribute : Attribute
{
public DynamicTargetAttribute(Type type)
{
Type = type;
}
public Type Type { get; }
}
The custom attribute is parsed in the RegisterDynamicTargets(Assembly) of the following DynamicRepository class to identify the plugins and the types (e.g., ICustomerRepository) to be registered. The targets are registered with the CommandRouter shown below.
/// <summary>
/// A dynamic command repository.
/// </summary>
public static class DynamicCommands
{
/// <summary>
/// For all assemblies in the current domain, registers all targets marked with the
/// <see cref="DynamicTargetAttribute" />.
/// </summary>
public static void RegisterDynamicTargets()
{
foreach (Assembly assembly in AppDomain.CurrentDomain.GetAssemblies())
{
RegisterDynamicTargets(assembly);
}
}
/// <summary>
/// For the given <see cref="Assembly" />, registers all targets marked with the
/// <see cref="DynamicTargetAttribute" />.
/// </summary>
/// <param name="assembly"></param>
public static void RegisterDynamicTargets(Assembly assembly)
{
IEnumerable<Type> types = assembly
.GetTypes()
.Where(type => type.CustomAttributes
.Any(ca => ca.AttributeType == typeof(DynamicTargetAttribute)));
foreach (Type type in types)
{
// Note: This assumes that we simply instantiate an instance upon registration.
// You might have a different convention with your plugins (e.g., they might be
// singletons accessed via an Instance or Default property). Therefore, you
// might have to change this.
object target = Activator.CreateInstance(type);
IEnumerable<CustomAttributeData> customAttributes = type.CustomAttributes
.Where(ca => ca.AttributeType == typeof(DynamicTargetAttribute));
foreach (CustomAttributeData customAttribute in customAttributes)
{
CustomAttributeTypedArgument argument = customAttribute.ConstructorArguments.First();
CommandRouter.Default.RegisterTarget((Type) argument.Value, target);
}
}
}
/// <summary>
/// Registers the given target.
/// </summary>
/// <typeparam name="TTarget">The type of the target.</typeparam>
/// <param name="target">The target.</param>
public static void RegisterTarget<TTarget>(TTarget target)
{
CommandRouter.Default.RegisterTarget(target);
}
/// <summary>
/// Gets the <see cref="Command{TTarget}" /> for the given <typeparamref name="TTarget" />
/// type.
/// </summary>
/// <typeparam name="TTarget">The target type.</typeparam>
/// <returns>The <see cref="Command{TTarget}" />.</returns>
public static Command<TTarget> Get<TTarget>()
{
return obj => (TTarget) obj;
}
/// <summary>
/// Extension method used to help dispatch the command.
/// </summary>
/// <typeparam name="TTarget">The type of the target.</typeparam>
/// <typeparam name="TResult">The type of the result of the function invoked on the target.</typeparam>
/// <param name="_">The <see cref="Command{TTarget}" />.</param>
/// <param name="func">The function invoked on the target.</param>
/// <returns>The result of the function invoked on the target.</returns>
public static TResult Execute<TTarget, TResult>(this Command<TTarget> _, Func<TTarget, TResult> func)
{
return CommandRouter.Default.Execute(func);
}
}
Instead of dynamically creating properties, the above utility class offers a simple Command<TTarget> Get<TTarget>() method, with which you can create the Command<TTarget> instance, which is then used in the Execute extension method. The latter method finally delegates to the CommandRouter shown next.
/// <summary>
/// Command router used to dispatch commands to targets.
/// </summary>
public class CommandRouter
{
public static readonly CommandRouter Default = new CommandRouter();
private readonly Dictionary<Type, object> _commandTargets = new Dictionary<Type, object>();
/// <summary>
/// Registers a target.
/// </summary>
/// <typeparam name="TTarget">The type of the target instance.</typeparam>
/// <param name="target">The target instance.</param>
public void RegisterTarget<TTarget>(TTarget target)
{
_commandTargets[typeof(TTarget)] = target;
}
/// <summary>
/// Registers a target instance by <see cref="Type" />.
/// </summary>
/// <param name="type">The <see cref="Type" /> of the target.</param>
/// <param name="target">The target instance.</param>
public void RegisterTarget(Type type, object target)
{
_commandTargets[type] = target;
}
internal TResult Execute<TTarget, TResult>(Func<TTarget, TResult> func)
{
var result = default(TResult);
if (_commandTargets.TryGetValue(typeof(TTarget), out object target))
{
result = func((TTarget)target);
}
return result;
}
}
#endregion
Finally, here are a few unit tests showing how the above classes work.
#region Unit Tests
public class DynamicCommandTests
{
[Fact]
public void TestUsingStaticRepository_StaticDeclaration_Success()
{
ICustomerRepository customerRepository = new CustomerRepositoryPlugin();
CommandRouter.Default.RegisterTarget(customerRepository);
Command<ICustomerRepository> command = Repositories.CustomerRepositoryCommand;
Customer expected = command.Execute(repo => repo.AddCustomer("Bob"));
Customer actual = command.Execute(repo => repo.GetCustomer("Bob"));
Assert.Equal(expected, actual);
Assert.Equal("Bob", actual.Name);
}
[Fact]
public void TestUsingDynamicRepository_ManualRegistration_Success()
{
ICustomerRepository customerRepository = new CustomerRepositoryPlugin();
DynamicCommands.RegisterTarget(customerRepository);
Command<ICustomerRepository> command = DynamicCommands.Get<ICustomerRepository>();
Customer expected = command.Execute(repo => repo.AddCustomer("Bob"));
Customer actual = command.Execute(repo => repo.GetCustomer("Bob"));
Assert.Equal(expected, actual);
Assert.Equal("Bob", actual.Name);
}
[Fact]
public void TestUsingDynamicRepository_DynamicRegistration_Success()
{
// Register all plugins, i.e., CustomerRepositoryPlugin and ProductRepositoryPlugin
// in this test case.
DynamicCommands.RegisterDynamicTargets();
// Invoke ICustomerRepository methods on CustomerRepositoryPlugin target.
Command<ICustomerRepository> customerCommand = DynamicCommands.Get<ICustomerRepository>();
Customer expectedBob = customerCommand.Execute(repo => repo.AddCustomer("Bob"));
Customer actualBob = customerCommand.Execute(repo => repo.GetCustomer("Bob"));
Assert.Equal(expectedBob, actualBob);
Assert.Equal("Bob", actualBob.Name);
// Invoke IProductRepository methods on ProductRepositoryPlugin target.
Command<IProductRepository> productCommand = DynamicCommands.Get<IProductRepository>();
Product expectedHammer = productCommand.Execute(repo => repo.AddProduct("Hammer"));
Product actualHammer = productCommand.Execute(repo => repo.GetProduct("Hammer"));
Assert.Equal(expectedHammer, actualHammer);
Assert.Equal("Hammer", actualHammer.Name);
}
}
#endregion
You can find the whole implementation here.
Related
So I'm following an example of setting up LoggerMiddleware which is demonstrated here which I've adapted to just use .NET Core's ILogger. We already had a LoggerModule which work pre v6 so I copied the creation code into the LoggerMiddleware class to suit.
What I don't understand is how to register this. There's no default constructor on the Module so I can't use builder.RegisterModule<MiddlewareModule>(). I know I can pre-create the MiddlewareModule passing in an instance of the LoggerMiddleware then builder.RegisterModule(moduleInstance) but this MiddlewareModule is supposed to be used for all Middleware we create. This doco page doesn't elaborate on that final detail. Do I just do this per instance of IResolveMiddleware?:
var loggerMiddlewareModule = new MiddlewareModule(new LoggerMiddleware());
builder.RegisterModule(loggerMiddlewareModule);
var otherMiddlewareModule = new MiddlewareModule(new OtherMiddleware());
builder.RegisterModule(otherMiddlewareModule);
My Middleware and Module:
public class LoggerMiddleware : IResolveMiddleware
{
public PipelinePhase Phase => PipelinePhase.ParameterSelection;
public void Execute(ResolveRequestContext context, Action<ResolveRequestContext> next)
{
var t = context.Registration.Activator.LimitType;
// Add our parameters.
context.ChangeParameters(context.Parameters.Union(
new[]
{
new ResolvedParameter((p, i) => p.ParameterType == typeof(ILogger), (p, i) => GetLogger(i, t)),
new ResolvedParameter(
(p, i) => p.ParameterType.GenericTypeArguments.Any() &&
p.ParameterType.GetGenericTypeDefinition() == typeof(ILogger<>),
(p, i) => GetGenericTypeLogger(i, t))
}));
// Continue the resolve.
next(context);
// Has an instance been activated?
if (context.NewInstanceActivated)
{
var instanceType = context.Instance.GetType();
// Get all the injectable properties to set.
// If you wanted to ensure the properties were only UNSET properties,
// here's where you'd do it.
var properties = instanceType
.GetProperties(BindingFlags.Public | BindingFlags.Instance)
.Where(p => p.PropertyType == typeof(ILogger) && p.CanWrite && p.GetIndexParameters().Length == 0);
// Set the properties located.
foreach (var propToSet in properties)
{
propToSet.SetValue(context.Instance, GetLogger(context, instanceType), null);
}
}
}
/// <summary>
/// The log cache.
/// </summary>
private static readonly ConcurrentDictionary<Type, object> _logCache = new ConcurrentDictionary<Type, object>();
/// <summary>
/// Gets the logger.
/// </summary>
/// <param name="context">The component context.</param>
/// <param name="declaringType">The type of logger.</param>
/// <returns>The logger instance.</returns>
private static object GetGenericTypeLogger(IComponentContext context, Type declaringType)
{
return _logCache.GetOrAdd(
declaringType,
x =>
{
var wrapper = typeof(LoggerWrapper<>);
var specificWrapper = wrapper.MakeGenericType(declaringType);
var instance = (ILoggerWrapper)Activator.CreateInstance(specificWrapper);
var factory = context.Resolve<ILoggerFactory>();
return instance!.Create(factory);
});
}
/// <summary>
/// Gets the logger.
/// </summary>
/// <param name="context">The component context.</param>
/// <param name="declaringType">The type of logger.</param>
/// <returns>The logger instance.</returns>
private static object GetLogger(IComponentContext context, Type declaringType)
{
return _logCache.GetOrAdd(
declaringType,
x =>
{
var factory = context.Resolve<ILoggerFactory>();
return factory.CreateLogger(declaringType);
});
}
/// <summary>
/// The <see cref="ILoggerWrapper" />
/// interface defines the method for creating a generic type logger.
/// </summary>
private interface ILoggerWrapper
{
/// <summary>
/// Creates a generic type logger using the specified factory.
/// </summary>
/// <param name="factory">The factory.</param>
/// <returns>The logger.</returns>
object Create(ILoggerFactory factory);
}
private class LoggerWrapper<T> : ILoggerWrapper
{
public object Create(ILoggerFactory factory)
{
return factory.CreateLogger<T>();
}
}
}
public class MiddlewareModule : Module
{
private readonly IResolveMiddleware _middleware;
public MiddlewareModule(IResolveMiddleware middleware)
{
_middleware = middleware;
}
protected override void AttachToComponentRegistration(IComponentRegistryBuilder componentRegistry, IComponentRegistration registration)
{
// Attach to the registrations pipeline build
registration.PipelineBuilding += (sender, pipeline) =>
{
// Add our middleware to the pipeline
pipeline.Use(_middleware);
};
}
===============
Further question:
So when I run this I'm getting DependencyResolutionException and not quite sure where to look.
This is what the middleware module registration looks like:
There are other standard modules doing RegisterType and Register calls.
You've got it right; that example MiddlewareModule registers a single instance of an IResolveMiddleware in the pipeline of every registration, so you need one instance of the module for each different middleware you want to add.
You could equally have a custom Module that registers multiple middleware, or construct a different middleware instance per-registration; this was just an example.
So, I'm trying to wrap my head around Microsoft's Dataflow library. I've built a very simple pipeline consisting of just two blocks:
var start = new TransformBlock<Foo, Bar>();
var end = new ActionBlock<Bar>();
start.LinkTo(end);
Now I can asynchronously process Foo instances by calling:
start.SendAsync(new Foo());
What I do not understand is how to do the processing synchronously, when needed. I thought that waiting on SendAsync would be enough:
start.SendAsync(new Foo()).Wait();
But apparently it returns as soon as item is accepted by first processor in pipeline, and not when item is fully processed. So is there a way to wait until given item was processed by last (end) block? Apart from passing a WaitHandle through entire pipeline.
In short that's not supported out of the box in data flow. Essentially what you need to do is to tag the data so you can retrieve it when processing is done. I've written up a way to do this that let's the consumer await a Job as it gets processed by the pipeline. The only concession to pipeline design is that each block take a KeyValuePair<Guid, T>. This is the basic JobManager and the post I wrote about it. Note the code in the post is a bit dated and needs some updates but it should get you in the right direction.
namespace ConcurrentFlows.DataflowJobs {
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Threading.Tasks;
using System.Threading.Tasks.Dataflow;
/// <summary>
/// A generic interface defining that:
/// for a specified input type => an awaitable result is produced.
/// </summary>
/// <typeparam name="TInput">The type of data to process.</typeparam>
/// <typeparam name="TOutput">The type of data the consumer expects back.</typeparam>
public interface IJobManager<TInput, TOutput> {
Task<TOutput> SubmitRequest(TInput data);
}
/// <summary>
/// A TPL-Dataflow based job manager.
/// </summary>
/// <typeparam name="TInput">The type of data to process.</typeparam>
/// <typeparam name="TOutput">The type of data the consumer expects back.</typeparam>
public class DataflowJobManager<TInput, TOutput> : IJobManager<TInput, TOutput> {
/// <summary>
/// It is anticipated that jobHandler is an injected
/// singleton instance of a Dataflow based 'calculator', though this implementation
/// does not depend on it being a singleton.
/// </summary>
/// <param name="jobHandler">A singleton Dataflow block through which all jobs are processed.</param>
public DataflowJobManager(IPropagatorBlock<KeyValuePair<Guid, TInput>, KeyValuePair<Guid, TOutput>> jobHandler) {
if (jobHandler == null) { throw new ArgumentException("Argument cannot be null.", "jobHandler"); }
this.JobHandler = JobHandler;
if (!alreadyLinked) {
JobHandler.LinkTo(ResultHandler, new DataflowLinkOptions() { PropagateCompletion = true });
alreadyLinked = true;
}
}
private static bool alreadyLinked = false;
/// <summary>
/// Submits the request to the JobHandler and asynchronously awaits the result.
/// </summary>
/// <param name="data">The input data to be processd.</param>
/// <returns></returns>
public async Task<TOutput> SubmitRequest(TInput data) {
var taggedData = TagInputData(data);
var job = CreateJob(taggedData);
Jobs.TryAdd(job.Key, job.Value);
await JobHandler.SendAsync(taggedData);
return await job.Value.Task;
}
private static ConcurrentDictionary<Guid, TaskCompletionSource<TOutput>> Jobs {
get;
} = new ConcurrentDictionary<Guid, TaskCompletionSource<TOutput>>();
private static ExecutionDataflowBlockOptions Options {
get;
} = GetResultHandlerOptions();
private static ITargetBlock<KeyValuePair<Guid, TOutput>> ResultHandler {
get;
} = CreateReplyHandler(Options);
private IPropagatorBlock<KeyValuePair<Guid, TInput>, KeyValuePair<Guid, TOutput>> JobHandler {
get;
}
private KeyValuePair<Guid, TInput> TagInputData(TInput data) {
var id = Guid.NewGuid();
return new KeyValuePair<Guid, TInput>(id, data);
}
private KeyValuePair<Guid, TaskCompletionSource<TOutput>> CreateJob(KeyValuePair<Guid, TInput> taggedData) {
var id = taggedData.Key;
var jobCompletionSource = new TaskCompletionSource<TOutput>();
return new KeyValuePair<Guid, TaskCompletionSource<TOutput>>(id, jobCompletionSource);
}
private static ExecutionDataflowBlockOptions GetResultHandlerOptions() {
return new ExecutionDataflowBlockOptions() {
MaxDegreeOfParallelism = Environment.ProcessorCount,
BoundedCapacity = 1000
};
}
private static ITargetBlock<KeyValuePair<Guid, TOutput>> CreateReplyHandler(ExecutionDataflowBlockOptions options) {
return new ActionBlock<KeyValuePair<Guid, TOutput>>((result) => {
RecieveOutput(result);
}, options);
}
private static void RecieveOutput(KeyValuePair<Guid, TOutput> result) {
var jobId = result.Key;
TaskCompletionSource<TOutput> jobCompletionSource;
if (!Jobs.TryRemove(jobId, out jobCompletionSource)) {
throw new InvalidOperationException($"The jobId: {jobId} was not found.");
}
var resultValue = result.Value;
jobCompletionSource.SetResult(resultValue);
}
}
}
I ended up using the following pipeline:
var start = new TransformBlock<FooBar, FooBar>(...);
var end = new ActionBlock<FooBar>(item => item.Complete());
start.LinkTo(end);
var input = new FooBar {Input = new Foo()};
start.SendAsync(input);
input.Task.Wait();
Where
class FooBar
{
public Foo Input { get; set; }
public Bar Result { get; set; }
public Task<Bar> Task { get { return _taskSource.Task; } }
public void Complete()
{
_taskSource.SetResult(Result);
}
private TaskCompletionSource<Bar> _taskSource = new TaskCompletionSource<Bar>();
}
Less than ideal, but it works.
I'm trying to apply some behavior using a home grown type of "aspect", really a .net Attribute. I have a base class (BankingServiceBase) that reflects on itself at startup to see what "aspects" are applied to it. It then can execute custom behavior before or after operations. I'm using Autofac as my IOC container. I'm trying to apply the PropertiesAutowired method to the aspect's registration. In the below sample code I want Autofac to inject an ILog instance to my aspect/attribute. It isn't doing that however. My guess is that when I call GetCustomAttributes, it's creating a new instance instead of getting the registered instance from Autofac. Thoughts? Here is some usable sample code to display the problem:
internal class Program
{
private static void Main()
{
var builder = new ContainerBuilder();
builder
.RegisterType<ConsoleLog>()
.As<ILog>();
builder
.RegisterType<BankingService>()
.As<IBankingService>();
builder
.RegisterType<LogTransfer>()
.As<LogTransfer>()
.PropertiesAutowired();
var container = builder.Build();
var bankingService = container.Resolve<IBankingService>();
bankingService.Transfer("ACT 1", "ACT 2", 180);
System.Console.ReadKey();
}
public interface IBankingService
{
void Transfer(string from, string to, decimal amount);
}
public interface ILog
{
void LogMessage(string message);
}
public class ConsoleLog : ILog
{
public void LogMessage(string message)
{
System.Console.WriteLine(message);
}
}
[AttributeUsage(AttributeTargets.Class)]
public abstract class BankingServiceAspect : Attribute
{
public virtual void PreTransfer(string from, string to, decimal amount)
{
}
public virtual void PostTransfer(bool success)
{
}
}
public class LogTransfer : BankingServiceAspect
{
// Note: this is never getting set from Autofac!
public ILog Log { get; set; }
public override void PreTransfer(string from, string to, decimal amount)
{
Log.LogMessage(string.Format("About to transfer from {0}, to {1}, for amount {2}", from, to, amount));
}
public override void PostTransfer(bool success)
{
Log.LogMessage(success ? "Transfer completed!" : "Transfer failed!");
}
}
public abstract class BankingServiceBase : IBankingService
{
private readonly List<BankingServiceAspect> aspects;
protected BankingServiceBase()
{
// Note: My guess is that this "GetCustomAttributes" is happening before the IOC dependency map is built.
aspects =
GetType().GetCustomAttributes(typeof (BankingServiceAspect), true).Cast<BankingServiceAspect>().
ToList();
}
void IBankingService.Transfer(string from, string to, decimal amount)
{
aspects.ForEach(a => a.PreTransfer(from, to, amount));
try
{
Transfer(from, to, amount);
aspects.ForEach(a => a.PostTransfer(true));
}
catch (Exception)
{
aspects.ForEach(a => a.PostTransfer(false));
}
}
public abstract void Transfer(string from, string to, decimal amount);
}
[LogTransfer]
public class BankingService : BankingServiceBase
{
public override void Transfer(string from, string to, decimal amount)
{
// Simulate some latency..
Thread.Sleep(1000);
}
}
}
You're correct that GetCustomAttributes doesn't resolve the custom attributes via Autofac - if you think about it, how could FCL code such as GetCustomAttributes know about Autofac? The custom attributes are actually retrieved from assembly metadata, so they never go through Autofac's resolution process and therefore your registration code is never used.
What you can do is to inject the services into the attribute instance yourself. Begin with the code in Oliver's answer to generate the list of aspect attributes. However, before returning the list, you can process each attribute and inject services into any dependent fields and properties. I have a class called AttributedDependencyInjector, which I use via an extension method. It uses reflection to scan for fields and properties that are decorated with the InjectDependencyAttribute and then set the value of those properties. There's rather a lot of code to cope with various scenarios, but here it is.
The attribute class:
/// <summary>
/// Attribute that signals that a dependency should be injected.
/// </summary>
[AttributeUsage(AttributeTargets.Property | AttributeTargets.Field, AllowMultiple = false, Inherited = true)]
public sealed class InjectDependencyAttribute : Attribute
{
/// <summary>
/// Initializes a new instance of the <see cref = "InjectDependencyAttribute" /> class.
/// </summary>
public InjectDependencyAttribute()
{
this.PreserveExistingValue = false;
}
/// <summary>
/// Gets or sets a value indicating whether to preserve an existing non-null value.
/// </summary>
/// <value>
/// <c>true</c> if the injector should preserve an existing value; otherwise, <c>false</c>.
/// </value>
public bool PreserveExistingValue { get; set; }
}
The injector class:
public class AttributedDependencyInjector
{
/// <summary>
/// The component context.
/// </summary>
private readonly IComponentContext context;
/// <summary>
/// Initializes a new instance of the <see cref="AttributedDependencyInjector"/> class.
/// </summary>
/// <param name="context">The context.</param>
public AttributedDependencyInjector(IComponentContext context)
{
this.context = context;
}
/// <summary>
/// Injects dependencies into an instance.
/// </summary>
/// <param name="instance">The instance.</param>
public void InjectDependencies(object instance)
{
this.InjectAttributedFields(instance);
this.InjectAttributedProperties(instance);
}
/// <summary>
/// Gets the injectable fields.
/// </summary>
/// <param name="instanceType">
/// Type of the instance.
/// </param>
/// <param name="injectableFields">
/// The injectable fields.
/// </param>
private static void GetInjectableFields(
Type instanceType, ICollection<Tuple<FieldInfo, InjectDependencyAttribute>> injectableFields)
{
const BindingFlags BindingsFlag =
BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.DeclaredOnly;
IEnumerable<FieldInfo> fields = instanceType.GetFields(BindingsFlag);
// fields
foreach (FieldInfo field in fields)
{
Type fieldType = field.FieldType;
if (fieldType.IsValueType)
{
continue;
}
// Check if it has an InjectDependencyAttribute
var attribute = field.GetAttribute<InjectDependencyAttribute>(false);
if (attribute == null)
{
continue;
}
var info = new Tuple<FieldInfo, InjectDependencyAttribute>(field, attribute);
injectableFields.Add(info);
}
}
/// <summary>
/// Gets the injectable properties.
/// </summary>
/// <param name="instanceType">
/// Type of the instance.
/// </param>
/// <param name="injectableProperties">
/// A list into which are appended any injectable properties.
/// </param>
private static void GetInjectableProperties(
Type instanceType, ICollection<Tuple<PropertyInfo, InjectDependencyAttribute>> injectableProperties)
{
// properties
foreach (var property in instanceType.GetProperties(
BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.DeclaredOnly))
{
Type propertyType = property.PropertyType;
// Can't inject value types
if (propertyType.IsValueType)
{
continue;
}
// Can't inject non-writeable properties
if (!property.CanWrite)
{
continue;
}
// Check if it has an InjectDependencyAttribute
var attribute = property.GetAttribute<InjectDependencyAttribute>(false);
if (attribute == null)
{
continue;
}
// If set to preserve existing value, we must be able to read it!
if (attribute.PreserveExistingValue && !property.CanRead)
{
throw new BoneheadedException("Can't preserve an existing value if it is unreadable");
}
var info = new Tuple<PropertyInfo, InjectDependencyAttribute>(property, attribute);
injectableProperties.Add(info);
}
}
/// <summary>
/// Determines whether the <paramref name="propertyType"/> can be resolved in the specified context.
/// </summary>
/// <param name="propertyType">
/// Type of the property.
/// </param>
/// <returns>
/// <c>true</c> if <see cref="context"/> can resolve the specified property type; otherwise, <c>false</c>.
/// </returns>
private bool CanResolve(Type propertyType)
{
return this.context.IsRegistered(propertyType) || propertyType.IsAssignableFrom(typeof(ILog));
}
/// <summary>
/// Injects dependencies into the instance's fields.
/// </summary>
/// <param name="instance">
/// The instance.
/// </param>
private void InjectAttributedFields(object instance)
{
Type instanceType = instance.GetType();
// We can't get information about the private members of base classes through reflecting a subclass,
// so we must walk up the inheritance hierarchy and reflect at each level
var injectableFields = new List<Tuple<FieldInfo, InjectDependencyAttribute>>();
var type = instanceType;
while (type != null)
{
GetInjectableFields(type, injectableFields);
type = type.BaseType;
}
// fields
foreach (var fieldDetails in injectableFields)
{
var field = fieldDetails.Item1;
var attribute = fieldDetails.Item2;
if (!this.CanResolve(field.FieldType))
{
continue;
}
// Check to preserve existing value
if (attribute.PreserveExistingValue && (field.GetValue(instance) != null))
{
continue;
}
object fieldValue = this.Resolve(field.FieldType, instanceType);
field.SetValue(instance, fieldValue);
}
}
/// <summary>
/// Injects dependencies into the instance's properties.
/// </summary>
/// <param name="instance">
/// The instance.
/// </param>
private void InjectAttributedProperties(object instance)
{
Type instanceType = instance.GetType();
// We can't get information about the private members of base classes through reflecting a subclass,
// so we must walk up the inheritance bierarchy and reflect at each level
var injectableProperties = new List<Tuple<PropertyInfo, InjectDependencyAttribute>>();
var type = instanceType;
while (type != typeof(object))
{
Debug.Assert(type != null, "type != null");
GetInjectableProperties(type, injectableProperties);
type = type.BaseType;
}
// Process the list and inject properties as appropriate
foreach (var details in injectableProperties)
{
var property = details.Item1;
var attribute = details.Item2;
// Check to preserve existing value
if (attribute.PreserveExistingValue && (property.GetValue(instance, null) != null))
{
continue;
}
var propertyValue = this.Resolve(property.PropertyType, instanceType);
property.SetValue(instance, propertyValue, null);
}
}
/// <summary>
/// Resolves the specified <paramref name="propertyType"/> within the context.
/// </summary>
/// <param name="propertyType">
/// Type of the property that is being injected.
/// </param>
/// <param name="instanceType">
/// Type of the object that is being injected.
/// </param>
/// <returns>
/// The object instance to inject into the property value.
/// </returns>
private object Resolve(Type propertyType, Type instanceType)
{
if (propertyType.IsAssignableFrom(typeof(ILog)))
{
return LogManager.GetLogger(instanceType);
}
return this.context.Resolve(propertyType);
}
}
The extension method:
public static class RegistrationExtensions
{
/// <summary>
/// Injects dependencies into the instance's properties and fields.
/// </summary>
/// <param name="context">
/// The component context.
/// </param>
/// <param name="instance">
/// The instance into which to inject dependencies.
/// </param>
public static void InjectDependencies(this IComponentContext context, object instance)
{
Enforce.ArgumentNotNull(context, "context");
Enforce.ArgumentNotNull(instance, "instance");
var injector = new AttributedDependencyInjector(context);
injector.InjectDependencies(instance);
}
}
Try to implement a lazy loading of the aspects
private readonly List<BankingServiceAspect> _aspects;
private List<BankingServiceAspect> Aspects
{
get
{
if (_aspects == null) {
_aspects = GetType()
.GetCustomAttributes(typeof(BankingServiceAspect), true)
.Cast<BankingServiceAspect>()
.ToList();
}
return _aspects;
}
}
Then use it like this
Aspects.ForEach(a => a.PreTransfer(from, to, amount));
...
I have an ASP.NET MVC project in which the model is managed through .NET entities and it seems that some times it loses the connection, but this happens only on stored procedures.
I get the following error:
Execution of the command requires an open and available connection. The connection's current state is broken.
Why is this happening?
Code
public ObjectResult<Categories> GetCategoriesStructure() {
return ObjectContext.getCategoriesStructure();
}
var catss = GetCategoriesStructure().ToList();
this exception occurs when I am trying to assign the List to catss variable
Object Context Instantiation
public abstract class ObjectContextManager {
/// <summary>
/// Returns a reference to an ObjectContext instance.
/// </summary>
public abstract TObjectContext GetObjectContext<TObjectContext>()
where TObjectContext : ObjectContext, new();
}
public abstract class BaseDAO<TObjectContext, TEntity> : IBaseDAO<TObjectContext, TEntity>
where TObjectContext : System.Data.Objects.ObjectContext, new()
where TEntity : System.Data.Objects.DataClasses.EntityObject {
private ObjectContextManager _objectContextManager;
/// <summary>
/// Returns the current ObjectContextManager instance. Encapsulated the
/// _objectContextManager field to show it as an association on the class diagram.
/// </summary>
private ObjectContextManager ObjectContextManager {
get { return _objectContextManager; }
set { _objectContextManager = value; }
}
/// <summary>
/// Returns an ObjectContext object.
/// </summary>
protected internal TObjectContext ObjectContext {
get {
if (ObjectContextManager == null)
this.InstantiateObjectContextManager();
return ObjectContextManager.GetObjectContext<TObjectContext>();
}
}
/// <summary>
/// Default constructor.
/// </summary>
public BaseDAO() { }
/// <summary>
/// Instantiates a new ObjectContextManager based on application configuration settings.
/// </summary>
private void InstantiateObjectContextManager() {
/* Retrieve ObjectContextManager configuration settings: */
Hashtable ocManagerConfiguration = ConfigurationManager.GetSection("ObjectContextManagement.ObjectContext") as Hashtable;
if (ocManagerConfiguration != null && ocManagerConfiguration.ContainsKey("managerType")) {
string managerTypeName = ocManagerConfiguration["managerType"] as string;
if (string.IsNullOrEmpty(managerTypeName))
throw new ConfigurationErrorsException("The managerType attribute is empty.");
else
managerTypeName = managerTypeName.Trim().ToLower();
try {
/* Try to create a type based on it's name: */
Assembly frameworkAssembly = Assembly.GetAssembly(typeof(ObjectContextManager));
Type managerType = frameworkAssembly.GetType(managerTypeName, true, true);
/* Try to create a new instance of the specified ObjectContextManager type: */
this.ObjectContextManager = Activator.CreateInstance(managerType) as ObjectContextManager;
} catch (Exception e) {
throw new ConfigurationErrorsException("The managerType specified in the configuration is not valid.", e);
}
} else
throw new ConfigurationErrorsException("ObjectContext tag or its managerType attribute is missing in the configuration.");
}
/// <summary>
/// Persists all changes to the underlying datastore.
/// </summary>
public void SaveAllObjectChanges() {
this.ObjectContext.SaveChanges();
}
/// <summary>
/// Adds a new entity object to the context.
/// </summary>
/// <param name="newObject">A new object.</param>
public virtual void Add(TEntity newObject) {
this.ObjectContext.AddObject(newObject.GetType().Name, newObject);
}
/// <summary>
/// Deletes an entity object.
/// </summary>
/// <param name="obsoleteObject">An obsolete object.</param>
public virtual void Delete(TEntity obsoleteObject) {
this.ObjectContext.DeleteObject(obsoleteObject);
}
public void Detach(TEntity obsoleteObject) {
this.ObjectContext.Detach(obsoleteObject);
}
/// <summary>
/// Updates the changed entity object to the context.
/// </summary>
/// <param name="newObject">A new object.</param>
public virtual void Update(TEntity newObject) {
ObjectContext.ApplyPropertyChanges(newObject.GetType().Name, newObject);
ObjectContext.Refresh(RefreshMode.ClientWins, newObject);
}
public virtual TEntity LoadByKey(String propertyName, Object keyValue) {
IEnumerable<KeyValuePair<string, object>> entityKeyValues =
new KeyValuePair<string, object>[] {
new KeyValuePair<string, object>(propertyName, keyValue) };
// Create the key for a specific SalesOrderHeader object.
EntityKey key = new EntityKey(this.ObjectContext.GetType().Name + "." + typeof(TEntity).Name, entityKeyValues);
return (TEntity)this.ObjectContext.GetObjectByKey(key);
}
#region IBaseDAO<TObjectContext,TEntity> Members
public bool validation(TEntity newObject) {
return newObject.GetType().Name.ToString() == "Int32";
}
#endregion
}
Without knowing how you are instantiating your ObjectContext, I'll throw something in the answer bucket here.
This is how I do my Entity Framework commands and connections (for small simple projects at least):
using (MyEntities context = new MyEntities())
{
return context.getCategoriesStructure();
}
You can also optionally pass in a connection string when instantiating your context (if not, it will use the one in your app.config):
new MyEntities("...connection string...")
If this does not help your issue, please help us understand your code a little better by posting how you are creating your ObjectContext. You could at least attempt to do it this way to see if it works; that will tell you whether it is an issue with your connection string or not.
I have a constructor which has a non-interface dependency:
public MainWindowViewModel(IWorkItemProvider workItemProvider, WeekNavigatorViewModel weekNavigator)
I am using the Moq.Contrib automockcontainer. If I try to automock the MainWindowViewModel class, I get an error due to the WeekNavigatorViewModel dependency.
Are there any automocking containers which supports mocking of non-interface types?
As Mark has shown below; yes you can! :-) I replaced the Moq.Contrib AutoMockContainer with the stuff Mark presents in his answer, the only difference is that the auto-generated mocks are registered as singletons, but you can make this configurable. Here is the final solution:
/// <summary>
/// Auto-mocking factory that can create an instance of the
/// class under test and automatically inject mocks for all its dependencies.
/// </summary>
/// <remarks>
/// Mocks interface and class dependencies
/// </remarks>
public class AutoMockContainer
{
readonly IContainer _container;
public AutoMockContainer(MockFactory factory)
{
var builder = new ContainerBuilder();
builder.RegisterSource(new AnyConcreteTypeNotAlreadyRegisteredSource());
builder.RegisterSource(new MoqRegistrationSource(factory));
_container = builder.Build();
}
/// <summary>
/// Gets or creates a mock for the given type, with
/// the default behavior specified by the factory.
/// </summary>
public Mock<T> GetMock<T>() where T : class
{
return (_container.Resolve<T>() as IMocked<T>).Mock;
}
/// <summary>
/// Creates an instance of a class under test,
/// injecting all necessary dependencies as mocks.
/// </summary>
/// <typeparam name="T">Requested object type.</typeparam>
public T Create<T>() where T : class
{
return _container.Resolve<T>();
}
public T Resolve<T>()
{
return _container.Resolve<T>();
}
/// <summary>
/// Registers and resolves the given service on the container.
/// </summary>
/// <typeparam name="TService">Service</typeparam>
/// <typeparam name="TImplementation">The implementation of the service.</typeparam>
public void Register<TService, TImplementation>()
{
var builder = new ContainerBuilder();
builder.RegisterType<TImplementation>().As<TService>().SingleInstance();
builder.Update(_container);
}
/// <summary>
/// Registers the given service instance on the container.
/// </summary>
/// <typeparam name="TService">Service type.</typeparam>
/// <param name="instance">Service instance.</param>
public void Register<TService>(TService instance)
{
var builder = new ContainerBuilder();
if (instance.GetType().IsClass)
builder.RegisterInstance(instance as object).As<TService>();
else
builder.Register(c => instance).As<TService>();
builder.Update(_container);
}
class MoqRegistrationSource : IRegistrationSource
{
private readonly MockFactory _factory;
private readonly MethodInfo _createMethod;
public MoqRegistrationSource(MockFactory factory)
{
_factory = factory;
_createMethod = factory.GetType().GetMethod("Create", new Type[] { });
}
public IEnumerable<IComponentRegistration> RegistrationsFor(Service service, Func<Service, IEnumerable<IComponentRegistration>> registrationAccessor)
{
var swt = service as IServiceWithType;
if (swt == null)
{
yield break;
}
if (!swt.ServiceType.IsInterface)
yield break;
var existingReg = registrationAccessor(service);
if (existingReg.Any())
{
yield break;
}
var reg = RegistrationBuilder.ForDelegate((c, p) =>
{
var createMethod = _createMethod.MakeGenericMethod(swt.ServiceType);
return ((Mock)createMethod.Invoke(_factory, null)).Object;
}).As(swt.ServiceType).SingleInstance().CreateRegistration();
yield return reg;
}
public bool IsAdapterForIndividualComponents
{
get { return false; }
}
}
}
You can pretty easily write one yourself if you leverage a DI Container that supports just-in-time resolution of requested types.
I recently wrote a prototype for exactly that purpose using Autofac and Moq, but other containers could be used instead.
Here's the appropriate IRegistrationSource:
public class AutoMockingRegistrationSource : IRegistrationSource
{
private readonly MockFactory mockFactory;
public AutoMockingRegistrationSource()
{
this.mockFactory = new MockFactory(MockBehavior.Default);
this.mockFactory.CallBase = true;
this.mockFactory.DefaultValue = DefaultValue.Mock;
}
public MockFactory MockFactory
{
get { return this.mockFactory; }
}
#region IRegistrationSource Members
public IEnumerable<IComponentRegistration> RegistrationsFor(
Service service,
Func<Service, IEnumerable<IComponentRegistration>> registrationAccessor)
{
var swt = service as IServiceWithType;
if (swt == null)
{
yield break;
}
var existingReg = registrationAccessor(service);
if (existingReg.Any())
{
yield break;
}
var reg = RegistrationBuilder.ForDelegate((c, p) =>
{
var createMethod =
typeof(MockFactory).GetMethod("Create", Type.EmptyTypes).MakeGenericMethod(swt.ServiceType);
return ((Mock)createMethod.Invoke(this.MockFactory, null)).Object;
}).As(swt.ServiceType).CreateRegistration();
yield return reg;
}
#endregion
}
You can now set up the container in a unit test like this:
[TestMethod]
public void ContainerCanCreate()
{
// Fixture setup
var builder = new ContainerBuilder();
builder.RegisterSource(new AnyConcreteTypeNotAlreadyRegisteredSource());
builder.RegisterSource(new AutoMockingRegistrationSource());
var container = builder.Build();
// Exercise system
var result = container.Resolve<MyClass>();
// Verify outcome
Assert.IsNotNull(result);
// Teardown
}
That's all you need to get started.
MyClass is a concrete class with an abstract dependency. Here is the constructor signature:
public MyClass(ISomeInterface some)
Notice that you don't have to use Autofac (or any other DI Container) in your production code.