Using IoC container as a service locator for HttpHandler - nunit

This question relates to my other post.
Ok so after a bit more messing around I decided to do it this way. Which seems to work fine when I run it, although I'm getting the following error in NUnit: Could not load file or assembly 'Castle.Core, Version=1.0.3.0, Culture=neutral, PublicKeyToken=407dd0808d44fbdc' or one of its dependencies. The located assembly's manifest definition does not match the assembly reference. (Exception from HRESULT: 0x80131040) So not sure what is happening there???
Just wanted to know what others thought about the design and if there are any obvious 'no no's' or improvements. I.e. Is the constructor of the base handler a good place to instantiate the windsor component or is there a better place to do this? As I said in the original post the idea behind doing things this way was to keep the components nicely decoupled and to make unit testing easy. I should also add I'm new to unit testing, mocking. Thanks!
public abstract class BaseHttpHandler : IHttpHandler
{
private HttpContext _httpContext;
private ILogger _logger;
private IDataRepository _dataRepository;
protected HttpRequest Request { get { return _httpContext.Request; } }
protected HttpResponse Response { get { return _httpContext.Response; } }
protected bool IsRequestFromUAD { get { return Request.UserAgent == null ? false : Request.UserAgent.Equals("UAD"); } }
protected ILogger Logger { get { return _logger; } }
protected IDataRepository DataRepository { get { return _dataRepository; } }
public virtual bool IsReusable { get { return false; } }
public BaseHttpHandler()
{
var container = new WindsorContainer(new XmlInterpreter(new ConfigResource("castle")));
_logger = container.Resolve<ILogger>();
_dataRepository = container.Resolve<IDataRepository>();
}
public void ProcessRequest(HttpContext context)
{
_httpContext = context;
ProcessRequest(new HttpContextWrapper(context));
}
public abstract void ProcessRequest(HttpContextBase context);
}
public class UADRecordHttpHandler : BaseHttpHandler
{
public override void ProcessRequest(HttpContextBase context)
{
if (IsRequestFromUAD)
{
using (var reader = new StreamReader(context.Request.InputStream))
{
string data = reader.ReadToEnd();
if (Logger != null)
Logger.Log(data);
if(DataRepository != null)
DataRepository.Write(data);
context.Response.Write(data);
}
}
else
ReturnResponse(HttpStatusCode.BadRequest);
}
}

That's a very bad thing to do, what you're doing here. You should have one instance of the container per application, while with this code you will have one per each request.

About the error in NUnit: make sure you don't have other versions of Castle assemblies in the GAC. If so, uninstall them.
About your BaseHttpHandler: the problem with this implementation is that you're creating a new container. Instead, use a single container per application, like Krzysztof said. Use a static service locator, e.g. CommonServiceLocator. (I never recommend this but it's one of the few places where it does make sense).

Related

Why am I getting error: "Cannot access disposed object" in .net core 2 with EF and AutoFac?

First the error:
Cannot access a disposed object. A common cause of this error is disposing a context that was resolved from dependency injection and
then later trying to use the same context instance elsewhere in your
application. This may occur if you are calling Dispose() on the
context, or wrapping the context in a using statement. If you are
using dependency injection, you should let the dependency injection
container take care of disposing context instances.
Object name: 'MemberContext'.
I have 3 projects, Domain, API and WebSPA app.
Domain has 2 modules, DomainModule and MediatorModule
public class DomainModule : Autofac.Module
{
protected override void Load(ContainerBuilder builder)
{
builder.RegisterAssemblyTypes(typeof(MemberContext).Assembly)
.AsImplementedInterfaces()
.InstancePerLifetimeScope(); // via assembly scan
builder.RegisterType<MemberContext>().AsSelf()
.InstancePerLifetimeScope(); // or individually
}
}
public class MediatorModule : Autofac.Module
{
protected override void Load(ContainerBuilder builder)
{
// enables contravariant Resolve() for interfaces with single contravariant ("in") arg
builder
.RegisterSource(new ContravariantRegistrationSource());
// mediator itself
builder
.RegisterType<Mediator>()
.As<IMediator>()
.InstancePerLifetimeScope();
// request handlers
builder
.Register<SingleInstanceFactory>(ctx =>
{
var c = ctx.Resolve<IComponentContext>();
return t =>
{
object o;
return c.TryResolve(t, out o) ? o : null;
};
})
.InstancePerLifetimeScope();
// notification handlers
builder
.Register<MultiInstanceFactory>(ctx =>
{
var c = ctx.Resolve<IComponentContext>();
return t => (IEnumerable<object>) c.Resolve(typeof(IEnumerable<>).MakeGenericType(t));
})
.InstancePerLifetimeScope();
}
}
In API project I have also 2 modules, ApplicationModule and again MediatorModule same as the one above.
public class ApplicationModule : Module
{
protected override void Load(ContainerBuilder builder)
{
builder.RegisterAssemblyTypes(typeof(Startup).Assembly)
.AsImplementedInterfaces()
.InstancePerLifetimeScope(); // via assembly scan
builder.RegisterType<MemberContext>().AsSelf().InstancePerLifetimeScope(); // or individually
}
}
No, when I debug I can see that member context gets newed up on each request, yet on second request, it throws above error. To make sure I am not going crazy, I modified constructor of dbcontext to create an id for context so i can verify they are different. What am I doing wrong?
public MemberContext(DbContextOptions<MemberContext> options) : base(options)
{
MemberContextId = Guid.NewGuid();
Console.WriteLine("member context created: " + MemberContextId);
}
Here is the startup in API
public class Startup
{
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
public IConfiguration Configuration { get; }
// This method gets called by the runtime. Use this method to add services to the container.
public IServiceProvider ConfigureServices(IServiceCollection services)
{
services.AddCors(options =>
{
options.AddPolicy("CorsPolicy",
builder => builder.AllowAnyOrigin()
.AllowAnyMethod()
.AllowAnyHeader()
// .AllowCredentials()
);
});
services.AddMvc()
.AddControllersAsServices();//Injecting Controllers themselves thru DI
//For further info see: http://docs.autofac.org/en/latest/integration/aspnetcore.html#controllers-as-services
AddSwaggerGen(services);
//var connection = Configuration["ConnectionString"];
//services.AddDbContext<MemberContext>(options => options.UseSqlServer(connection),ServiceLifetime.Scoped);
services.AddEntityFrameworkSqlServer()
.AddDbContext<MemberContext>(options =>
{
options.UseSqlServer(Configuration["ConnectionString"]
//,sqlServerOptionsAction: sqlOptions =>
//{
// sqlOptions.MigrationsAssembly(typeof(Startup).GetTypeInfo().Assembly.GetName().Name);
// sqlOptions.EnableRetryOnFailure(maxRetryCount: 10, maxRetryDelay: TimeSpan.FromSeconds(30), errorNumbersToAdd: null);
//}
);
},
ServiceLifetime.Scoped //Showing explicitly that the DbContext is shared across the HTTP request scope (graph of objects started in the HTTP request)
);
var container = new ContainerBuilder();
container.Populate(services);
container.RegisterAssemblyModules(typeof(VIN.Members.Domain.Entities.Member).Assembly,
typeof(Startup).Assembly);
return new AutofacServiceProvider(container.Build());
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
//NOTE: must be before UseMVC !!!
app.UseCors("CorsPolicy");
app.UseMvc();
app.UseSwagger();
app.UseSwaggerUI(c =>
{
c.SwaggerEndpoint("/swagger/v1/swagger.json", "My API V1");
});
}
private void AddSwaggerGen(IServiceCollection services)
{
services.AddSwaggerGen(options =>
{
options.DescribeAllEnumsAsStrings();
options.SwaggerDoc("v1", new Swashbuckle.AspNetCore.Swagger.Info
{
Title = "VIN Members HTTP API",
Version = "v1",
Description = "Members Service HTTP API",
TermsOfService = "Terms Of Service"
});
});
}
}
UPDATE:
What I am trying to do is delete a record. On client side code looks like this
onDelete(item: IMember) {
//TODO: replace this with dialog service component
if (window.confirm('Are sure you want to delete this member?')) {
//put your delete method logic here
this.service.deleteMember(item).subscribe(x => {
this.getMembers();
});
}
}
this delete request gets mapped to a controller that passes it to mediator
Controller
// DELETE api/members/5
[HttpDelete("{id}")]
public void Delete(Guid id)
{
var command = new DeleteMember.Command(id);
_mediator.Send(command).ConfigureAwait(false);
}
and finally handler
public class DeleteMember
{
public class Command : IRequest
{
public Command(Guid memberId)
{
Guard.NotNull(memberId, nameof(memberId));
MemberId = memberId;
}
public Guid MemberId { get; }
}
public class Handler : AsyncRequestHandler<Command>
{
private MemberContext _context;
public Handler(MemberContext context)
{
_context = context;
Console.WriteLine("Delete member context: " + context.MemberContextId);
}
protected override async Task HandleCore(Command cmd)
{
try
{
var member = await _context.FindAsync<Member>(cmd.MemberId);//.ConfigureAwait(false);
// if (member != null)
//// {
_context.Remove(member);
await _context.SaveChangesAsync().ConfigureAwait(false);
// }
}
catch (Exception e)
{
Console.WriteLine(e);
throw;
}
}
}
}
As you can see there is no code that disposes that context. Scratching my head.
See this commented out check for member if null. That was throwing error as well, I commented it out just to see what will happen, and now it throws as SaveChangesAsync.
As request completes, context gets disposed. Since command handler uses SaveChangesAsync(), context is disposed before save completes. Culprit is controller method :). It should be async as well.
[HttpDelete("{id}")]
public async Task Delete(Guid id)
{
var command = new DeleteMember.Command(id);
await _mediator.Send(command).ConfigureAwait(false);
}
Your DbContext is scoped, meaning that Dependency Injection will return the same DbContext object every time one is asked for within the same HTTP request (in the case of ASP.NET).
That means that you should not be calling Dispose on your DbContext (otherwise that same object can't be used a second time). That seems to be what is happening to you, intentionally or not.
That does mean you should not be using using with it. Are you using using anywhere in your code against your DbContext?
I don't think you showed the line where the Exception is being thrown.
Update:
Try overriding Dispose in your MemberContext class. Something like this:
public override void Dispose() {
base.Dispose();
}
But just set a breakpoint there. When it breaks (if it does) check the stack trace and see what called it.
This can also be caused by having async void instead of async Task within WebAPI in my experience.

Play 2.5 preserve context in async calls

In our controller class we reach out to another service to get some data :
Future<JsonNode> futureSite = someClient.getSite(siteId, queryParams);
return FutureConverters.toJava(futureSite).thenApplyAsync((siteJson) -> {
Site site = Json.fromJson(siteJson, Site.class);
try {
return function.apply(site);
} catch (RequestException e) {
return e.result;
}
}).exceptionally(throwable -> {
if(throwable instanceof OurClientException) {
if(((OurClientException) throwable).httpStatusCode == 404) {
return entityNotFound("Site", siteId);
}
}
return null;
});
What we notice is that context which is set in unit tests (we use scalatest-play) is lost and becomes null after we make the Async call (FutureConverters.toJava(futureSite).thenApplyAsync((siteJson), as t is on a separate thread.
Which causes problem down in the controller code, where we use the above function ... request() would now throw a runtime exception saying there is no context available.
How can we preserve the context ?
You should inject play.libs.concurrent.HttpExecutionContext to your controller and then specify current context as second argument for CompletionStage#thenApplyAsync(..,..).
public class Application extends Controller {
#Inject HttpExecutionContext ec;
public CompletionStage<Result> index() {
someCompletableFuture.supplyAsync(() -> {
// do something with request()
}, ec.current());
}}
P.S. https://www.playframework.com/documentation/2.5.x/JavaAsync#Using-CompletionStage-inside-an-Action
I addition to Nick's V answer.
If you are building a non-blocking app using Play Java API, it might become quite cumbersome to inject HttpExecutionContext and pass ec.current()) every time you need to call methods on CompletionStage.
To make life easier you can use a decorator, which will preserve the context between calls.
public class ContextPreservingCompletionStage<T> implements CompletionStage<T> {
private HttpExecutionContext context;
private CompletionStage<T> delegate;
public ContextPreservingCompletionStage(CompletionStage<T> delegate,
HttpExecutionContext context) {
this.delegate = delegate;
this.context = context;
}
...
}
So you will need to pass context only once:
return new ContextPreservingCompletionStage<>(someCompletableFuture, context)
.thenCompose(something -> {...});
.thenApply(something -> {...});
Instead of
return someCompletableFuture.thenComposeAsync(something -> {...}, context.current())
.thenApplyAsync(something -> {...}, context.current());
That is particularly useful if you are building a multi-tier app, and passing CompletionStages between different classes.
Full decorator implementation example is here.

How to mock Entity Framework in a N-Layer Architecture

I have a N-Layer application with Entity Framework (Code-First approach). Now I want to automatize some tests. I am using Moq framework. I am finding some problem about writing the tests. Perhaps my architecture is wrong? With wrong, I mean that I wrote components that are not well isolated and so they are not testable. I do not really like this... Or perhaps, I simply cannot use correctly moq framework.
I let you see my architecture:
At every level I inject my context in the constructor of the class.
The Facade:
public class PublicAreaFacade : IPublicAreaFacade, IDisposable
{
private UnitOfWork _unitOfWork;
public PublicAreaFacade(IDataContext context)
{
_unitOfWork = new UnitOfWork(context);
}
}
The BLL:
public abstract class BaseManager
{
protected IDataContext Context;
public BaseManager(IDataContext context)
{
this.Context = context;
}
}
The Repository:
public class Repository<TEntity>
where TEntity : class
{
internal PublicAreaContext _context;
internal DbSet<TEntity> _dbSet;
public Repository(IDataContext context)
{
this._context = context as PublicAreaContext;
}
}
IDataContext is an interface that is implemented by my DbContext:
public partial class PublicAreaContext : DbContext, IDataContext
Now, how I mock EF and how I write the tests:
[TestInitialize]
public void Init()
{
this._mockContext = ContextHelper.CreateCompleteContext();
}
Where ContextHelper.CreateCompleteContext() is:
public static PublicAreaContext CreateCompleteContext()
{
//Here I mock my context
var mockContext = new Mock<PublicAreaContext>();
//Here I mock my entities
List<Customer> customers = new List<Customer>()
{
new Customer() { Code = "123455" }, //Customer with no invoice
new Customer() { Code = "123456" }
};
var mockSetCustomer = ContextHelper.SetList(customers);
mockContext.Setup(m => m.Set<Customer>()).Returns(mockSetCustomer);
...
return mockContext.Object;
}
And here how I write my test:
[TestMethod]
public void Success()
{
#region Arrange
PrepareEasyPayPaymentRequest request = new PrepareEasyPayPaymentRequest();
request.CodiceEasyPay = "128855248542874445877";
request.Servizio = "MyService";
#endregion
#region Act
PublicAreaFacade facade = new PublicAreaFacade(this._mockContext);
PrepareEasyPayPaymentResponse response = facade.PrepareEasyPayPayment(request);
#endregion
#region Assert
Assert.IsTrue(response.Result == it.MC.WebApi.Models.ResponseDTO.ResponseResult.Success);
#endregion
}
Here It seems It works everything correctly!!! And It looks like my architecture is correct. But what if I want to insert/update an Entity? Nothing work anymore! I explain why:
As you can see I pass a *Request object (it is the DTO) to the facade, then in my TOA I generate my entity from the propertiess of the DTO:
private PaymentAttemptTrace CreatePaymentAttemptTraceEntity(string customerCode, int idInvoice, DateTime paymentDate)
{
PaymentAttemptTrace trace = new PaymentAttemptTrace();
trace.customerCode = customerCode;
trace.InvoiceId = idInvoice;
trace.PaymentDate = paymentDate;
return trace;
}
PaymentAttemptTrace is the Entity I will inserto to Entity Framework.. It is not mocked and I cannot inject it. So even if I pass my mocked context (IDataContext), when I try to insert an Entity that is not mocked my test fails!
Here that doubt about I have a wrong architecture has raised!
So, what's wrong? The architecture or the way I use moq?
Thank you for help
UPDATE
Here how I test my code.. For example, I want to test the trace of a payment..
Here the test:
[TestMethod]
public void NoPaymentDate()
{
TracePaymentAttemptRequest request = new TracePaymentAttemptRequest();
request.AliasTerminale = "MyTerminal";
//...
//I create my request object
//You can see how I create _mockContext above
PublicAreaFacade facade = new PublicAreaFacade(this._mockContext);
TracePaymentAttemptResponse response = facade.TracePaymentAttempt(request);
//My asserts
}
Here the facade:
public TracePaymentAttemptResponse TracePaymentAttempt(TracePaymentAttemptRequest request)
{
TracePaymentAttemptResponse response = new TracePaymentAttemptResponse();
try
{
...
_unitOfWork.PaymentsManager.SavePaymentAttemptResult(
easyPay.CustomerCode,
request.CodiceTransazione,
request.EsitoPagamento + " - " + request.DescrizioneEsitoPagamento,
request.Email,
request.AliasTerminale,
request.NumeroContratto,
easyPay.IdInvoice,
request.TotalePagamento,
paymentDate);
_unitOfWork.Commit();
response.Result = ResponseResult.Success;
}
catch (Exception ex)
{
response.Result = ResponseResult.Fail;
response.ResultMessage = ex.Message;
}
return response;
}
Here how I developed the PaymentsManager:
public PaymentAttemptTrace SavePaymentAttemptResult(string customerCode, string transactionCode, ...)
{
//here the problem... PaymentAttemptTrace is the entity of entity framework.. Here i do the NEW of the object.. It should be injected, but I think it would be a wrong solution
PaymentAttemptTrace trace = new PaymentAttemptTrace();
trace.customerCode = customerCode;
trace.InvoiceId = idInvoice;
trace.PaymentDate = paymentDate;
trace.Result = result;
trace.Email = email;
trace.Terminal = terminal;
trace.EasypayCode = transactionCode;
trace.Amount = amount;
trace.creditCardId = idCreditCard;
trace.PaymentMethod = paymentMethod;
Repository<PaymentAttemptTrace> repository = new Repository<PaymentAttemptTrace>(base.Context);
repository.Insert(trace);
return trace;
}
In the end how I wrote the repository:
public class Repository<TEntity>
where TEntity : class
{
internal PublicAreaContext _context;
internal DbSet<TEntity> _dbSet;
public Repository(IDataContext context)
{
//the context is mocked.. Its type is {Castle.Proxies.PublicAreaContextProxy}
this._context = context as PublicAreaContext;
//the entity is not mocked. Its type is {PaymentAttemptTrace} but should be {Castle.Proxies.PaymentAttemptTraceProxy}... so _dbSet result NULL
this._dbSet = this._context.Set<TEntity>();
}
public virtual void Insert(TEntity entity)
{
//_dbSet is NULL so "Object reference not set to an instance of an object" exception is raised
this._dbSet.Add(entity);
}
}
Your architecture looks good, but the implementation is flawed. It is leaking abstraction.
In your diagram the Façade layer depends only on the BLL but when you look at the PublicAreaFacade's constructor you will see that in reality it has a direct dependency to an interface from the Repository layer:
public PublicAreaFacade(IDataContext context)
{
_unitOfWork = new UnitOfWork(context);
}
This should not be. It should only take its direct dependency as input -- the PaymentsManager or -- even better -- an interface of it:
public PublicAreaFacade(IPaymentsManager paymentsManager)
{
...
}
The concequence is that your code becomes way more testable. When you look at your tests now you see that you have to mock the most inner layer of your system (i.e. the IDataContext and even its entity accessors Set<TEntity>) altough you are testing one of the most outer layers of your system (the PublicAreaFacade class).
This is how a unit test for the TracePaymentAttempt method would look like if the PublicAreaFacade only depended on IPaymentsManager:
[TestMethod]
public void CallsPaymentManagerWithRequestDataWhenTracingPaymentAttempts()
{
// Arrange
var pm = new Mock<IPaymentsManager>();
var pa = new PulicAreaFacade(pm.Object);
var payment = new TracePaymentAttemptRequest
{
...
}
// Act
pa.TracePaymentAttempt(payment);
// Assert that we call the correct method of the PaymentsManager with the data from
// the request.
pm.Verify(pm => pm.SavePaymentAttemptResult(
It.IsAny<string>(),
payment.CodiceTransazione,
payment.EsitoPagamento + " - " + payment.DescrizioneEsitoPagamento,
payment.Email,
payment.AliasTerminale,
payment.NumeroContratto,
It.IsAny<int>(),
payment.TotalePagamento,
It.IsAny<DateTime>()))
}
Pass IUnitOfWork into the Facade or BLL layer constructor, whichever one makes calls on the unit of work directly. Then you can setup what the Mock<IUnitOfWork> is returning in your tests. You should not need to pass IDataContext to everything except maybe the repo constructors and the unit of work.
For example, if the Facade has a method PrepareEasyPayPayment that makes a repo call through a UnitOfWork call, setup the mock like this:
// Arrange
var unitOfWork = new Mock<IUnitOfWork>();
unitOfWork.Setup(x => x.PrepareEasyPayPaymentRepoCall(request)).Returns(true);
var paymentFacade = new PaymentFacade(unitOfWork.Object);
// Act
var result = paymentFacade.PrepareEasyPayPayment(request);
Then you've mocked out the data call and can more easily test your code in the Facade.
For the insert testing, you should have a Facade method like CreatePayment which takes a PrepareEasyPayPaymentRequest. Inside that CreatePayment method, it should reference the repo, probably through the unit of work, like
var result = _unitOfWork.CreatePaymentRepoCall(request);
if (result == true)
{
// yes!
}
else
{
// oh no!
}
What you want to mock for unit testing is that this create/insert repo call returns true or false so you can test the code branches after the repo call has completed.
You can also test that the insert call was made as expected, but that's usually not as valuable unless the parameters for that call have a lot of logic involved in building them.
it sounds like you need to change the code a little bit. Newing things introduces hardcoded dependencies and makes them untestable, so try to abstract them away. Maybe you can hide everything to do with EF behind another layer, then all you have to do is mock that particular layer layer and never touch EF.
You can use this open source framework for unit testing which is good to mock entity framework dbcontext
https://effort.codeplex.com/
Try this will help you to mock your data efficiently.

How can I register a (boundless) type hierarchy using Autofac?

I've got a Factory interface (along with concrete implementations):
// foo.dll
interface IFooProvider
{
T GetFoo<T>()
where T : BaseFoo;
}
My BaseFoo is not abstract, but only its subclasses are actually useful:
// shared.dll
class BaseFoo
{ ... }
I've also got a (potentially unbounded) number of subclasses of BaseFoo across many assemblies:
// foo.dll
class AFoo : BaseFoo
{ ... }
// foo2.dll
class BFoo : BaseFoo
{ ... }
... and many more ...
Naively, I had been registering the Foo-derived classes in an unsurprising way:
// foo.dll
class ConcreteFooRegistration : Module
{
protected override void Load(ContainerBuilder builder)
{
// a concrete FooProvider is registered elsewhere
builder.Register(c => c.Resolve<IFooProvider>().GetFoo<AFoo>());
builder.Register(c => c.Resolve<IFooProvider>().GetFoo<BFoo>());
...
}
}
But this implies that:
the assembly containing ConcreteFooRegistration (e.g. foo.dll) also contains some/all of AFoo, BFoo, etc.
the assembly containing ConcreteFooRegistration (e.g. foo.dll) references the assemblies (e.g. foo2.dll) containing some/all of AFoo, BFoo, etc.
IFooProvider be available to any other assembly containing BaseFoo-derived classes and the Module that registers them
For sake of discussion, assume that none of these is possible and/or desirable. That is, I'm looking for solutions other than "move IFooProvider into shared.dll".
Since AFoo and BFoo are the real dependencies that other types are interested in, and IFooProvider is (from that perspective) just an instantiation detail, I got inspired by the Autofac+Serilog integration that Nicholas came up with. I've used a similar approach elsewhere, so I wrote up an AttachToComponentRegistration() implementation:
// foo.dll
class ConcreteFooRegistration : Module
{
// NOTICE: there's no Load() method
protected override void AttachToComponentRegistration(...)
{
...
registration.Preparing += (sender, e) =>
{
var pFoo = new ResolvedParameter(
(p, i) => p.ParameterType.IsAssignableTo<BaseFoo>(),
(p, i) => i.Resolve<IFooProvider>().GetFoo<FooWeNeed>()
);
e.Parameters = new [] { pFoo }.Concat(e.Parameters);
};
}
}
This was successful, in that I was able to remove all the individual BaseFoo-derived registrations from ConcreteFooRegistration and still successfully resolve arbitrary BaseFoo-derived dependencies with constructor injection:
// other.dll:
class WorkerRegisteration : Module
{
protected override void Load(ContainerBuilder builder)
{
builder.RegisterType<Worker>();
// NOTICE: FooYouDidntKnowAbout is NOT explicitly registered
}
}
class Worker
{
public Worker(FooYouDidntKnowAbout foo)
{ ... }
...
}
BUT: now I can't arbitrarily resolve AFoo outside of constructor injection:
builder.Register(c =>
{
// here's one use for a BaseFoo outside constructor injection
var foo = c.Resolve<AFoo>();
if (foo.PropValue1)
return new OtherClass(foo.PropValue2);
else
return new YetAnother(foo.PropValue3);
}
...
builder.Register(c =>
{
// here's another
var foo = c.Resolve<AFoo>();
return c.Resolve(foo.TypePropValue);
});
Assuming that publishing IFooProvider as a public export of foo.dll or moving it to shared.dll is undesirable/impossible, thus eliminating the naive-but-unsurprising implementation above, (how) can I set up my registrations to be able to resolve arbitrary subclasses of BaseFoo from anywhere?
Thanks!
I think what you're looking for is a registration source. A registration source is a dynamic "registration provider" you can use to feed Autofac registrations as needed.
As of this writing, the doc on registration sources is pretty thin (I just haven't gotten a chance to write it) but there's a blog article with some details about it.
Registration sources are how Autofac supports things like IEnumerable<T> or Lazy<T> - we don't require you actually register every collection, instead we dynamically feed the registrations into the container using sources.
Anyway, let me write you up a sample here and maybe I can use it later to massage it into the docs, eh? :)
First, let's define a very simple factory and implementation. I'm going to use "Service" instead of "Foo" here because my brain stumbles after it sees "foo" too many times. That's a "me" thing. But I digress.
public interface IServiceProvider
{
T GetService<T>() where T : BaseService;
}
public class ServiceProvider : IServiceProvider
{
public T GetService<T>() where T : BaseService
{
return (T)Activator.CreateInstance(typeof(T));
}
}
OK, now let's make the service types. Obviously for this sample all the types are sort of in one assembly, but when your code references the type and the JIT brings it in from some other assembly, it'll work just the same. Don't worry about cross-assembly stuff for this.
public abstract class BaseService { }
public class ServiceA : BaseService { }
public class ServiceB : BaseService { }
Finally, a couple of classes that consume the services, just so we can see it working.
public class ConsumerA
{
public ConsumerA(ServiceA service)
{
Console.WriteLine("ConsumerA: {0}", service.GetType());
}
}
public class ConsumerB
{
public ConsumerB(ServiceB service)
{
Console.WriteLine("ConsumerB: {0}", service.GetType());
}
}
Good.
Here's the important bit, now: the registration source. The registration source is where you will:
Determine if the resolve operation is asking for a BaseService type or not. If it's not, then you can't handle it so you'll bail.
Build up the dynamic registration for the specific type of BaseService derivative being requested, which will include the lambda that invokes the provider/factory to get the instance.
Return the dynamic registration to the resolve operation so it can do the work.
It looks like this:
using Autofac;
using Autofac.Core;
using Autofac.Core.Activators.Delegate;
using Autofac.Core.Lifetime;
using Autofac.Core.Registration;
public class ServiceRegistrationSource : IRegistrationSource
{
public IEnumerable<IComponentRegistration> RegistrationsFor(
Service service,
Func<Service, IEnumerable<IComponentRegistration>> registrationAccessor)
{
var swt = service as IServiceWithType;
if(swt == null || !typeof(BaseService).IsAssignableFrom(swt.ServiceType))
{
// It's not a request for the base service type, so skip it.
return Enumerable.Empty<IComponentRegistration>();
}
// This is where the magic happens!
var registration = new ComponentRegistration(
Guid.NewGuid(),
new DelegateActivator(swt.ServiceType, (c, p) =>
{
// The factory method is generic, but we're working
// at a reflection level, so there's a bit of crazy
// to deal with.
var provider = c.Resolve<IServiceProvider>();
var method = provider.GetType().GetMethod("GetService").MakeGenericMethod(swt.ServiceType);
return method.Invoke(provider, null);
}),
new CurrentScopeLifetime(),
InstanceSharing.None,
InstanceOwnership.OwnedByLifetimeScope,
new [] { service },
new Dictionary<string, object>());
return new IComponentRegistration[] { registration };
}
public bool IsAdapterForIndividualComponents { get{ return false; } }
}
It looks complex, but it's not too bad.
The last step is to get the factory registered as well as the registration source. For my sample, I put those in an Autofac module so they're both registered together - it doesn't make sense to have one without the other.
public class ServiceProviderModule : Autofac.Module
{
protected override void Load(ContainerBuilder builder)
{
builder.RegisterType<ServiceProvider>().As<IServiceProvider>();
builder.RegisterSource(new ServiceRegistrationSource());
}
}
Finally, let's see it in action. If I throw this code into a console app...
static void Main()
{
var builder = new ContainerBuilder();
builder.RegisterType<ConsumerA>();
builder.RegisterType<ConsumerB>();
builder.RegisterModule<ServiceProviderModule>();
var container = builder.Build();
using(var scope = container.BeginLifetimeScope())
{
var a = scope.Resolve<ConsumerA>();
var b = scope.Resolve<ConsumerB>();
}
}
What you end up with on the console is:
ConsumerA: ServiceA
ConsumerB: ServiceB
Note I had to register my consuming classes but I didn't explicitly register any of the BaseService-derived classes - that was all done by the registration source.
If you want to see more registration source samples, check out the Autofac source, particularly under the Autofac.Features namespace. There you'll find things like the CollectionRegistrationSource, which is responsible for handling IEnumerable<T> support.

Fluent NHibernate not querying the database correctly

i have just looked into hooking my application into nhibernate (fluent nhibernate) but i am having a few difficulties...
I have tried to follow what people have done and found this:
public class NHibernateSessionPerRequest : IHttpModule
{
private static readonly ISessionFactory _sessionFactory;
static NHibernateSessionPerRequest()
{
_sessionFactory = CreateSessionFactory();
}
public void Init(HttpApplication context)
{
context.BeginRequest += BeginRequest;
context.EndRequest += EndRequest;
}
public static ISession GetCurrentSession()
{
return _sessionFactory.GetCurrentSession();
}
public void Dispose() { }
private static void BeginRequest(object sender, EventArgs e)
{
ISession session = _sessionFactory.OpenSession();
session.BeginTransaction();
CurrentSessionContext.Bind(session);
}
private static void EndRequest(object sender, EventArgs e)
{
ISession session = CurrentSessionContext.Unbind(_sessionFactory);
if (session == null) return;
try
{
session.Transaction.Commit();
}
catch (Exception)
{
session.Transaction.Rollback();
}
finally
{
session.Close();
session.Dispose();
}
}
private static ISessionFactory CreateSessionFactory()
{
string connString = "AV8MediaUser";
FluentConfiguration configuration = Fluently.Configure()
.Database(MsSqlConfiguration.MsSql2008.ShowSql().ConnectionString(
x => x.FromConnectionStringWithKey(connString)))
.ExposeConfiguration(
c => c.SetProperty("current_session_context_class", "web"))
.Mappings(m => m.FluentMappings.AddFromAssemblyOf<Category>());
return configuration.BuildSessionFactory();
}
}
But when i run it through it seems to connect to the database correctly but it doesnt run a query:
return Session.CreateCriteria<Category>()
.List<Category>();
Am i doing something stupidly wrong?
CreateCriteria expects you to specify a model object name that you'd like to retrieve instances of. You seem to be missing your model object type name. Try something more like this:
List<YourModelObject> results = session.CreateCriteria<YourModelObject>()
.List<YourModelObject>();
To see what's actually being sent to the database consider using Ayende's NHProfiler - it will come in handy later when seeing what your more complex criteria queries or HQL queries actually result in...
Got it... i didnt realise that my mappings werent included in the project for some unknown reason... but included them again and it is all good!
Weird how it didnt throw an error though.