I am writing a small application that needs to communicate with a server using .NET Remoting. I am using AutoFac to register my instances and ran in to a problem when my remoting proxy object was being disposed, here is some sample code:
builder.Register(b =>
{
var channel = new TcpClientChannel();
// ...
var remoteObj = (IMyComponent)Activator.GetObject(typeof(IMyComponent), "tcp://...");
return remoteObj;
}).As<IMyComponent>();
// ... and then when using it:
using (var scope = this.container.BeginLifetimeScope()) {
var myComponent = scope.Resolve<IMyComponent>();
} // <= An exception will be thrown here since AutoFac will try to call .Dispose on myComponent
// Later I realized that the exception can be fixed by specifying an "empty" OnRealease-behavior when registering the component, probably because AutoFac doesnt try to treat MyComponent like an IDisposable.
...
}).As<MyIComponent>().OnRelease(c => { //Manual disposing here });
This exception caused me to wonder if I am doing something completely wrong here and how to I am supposed to handle the lifetime of the remote proxy correctly. Is there anything wrong in my approach, ie to "create" and return remote proxys through AutoFac? If so, how should the lifetime of remote proxys be handled?
There are some detailed docs on how Autofac handles disposal on the Autofac doc site. this may help clear up some of your questions.
If you have an IDisposable component on which you do not want Autofac to call Dispose for you, register it as ExternallyOwned and automatic disposal will be disabled.
Related
I am trying to add some items to the piranha sitemap using the delegate method OnGenerateSitemaps.
In this method I am calling to a service that gets data from entity framework context and then caches it. Whenever I try to use this service in the delegate-method I get a error that the dbContext has already been disposed.
System.AggregateException: 'One or more errors occurred. (Cannot
access a disposed context instance. A common cause of this error is
disposing a context instance 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 instance, or wrapping it in a using
statement. If you are using dependency injection, you should let the
dependency injection container take care of disposing context
instances.
I've tried making the service sync instead of async, I've tried awaiting the result and running the task sync, none of which works.
Any ideas on how to use my service in this delegate in Configure on startup?
services.AddScoped<ICachedSitemapService, CachedSitemapService>();
In startup I inject the service, which is scoped.
public void Configure(IApplicationBuilder app, IWebHostEnvironment env, IApi api, ICachedSitemapService cachedSitemapService)
{
App.Hooks.OnGenerateSitemap += (sitemap) =>
{
var items = await cachedSitemapService.GetCachedClinics().Result;
sitemap.AddRange(items);
return sitemap;
};
}
The service that is called is DbContext to get items:
private async Task<IEnumerable<SitemapItem>> GetSitemapClinics()
{
var spec = new ClinicSitemapSpecification();
//This throws error
var allClinics = await _clinicRepo.ListAsync(spec);
//code shortened for brevity, but returns a list.
}
I've tried below without any luck.
var items = await cachedSitemapService.GetCachedClinics().GetAwaiter().GetResult();
sitemap.AddRange(items);
We're (the Piranha team) planning on redesigning the hook system in version 10 (has to be a major version as it will break backward compatibility) to provide DI functionality in all hooks. However, in the meantime, the following should fix your issues.
using (var scope = app.ApplicationServices.CreateScope())
{
var service = scope.ServiceProvider.GetService< ICachedSitemapService>();
}
Since your service is registered to be scoped it can't be resolved directly from the root provider (like the error states). When resolving the service from a controller you are in the request scope, so the solution here is to just create a scope that will allow you to resolve it.
I use AutoFac. I have to resolve a type with an explicit instance which I get from another service.
For example: I have an instance of type Client which I get from somewhere (not from the container).
I want to configure the Autofac container so that always when an object of type Client should be resolved, it should return my instance.
Problem is that I don't have this instance at the time, when I configure the container with the Containerbuilder - so I cannot use for example LambdaRegistration.
Is there another solution for solving my problem?
You can do the following:
MyService service = null;
builder.Register(c => service).As<IMyService>();
// Later on
service = new MyService();
Depending on your needs there are quite some variations of this approach possible, such as:
Send a 'setter' delegate to some initialization code that will call the delegate after the service gets created, e.g. MyServiceInitializer.AfterInitialization(s => service = s);
Promote the service variable to a class property and provide that new wrapper to the initialization
Hide access to the service behind specific read and write abstractions, e.g. interface IMyServiceContext { IMyService Current { get; } } and interface IMyServiceSetter { void SetCurrent(IMyService service); }.
Prevent Autofac from accidentally resolving the service before it is initialized by throwing an exception instead of throwing null, e.g. Register(c => service ?? throw new InvalidOperationException("..."))
It's important to note, however, that in general, the creation of components should be fast and reliable. The fact that your component isn't available at startup is likely because it requires I/O to setup. This is a situation should should try to prevent, for instance by hiding it behind an abstraction completely. This allows you to implement a Proxy that allows the real service to be lazy loaded.
Hopefully this gives you some clues on how to solve this.
I have an issue with a database context being disposed. I have set up the databases like the below in the Configure services method. The code has been simplified to hopefully make it easier to read.
public void ConfigureServices(IServicesCollection services)
{
Services.AddDbContext<ApplicationDbContext>(options => options.UseSqlServer(Configuration.GetConnectionString("Database1")));
Services.AddDbContext<ApplicationDbContext>(options => options.UseSqlServer(Configuration.GetConnectionString("Database2")));
Services.AddDbContext<ApplicationDbContext>(options => options.UseSqlServer(Configuration.GetConnectionString("Database3")));
Services.AddDbContext<ApplicationDbContext>(options => options.UseSqlServer(Configuration.GetConnectionString("Database4")));
Services.AddScoped<IQueryService1, ConcreteService1>();
Services.AddScoped<IQueryService1, ConcreteService2>();
Services.AddScoped<IQueryService1, ConcreteService3>();
Services.AddScoped<IQueryService1, ConcreteService4>();
}
Now in one of the controllers I inject the relevant services that are required.
[Produces("application/json")]
[Route("api/[controller]/[action]
public class DigitalFinesController : Controller
{
private readonly IQueryService1 _Service1;
public DigitalFinesController(IConfiguration configuration, IQueryServices1 QueryService1)
{
_Service1 = QueryService1;
}
[Authorize]
[HttpPost]
[ActionName("SubmitFine")]
[ProducesResponseType(200)]
[ProducesResponseType(401)]
public async Task<IActionResult> SubmitFine([FromBody] Models.DigitalFine fine)
{
//This is a simple version of my issue
var vehicles = _Service1.Vehicles.FirstOrDefault(p=> p.vrm == "OX7 DFG");
if(vehicle == null)
{
return BadRequest("Vehicle is missing");
}
var fleet = _Service1.Fleets.FirstOrDefault(p=> p.Code = "MyCode");
}
}
And once I get to the second query I get the following exception
System.ObjectDisposedException: '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.
And I am stumped to understand why this is happening. Can anyone please give me a pointer to fix this?
Many thanks
Simon
I think it may have to do with how you're registering it. Try registering it with AddSingleton instead of AddScoped
https://learn.microsoft.com/en-us/aspnet/core/fundamentals/dependency-injection?view=aspnetcore-2.2#service-lifetimes-and-registration-options
Scoped
Scoped lifetime services are created once per request.
Warning
When using a scoped service in a middleware, inject the service into the Invoke or InvokeAsync method. Don't inject via constructor injection because it forces the service to behave like a singleton. For more information, see ASP.NET Core Middleware.
Singleton
Singleton lifetime services are created the first time they're requested (or when ConfigureServices is run and an instance is specified with the service registration). Every subsequent request uses the same instance. If the app requires singleton behaviour, allowing the service container to manage the service's lifetime is recommended. Don't implement the singleton design pattern and provide user code to manage the object's lifetime in the class.
Warning
It's dangerous to resolve a scoped service from a singleton. It may cause the service to have incorrect state when processing subsequent requests.
.AddScoped will dispose after lifetime of the request, try changing to singleton or transient:
Please see documentation:
https://learn.microsoft.com/en-us/aspnet/core/fundamentals/dependency-injection?view=aspnetcore-2.2
At this section:
In the sample app, the IMyDependency service is registered with the
concrete type MyDependency. The registration scopes the service
lifetime to the lifetime of a single request. Service lifetimes are
described later in this topic.
Warning
When using a scoped service in a middleware, inject the service into
the Invoke or InvokeAsync method. Don't inject via constructor
injection because it forces the service to behave like a singleton.
For more information, see ASP.NET Core Middleware.
I'm trying to get something like this working:
public class FooService : IFooService {
public FooService(Func<IBarService> barFactory) { ... }
}
public class BarService : IBarService, IDisposable { ... }
services.AddSingleton<IFooService, FooService>();
services.AddTransient<IBarService, BarService>();
services.AddSingleton<Func<IBarService>>(ctx => () => ctx.GetService<IBarService());
This works as far as resolving the BarService instance, but I can't figure out how to properly manage its lifetime. When I do this inside one of the members of FooService:
using (var bar = _barFactory())
{
...
}
I get an ObjectDispoedException:
System.ObjectDisposedException: 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.
However, if I just do var bar = _barFactory();, without the using statement, I have no way to signal to the DI container that I'm done with the instance, and it can be disposed.
What's the correct approach here?
(Side note: yes, I know that some will object that a singleton service should not be dependent on a transient service. That's not what's happening here; the singleton service is dependent on a singleton factory, that produces transient instances. The singleton then uses the transient service for one or two statements and then is done with it, so there should be no actual lifetime problems here.)
As described in documentation:
The container will call Dispose for IDisposable types it creates. However, if you add an instance to the container yourself, it will not be disposed.
So just don't use using statement and all should be OK.
Context:
I am using DI in my Web application. (I am using NInject, but hopefully this should not matter)
Some places constructor injection is not possible, for example in my custom log4net database logger (that's not me, who instantiates my custom logger instead the log4net framework). So I am using my DI container there in service locator DP mode, and asking an instance resolve explicitly in the logger code.
Note this is just a sample, in many other cases I had to use NInject as service locator DP instead of constructor injection.
Now the problem:
I have an IAuditContextProvider which serves current request's audit data, like IP etc. The question arises how I configure my DI container to instantiate a concrete provider. So far I've used a request scope (singleton by request) what is supported out of box by NInject.
However recently I faced the fact I had to start a background processing initiated by a request. This is done by
// This is 'outside' it's actually a request serving method running in the request context, btw it is an MVC action method,
// Pseudo code:
var auditProvider = Locator.Resolve<IAuditProvider>()
Task.Run(() =>
{
// I would like to get the very same resolved auditProvider instance here as outside.
// Please note: outer local variables are not solution, because of implicit calls here inside, for example if there is a logging statement here, then the service locator in the custom logger must resolve the very same instance as outside
// Some how I must 'stamp' this thread to be the 'same' as the outside
// request thread in point of view of my custom scope resolver (see below)
}
Note: Configuring the DI container a wide scoped singleton are not solution because of multiple requests are server parallel, and they can not use a common auditProvider.
OK, I thought this is what for custom (resolving) scopes are for. Here is the pseudo code how I am configuring my DI container:
kernel
.Bind(typeof(IAuditContextProvider))
.To(typeof(WebAuditContextProvider)).InScope(dummy =>
{
// Here I have to return a very same object/number/id when in
// 'outside' the request thread, and inside the worker thread.
// This way I hopefully get the very same instance when resolving.
// To be short: I have no idea how?
});
I don't think there is a good answer for your question within the current bounds.
I do have an alternative suggestion - just perform the work synchronously in another process. This would require a form of inter-process communication (IPC) but shouldn't be too difficult.
A simple but effective form of IPC is just writing a record to a database table (acting like a queue) and having a windows service/daemon polling for new records to "process". In this example, you would put a record in the table with the contextual information (user id, etc) and the service would utilize this context to perform the work synchronously, but the workflow would be asynchronous to the Web UI.
This also has a nice side benefit: You can start to build monitoring, retry logic, etc into the service. These things are much harder to do reliably within an ASP.NET model.
You could forgo the database queue completely by using something like message queues/buses/events, but the basic concept is the same.
Update:
Did you try to use closures in C#? Like this:
var auditProvider = Locator.Resolve<IAuditProvider>()
Task.Run(() =>
{
// with closure you'll get that very variable you need:
auditProvider.SomeMethod();
}
You should read whole article about closures by John Skeet and how they can help you together with TPL.
Other useful information:
Such DI is being called as Ambient Context in famous book Dependency Injection by M. Seeman:
A truly universal CROSS-CUTTING CONCERN can potentially pollute a large part of the API for an application if you have to pass an instance around to every collaborator. An alternative is to define a context that’s available to anyone who needs it and that can be ignored by everyone else.
The AMBIENT CONTEXT is available to any consumer via a static property
or method. A consuming class might use it like this:
public string GetMessage() { return SomeContext.Current.SomeValue; }
In this case, the context has a static Current property that a consumer can access. This property may be truly static, or may be associated with the currently executing thread. To be useful in DI scenarios, the context itself must be an ABSTRACTION and it must be possible to modify the context from the outside—in the previous example, this means that the Current property must be writable. The context itself might be implemented as shown in the following listing.
The context is an abstract class, which allows us to replace one context with another implementation at runtime.
public abstract class SomeContext
{
public static SomeContext Current
{
get
{
// Get current context from TLS
var ctx = Thread.GetData(Thread.GetNamedDataSlot("SomeContext")) as SomeContext;
if (ctx == null)
{
ctx = SomeContext.Default;
Thread.SetData(Thread.GetNamedDataSlot("SomeContext"), ctx);
}
return ctx;
}
set
{
Thread.SetData(Thread.GetNamedDataSlot("SomeContext"), value);
}
}
public static SomeContext Default = new DefaultContext();
public abstract string SomeValue { get; }
}
TLS here stands for Thread Local Storage, which can be useful idea for you here.
Also I suggest you to read about OperationContext class, which can be helpful for you if you want to pass some context for your Task, something like this:
// save current context before task start
var operationContext = OperationContext.Current;
Task.Run(() =>
{
// set current operation context inside your Task with closure
OperationContext.Current = operationContext;
// Your work here
}