I have the following in QUERY side of Project1 which primarily contains interfaces
public interface IQueryResult {}
public interface IQuery<TResult> where TResult : IQueryResult {}
public interface IQueryHandler<TQuery, TResult>
where TQuery : IQuery<TResult>
where TResult : IQueryResult
{
Task<TResult> HandleAsync(TQuery query);
}
public class PersonQueryResult : IQueryResult
{
public string Name { get; set; }
}
public class GetPersonDetailsQuery : IQuery<PersonQueryResult>
{
public int Id { get; set; }
}
public interface IQueryDispatcher
{
Task<TResult> DispatchAsync<TQuery, TResult>(TQuery query)
where TQuery : IQuery<TResult>
where TResult : IQueryResult;
}
In the Second Project2 which references Project 1, I have
public class GetPersonDetailsQueryHandler :
IQueryHandler<GetPersonDetailsQuery, PersonQueryResult>
{
public Task<PersonQueryResult> HandleAsync(GetPersonDetailsQuery query)
{
return Task.FromResult( new PersonQueryResult {Name = "Bamboo"});
}
}
The last Project 3 is a Web API project which only references Project 1 but NOT project 2. So it knows the interfaces and the commands and queries only. I need to configure autofac in a way that i can easily do something like this
var query = new GetPersonDetailsQuery { Id = 1 };
var magicHappensHere = new QueryDispatcher(); //any better way?
PersonQueryResult result = magicHappensHere.Dispatch(query);
Also the IQueryDispatcher I have in Project 1 does not seem fit for the job above.
A sample implementation for that interface which is open for suggestions is
public class QueryDispatcher : IQueryDispatcher
{
private readonly IComponentContext _context;
public QueryDispatcher(IComponentContext context)
{
this._context = context;
}
public Task<TResult> DispatchAsync<TQuery, TResult>(TQuery query) where TQuery : IQuery<TResult> where TResult : IQueryResult
{
var handler = _context.Resolve<IQueryHandler<TQuery, TResult>>();
return handler.HandleAsync(query);
}
}
Possible solutions that I dont know how to implement (A) Define an Autofac module in Project 2 and then Scan the Project 2 assembly in Web API?.. (B) http://docs.autofac.org/en/latest/register/registration.html#open-generic-components (C) Scanning assembly and trying to map automatically. Need help with code to be inserted here
private static void ConfigureAutofac(HttpConfiguration config)
{
var builder = new ContainerBuilder();
//*************//
//what to do here?
//**************//
builder.RegisterApiControllers(Assembly.GetExecutingAssembly());
builder.RegisterWebApiFilterProvider(config);
var container = builder.Build();
config.DependencyResolver = new AutofacWebApiDependencyResolver(container);
}
Finally got proposed solution (B) http://docs.autofac.org/en/latest/register/registration.html#open-generic-components to work. Also I can confirm that no references to the dll's have been added to the solution
However I still need a nice way to dispatch queries from the API project. Something more intuitive
private static void ConfigureAutofac(HttpConfiguration config)
{
var builder = new ContainerBuilder();
//*************//
//heres what i did
//*************//
var assemblies = BuildManager.GetReferencedAssemblies().Cast<Assembly>();
foreach (var assembly in assemblies)
{
builder.RegisterAssemblyTypes(assembly).AssignableTo<IQueryResult>().AsImplementedInterfaces();
builder.RegisterAssemblyTypes(assembly).AsClosedTypesOf(typeof(IQuery<>)).AsImplementedInterfaces();
builder.RegisterAssemblyTypes(assembly).AsClosedTypesOf(typeof(IQueryHandler<,>)).AsImplementedInterfaces();
}
//rest of the code
}
Your Web API project contains the application's Composition Root and the Composition Root by definition references all other assemblies in your application. Not referencing it makes no sense and just complicates things.
Please read this q/a for a more detailed discussion:
Many developers don’t want their [Web API] assembly to depend on the DAL assembly, but that's not really a problem. Don't forget that assemblies are a deployment artifact; you split code into multiple assemblies to allow code to be deployed separately. An architectural layer on the other hand is a logical artifact. It's very well possible (and common) to have multiple layers in the same assembly. In this case we'll end up having the Composition Root (layer) and the Presentation Layer in the same web application project (thus in the same assembly). And even though that assembly references the assembly containing the DAL, the Presentation Layer still does not reference the Data Access Layer. This is a big distinction. Of course, when we do this, we lose the ability for the compiler to check this architectural rule at compile time, but this shouldn't be a problem. Most architectural rules actually can't be checked by the compiler and there's always something like common sense. And if there's no common sense in your team, you can always use code reviews (which every team should IMO always do btw). You can also use a tool such as NDepend (which is commercial), which helps you verifying your architectural rules.
In general, hard references from your Composition Root to other assemblies only have to be prevented in case you have a plug-in model, where assemblies aren't known at compile-time, and can be added during or after deployment. These types of scenarios however are not very common for LOB applications.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With