I'm attempting to run MediatR with Autofac in .NET Framework 4.6.2.
My registration:
public class MediatorModule : Autofac.Module
{
protected override void Load(ContainerBuilder builder)
{
builder.RegisterSource(new ContravariantRegistrationSource());
builder.RegisterAssemblyTypes(typeof(IMediator ).Assembly).AsImplementedInterfaces();
builder.RegisterType<HealthCheckQueryHandler>().As<IRequestHandler<HealthCheckQuery, HttpStatusCode>>();
}
}
My controller:
[RoutePrefix("api/v1/healthcheck")]
public class HealthCheckController : ApiController
{
private readonly IMediator _mediator;
public HealthCheckController(IMediator mediator)
{
_mediator = mediator;
}
[HttpGet]
[Route("")]
public async Task<HttpStatusCode> Get()
{
var query = new HealthCheckQuery();
var result = await _mediator.Send(query);
return result;
}
}
When I try to hit this api method, I receive the following error:
None of the constructors found with 'Autofac.Core.Activators.Reflection.DefaultConstructorFinder' on type 'MediatR.Mediator' can be invoked with the available services and parameters:\r\nCannot resolve parameter 'MediatR.ServiceFactory serviceFactory' of constructor 'Void .ctor(MediatR.ServiceFactory)
The wiki (https://github.com/jbogard/MediatR/wiki) asks us to register SingleInstanceFactory and MultiInstanceFactory. But Visual Studio is not able to resolve either of these symbols.
How can I resolve this to get MediatR working with Autofac?
I know this is an old post, but in case anyone else googles for this.
You're missing registering the ServiceFactory instance.
builder.Register<ServiceFactory>(ctx =>
{
var c = ctx.Resolve<IComponentContext>();
return t => c.Resolve(t);
});
Taken from here: MediatR Examples
I assume you are using Mediatr v3.0.x.
You are missing the registrations for the SingleInstanceFactory and MultiInstanceFactory as here below.
builder.Register<SingleInstanceFactory>(ctx => {
IComponentContext c = ctx.Resolve<IComponentContext>();
return type => {
Object o;
return c.TryResolve(type, out o) ? o : null;
};
});
builder.Register<MultiInstanceFactory>(ctx => {
IComponentContext c = ctx.Resolve<IComponentContext>();
return type => (IEnumerable<Object>)c.Resolve(typeof(IEnumerable<>).MakeGenericType(type));
});
Related
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.
On the ContainerBuilder i can do the following:
builder.Register<ScenariosConfig>(c =>
(ScenariosConfig)c.Resolve<ConfigFactory>()
.Create(typeof(ScenariosConfig)))
.SingleInstance();
With assembly scanning i can do the following:
builder.RegisterAssemblyTypes(assemblies)
.Where(HasSingletonAttribute)
.As(t => GetNameMatchingInterfaces(t))
.SingleInstance();
Now the question: Is there any way to achieve the following: ?
builder.RegisterAssemblyTypes(assemblies)
.Where(... some condition)
.CreateByDelegate((container, type)
=> c.Resolve<ConfigFactory>().Create(type))
.SingleInstance();
I've already found out about IRegistrationSource with which i can achieve something similar. However, I'm a bit skeptic about the performance impact of creating ton's of IRegistrationSource's for each of my conventions which require a delegate for creation...
And also there's the fact that IRegistrationSource can't be used whenever you need to resolve all instances of IFoo which should be bound by such a "convention".
In the end we did choose to use an IRegistrationSource. The only alternative i've "found" would have been to detect all types per reflection (not using autofac API...) and then generating a delegate for each and registering this with autofac. Would not really result in code that easily understandable...
So for completeness sake' here's the IRegistrationSource implementation:
public class ConfigConventionRegistrationSource : IRegistrationSource
{
public IEnumerable<IComponentRegistration> RegistrationsFor(
Service service,
Func<Service, IEnumerable<IComponentRegistration>> registrationAccessor)
{
var s = service as IServiceWithType;
if (s != null
&& s.ServiceType.IsClass
&& s.ServiceType.Name.EndsWith("Config")
&& !s.ServiceType.GetInterfaces().Any())
{
yield return RegistrationBuilder
.ForDelegate((componentContext, parameters) =>
CreateConfigByFactory(componentContext, s.ServiceType))
.As(s.ServiceType)
.SingleInstance()
.CreateRegistration();
}
}
private static object CreateConfigByFactory(
IComponentContext componentContext,
Type configType)
{
IConfig configFactory = componentContext.Resolve<IConfig>();
MethodInfo method = Reflector<IConfig>
.GetMethod(x => x.Load<object>())
.GetGenericMethodDefinition()
.MakeGenericMethod(configType);
try
{
return method.Invoke(configFactory, new object[0]);
}
catch (TargetInvocationException tex)
{
ExceptionDispatchInfo
.Capture(tex.InnerException)
.Throw();
throw; // will not be reached as thrown above ;-)
}
}
public bool IsAdapterForIndividualComponents
{
get { return false; }
}
}
I would like to implement generic repository pattern with IDbSet<> interface of Entity Framework.
When i ask IDbSet<T> from Autofac, It should resolve IDbContext then call its Set<T> method to return the concrete type of IDbSet<T>
As an example, it should be doing something like this:
builder.Register<IDbSet<T>>(context => context.Resolve<IDbContext>().Set<T>());
How can i achive this with Autofac?
It seems based on this answer: https://stackoverflow.com/a/7997162/872395
that the only solution is to create a custom IRegistrationSource where you create the closed registrations:
public class DbSetRegistrationSource : IRegistrationSource
{
public bool IsAdapterForIndividualComponents
{
get { return true; }
}
public IEnumerable<IComponentRegistration> RegistrationsFor(
Service service,
Func<Service, IEnumerable<IComponentRegistration>> registrationAccessor)
{
var swt = service as IServiceWithType;
if (swt == null || !swt.ServiceType.IsGenericType)
yield break;
var def = swt.ServiceType.GetGenericTypeDefinition();
if (def != typeof(IDbSet<>))
yield break;
// if you have one `IDBContext` registeration you don't need the
// foreach over the registrationAccessor(dbContextServices)
yield return RegistrationBuilder.ForDelegate((c, p) =>
{
var dBContext = c.Resolve<IDBContext>();
var m = dBContext.GetType().GetMethod("Set", new Type[] {});
var method =
m.MakeGenericMethod(swt.ServiceType.GetGenericArguments());
return method.Invoke(dBContext, null);
})
.As(service)
.CreateRegistration();
}
}
The usage is very simple:
var containerBuilder = new ContainerBuilder();
containerBuilder.RegisterSource(new DbSetRegistrationSource());
containerBuilder.RegisterType<DbContext>().As<IDBContext>();
var container = containerBuilder.Build();
Beginners question:
Given two classes: Myclass5 and Myclass6 how can one wire up following factory method (returned as Func)
such that
myclass5 and myclass6 instances and IMyClass that they depend on are all retrieved via autofac (assuming that these three instances are registered).
public static MyClass4 FactoryMethod(int nu)
{
if (nu == 1)
return new MyClass5(....);
if (nu == 4)
return new MyClass6(....);
throw new NotImplementedException();
}
public abstract class MyClass4
{
}
public class MyClass5 : MyClass4
{
public MyClass5(int nu, IMyClass a)
{
}
}
public class MyClass6 : MyClass4
{
public MyClass6(int nu, IMyClass a)
{
}
}
For FactoryMethod to be able to create the instances, it requires access to a container. I would suggest create a delegate type for the factory method, which makes it easy to take dependency on it. Registration goes like this:
var cb = new ContainerBuilder();
cb.RegisterType<SomeClass>().As<IMyClass>();
cb.RegisterType<MyClass5>();
cb.RegisterType<MyClass6>();
cb.Register((c, p) =>
{
var context = c.Resolve<IComponentContext>();
return new FactoryMethod(nu =>
{
var nuParameter = TypedParameter.From(nu);
switch (nu)
{
case 1:
return context.Resolve<MyClass5>(nuParameter);
case 4:
return context.Resolve<MyClass6>(nuParameter);
default:
throw new NotImplementedException();
}
});
});
var container = cb.Build();
At resolve time, you can then take a dependency on the FactoryMethod delegate type and use it to resolve instances:
var factory = container.Resolve<FactoryMethod>();
var instance5 = factory(1);
var instance6 = factory(1);
Note: the delegate instance we're creating needs a context. We cannot use the c parameter directly since that context is only temporary. Thus we must resolve a IComponentContext to "bake" into the lambda.
Update: if you would like to extract the factory implementation into a method that is not dependent on the container I would suggest the following:
public class FactoryMethodImpl
{
readonly Func<int, MyClass5> _factory5;
readonly Func<int, MyClass6> _factory6;
public FactoryMethodImpl(Func<int, MyClass5> factory5, Func<int, MyClass6> factory6)
{
_factory5 = factory5;
_factory6 = factory6;
}
public MyClass4 Create(int nu)
{
switch (nu)
{
case 1:
return _factory5(nu);
case 4:
return _factory6(nu);
default:
throw new NotImplementedException();
}
}
}
Now, change the registration code to this:
var cb = new ContainerBuilder();
cb.RegisterType<SomeClass>().As<IMyClass>();
cb.RegisterType<MyClass5>();
cb.RegisterType<MyClass6>();
cb.RegisterType<FactoryMethodImpl>().SingleInstance();
cb.Register(c=> new FactoryMethod(c.Resolve<FactoryMethodImpl>().Create));
I'm developing modular application and I'd like for entities from different modules to be able to register their own friendly url slugs.
app.UseMvc(routes =>
{
routes.Routes.Add(new SlugRouter(routes.DefaultHandler));
(...)
});
But following code throws Cannot access a disposed object. Object name: 'CommerceDbContext'. when trying to access slug from the repository.
public class SlugRouter : IRouter
{
private readonly IRouter _target;
public SlugRouter(IRouter target)
{
_target = target;
}
public async Task RouteAsync(RouteContext context)
{
var slugRepository = context.HttpContext.RequestServices.GetService<IRepository<SlugEntity>>();
// ERROR: Cannot access a disposed object. Object name: 'CommerceDbContext'
var urlSlug = await slugRepository.GetAllIncluding(x => x.EntityType).FirstOrDefaultAsync(x => x.Slug == context.HttpContext.Request.Path.Value);
(...)
}
It must be something simple I'm missing to be able to access the repository from router. Thanks for any help.
Begin a unit of work:
public async Task RouteAsync(RouteContext context)
{
var slugRepository = context.HttpContext.RequestServices.GetService<IRepository<SlugEntity>>();
var unitOfWorkManager = context.HttpContext.RequestServices.GetService<IUnitOfWorkManager>();
using (var uow = unitOfWorkManager.Begin())
{
var urlSlug = await slugRepository.GetAllIncluding(x => x.EntityType).FirstOrDefaultAsync(x => x.Slug == context.HttpContext.Request.Path.Value);
await uow.CompleteAsync();
}
}
Access IModel. You do not need dbContext for.
for entities from different modules to be able to register their own
friendly url slugs
I do it this way:
1) move OnModelCreating to static methiod
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
BuildModel(modelBuilder);
}
public static void BuildModel(ModelBuilder modelBuilder)
{
// ...
}
2) Create model where you need:
var conventionSet = new ConventionSet();
var modelBuilder = new ModelBuilder(conventionSet);
AdminkaDbContext.BuildModel(modelBuilder);
var mutableModel = modelBuilder.Model;
There is your meta (in mutableModel ). You can loop through entities (types of entities).