Prism IDisposable Autofac and Lifetime Scope - prism

I am using Prism to navigate between views in my WPF application. One view in particular I've implemented with the IRegionMemberLifetime.KeepAlive => returns false; to create a new instance of the view every time we navigate to the view (we need to do this for display reasons). This view also hosts a custom win32 control that I need to do some cleanup in using IDisposable.Dispose. When I navigate to my view and then away from it, I'd expect Dispose to get called (to run cleanup). I was able to achieve this by implementing a custom region behavior as discussed here, https://github.com/PrismLibrary/Prism/issues/7. All this is working fine except everything gets marked for disposal but the GC doesn't actually get rid of anything. I'm using Autofac as my IOC container and after doing some research I've concluded the reason comes down to Autofac and lifetime scopes of IDisposables, https://nblumhardt.com/2011/01/an-autofac-lifetime-primer/. Basically Autofac holds references to the IDisposable and the GC won't get rid of the old view because of this. For instance I'm registering my view in the Module as _container.RegisterTypeForNavigation(); I'm not able to register this with any sort of lifetime and I'm not sure how I'd resolve this with a lifetime specified? When I call RegionManager.RequestNavigate I don't see any sort of overloads to specify lifetime? Any ideas would be appreciated.

RegisterTypeForNavigation essentially does builder.RegisterType(type).Named<object>(name); which you can do yourself, too, of course and apply any lifetime you desire. There's no magic in registering for navigation, RegisterTypeForNavigation is just a shorthand.
To make Autofac ignore the IDisposables, one can write
builder.RegisterType<SomeView>().Named<object>(typeof(SomeView).Name).ExternallyOwned();
From the docs:
Disabling Disposal
Components are owned by the container by default and will be disposed by it when appropriate. To disable this, register a component as having external ownership:
builder.RegisterType<SomeComponent>().ExternallyOwned();
The container will never call Dispose() on an object registered with external ownership. It is up to you to dispose of components registered in this fashion.

So extending #Haukinger answer. This is what finally worked for me:
//builder.RegisterTypeForNavigation<SomeView>();
builder.RegisterType<SomeView>().Named<object>
(typeof(SomeView).Name).ExternallyOwned();
That ExternallyOwned() signals to autofac that the user is going to handle calling dispose and that autofac shouldn't track the IDisposable.

Related

Is there a simple way to auto register [Exports] in a Prism app? WPF .NET 4.8

I used to be on a project that used Prism and when we needed a new service to do something, we'd just create an interface, a concrete class that implemented that interface and exported it, and then it just became available everywhere for [ImportingConstructor]. We didn't need to manually register it or anything. I no longer have access to that project, but I don't think there was any reflection magic that was done manually to accomplish this.
I'm in a new company and we are starting up a project using MEF / Prism and I'm trying to accomplish the same thing, but as of right now, I'm having to manually register items in order to import them. What am I missing?
I'm in .NET 4.8 WPF app
Additional info
we are basing our project from this website
https://prismlibrary.com/index.html
This is our app class
public partial class App
{
protected override Window CreateShell()
{
return Container.Resolve<ShellWindow>();
}
protected override void RegisterTypes(IContainerRegistry containerRegistry)
{
containerRegistry.RegisterSingleton<IStartupActionService, StartupActionService>();
containerRegistry.RegisterSingleton<IGeneralNavigationService, GeneralNavigationService>();
containerRegistry.RegisterSingleton<IExperimentSetupNavigationService, ExperimentSetupNavigationService>();
containerRegistry.RegisterSingleton<IProtocolSetupNavigationService, ProtocolSetupNavigationService>();
containerRegistry.RegisterSingleton<IOurProjectNavigationService, OurProjectNavigationService>();
containerRegistry.RegisterSingleton<IOurProjectUiService, OurProjectUiService>();
containerRegistry.RegisterManySingleton<WcfClientService>();
containerRegistry.RegisterSingleton<IControlClientService, ControlClientService>();
}
}
Why do I have to register each new service?
I've been reading about MEF, DryIoc, and others and I'm just not getting clear answers. Is there not a way to just have everything with an [Export] become immediately available for import?
Something else I need to do, that I think this whole registering thing is messing me up on is trying to come up with a way to have "dialogs" but tie them to a neutral class to make it more MVVM happy.
Dialog -> a region that pops open when you call a method. This method currently takes in a UserControl, assumed to have already been constructed and its ViewModel datacontext already attached.
What I would like to do and don't know how to start is
use a neutral container class to open one of these dialogs (similar to interaction request Notification
using attributes, attach an attribute to a view that indicates "I support this neutral container class" (assumed only one view per container)
this view supports [ImportingConstuctor] to bring in its ViewModel
the viewmodel itself supports [ImportingConstuctor] to bring in services needed
again, the desire to NOT need to register these items manually as we add them. Would like to add a service interface, the concrete class that [Export]s the interface and have it just available to the viewmodel and other services, and same for the views and viewmodels, export attribute tag them as necessary and have them just available to either/both grab an instance of them or manually create an instance of them and have their [ImportingConstructors] handled for me.

StructureMap3 HybridHttpOrThreadLocalScoped with no HttpSessionState

I am trying to figure out how to setup a StructureMap3 configuration, that works in both a WebApi and in a Console application, like:
For<ISession>().HybridHttpOrThreadLocalScoped().Use(p => p.GetInstance<TestingContainer>().GetSession());
For console apps I would like the object to live as long as the thread lives, and for websites as long as the http-session lives.
This is possible with MVC websites because HybridHttpOrThreadLocalScoped use the HttpSessionState to determine whether to create a new instance or to reuse an existing instance.
WebApi doesn't have this HttpSessionState and therefore HybridHttpOrThreadLocalScoped won't work.
If I didn't care about the console app, then I would probably configure structuremap with Transient() or AlwaysUnique or similar.
So, what is the equivalent to HybridHttpOrThreadLocalScoped when there are no HttpSessionState instance.
Thank you.
EDIT
-to rearrange the question...
In general you should favor Nested Containers for lifecycle management. The reasons behind this are exactly what you've just noted, that in some situations using either Thread, HTTP, or hybrid scoped simply doesn't work. I've seen it cause huge issues before where people assume DB connections are being disposed because they are in other environments, but in one environment they aren't. Also, the explicitness is nice.
To do this set the dependencies you want disposed per request to Transient (the default) and dispose of the nested container at the end of the request. I've written about this workflow in webapi here. Additionally the official docs recommend this nuget.
For the console app you'll want to do something like this:
//parent Container set up at app start
public void On_UserAction()
{
//global container set up at app start, either use ObjectFactory (bad, deprecated and to be removed) or just keep track of it yourself.
using(var nestedContainer = GlobalContainer.GetNestedContainer())
{
var dependency = nestedContainer.GetInstance<DependencyThatHandlesUserInput>();
}
}
and that's it, the using block handles all the disposal for you.
If you have any other questions please ask, I've spent a lot of time on this sort of thing :).

How to prevent controller, service and repository constructor executed by the mvc sitemap provider's menu html helper?

In the _Layout.cshtml, the #Html.MvcSiteMap().Menu("viewname") caused extra 2s in each request. I found that the repository's constructor being executed several times depends on the menu's count so I guess this might be where the extra 2 seconds cames from.
Is there a way to prevent the constructors be executed once the menu rendered?
I suspect the reason for this is because you are using Security Trimming. In order to determine whether each link has access, MVCSiteMapProvider creates and releases an instance of each controller for each action. The only way to avoid this is to disable security trimming.
With security trimming enabled, it is not recommended to have any heavy processing within your constructors, as this will negatively affect performance. You should defer any processing (such as opening database connections) to later on in the request lifecycle by using an Abstract Factory to create the connection and injecting the factory into your constructor instead of the dbcontext/connection object. See this post for an example.
That said, the AuthorizeAttributeAclModule is not as efficient as it could be because when you have controllers with a lot of action methods, the same controller instance could be reused instead of creating one for each action method. You could make a custom AuthorizeAttributeAclModule and use the HttpContext.Items dictionary to request-cache each controller so they are reused instead of re-instatiated. You will need to do some refactoring to do it, though. The controller is created on line 235 and it is released on line 108. You need to make sure that the release isn't called until the after very last node is checked. You can do this by creating an IActionFilter to release them after the action is complete, but it means that action method will need to know about the same request cache (in HttpContext.Items) as the AuthorizeAttributeAclModule. You just need to implement the OnActionExecuted method to clean up the controllers from the request cache.

MVC 3/Design Patterns issue

I made a settings page for my website. On this page the user is presented with a bunch of site wide settings they can manipulate. I made it so when the user selects a setting the page will automatically run an ajax request to send the setting to the database. My question is in how I do this.
At first I just did calls to the repository. One call to get the data back, put it into a ViewModel then give that ViewModel to the View and the ajax controller just sent the settings back to the database. This way seemed like the best at first especially for unit testing purposes since I could just pass in a fake repository if needed. Then for the user to get a setting they just called the repository and pass in the setting name they want.
Then I had a bright idea. I made a singleton class called SiteWideSettings and each possible setting on the site was a property of the site. When SiteSettings is called for the first time all of the settings are loaded. When Set is called on any of the properties it will call the repository function to send the setting. Now with my Settings view I'm just passing in SiteWideViewOptions.Current and on the ajax call I'm updating the property that was changed. This is working for me however it's not very unit testable since I can't really pass in a repository to a singleton's constructor since its constructor is private. What I currently have is working fine but I just don't feel like it's the best solution and unit testing isn't really possible here.
I'm thinking of one of the following but not sure which is the best.
Add a Repository property to the SiteWideSettings class
Add a function to the SiteWideSettings class to pass in a repository
Not use a singleton for this at all and just go back to what I was doing before I had this idea
Any comment on this would be greatly appreciated.
Note: I know. I know I'm doing unit testing wrong in this case because I didn't write my test first so please don't scold me for that.. I have already scolded myself and with my next task I won't do it again I promise :)
"Then I had a bright idea. I made a singleton class called
SiteWideSettings and..."
This sounds like a bad idea. Let your database be ground-truth for what the settings are, not some in-memory cache that you now need to keep up to date. Let your ORM do caching if you need it for performance otherwise you are just adding problems especially if you now try to run your site on more than one server.
If you want to simplify the controller so it has less 'set-up' and 'tear-down' code in it, use an IOC (e.g. Autofac) and inject any dependencies you need (e.g. a DataContext or a Repository) on a per-http-request basis.
Your action methods are now easier to test since you can simply instantiate your controller (injecting the dependencies manually using its constructor) and then call your method.

ASP.NET MVC 3, RavenDB, & Autofac Issue Plus 2 Other Autofac Questions

NOTE: There are 3 questions in here and I did not make separate questions since they are all somewhat related to the same code.
I have the following code that registers the connection to my RavenDB in the Application_Start once per the application's life cycle:
var store = new DocumentStore { Url = "http://localhost:8080" };
store.Initialize();
builder.RegisterInstance(store).SingleInstance();
Now this works fine and this is something that should be created only once per the application's life cycle. Now I wanted to add in the DocumentSession to Autofac so I tried to add in this in the Application_Start:
var session = store.OpenSession();
builder.RegisterInstance(session).SingleInstance();
In my UserRepository I have the following constructor:
public UserRepository(DocumentStore store, DocumentSession session)
When I try to run this, I get the follow runtime error:
Cannot resolve parameter 'Raven.Client.Document.DocumentSession Session' of constructor 'Void .ctor(Raven.Client.Document.DocumentStore, Raven.Client.Document.DocumentSession)'
That error to me sounds like Autofac does not think it has a DocumentSession however that is what store.OpenSession() returns so it should. Anyone know what would be causing this error? Am I not setting the session variable correctly (it is the same as the store variable which works fine)?
Another thing which may or may not be related to the above issue is how do I add an instance of an object to Autofac per request instead of per the applications life cycle? While the RavenDB DocumentStore object should only be created once be the life application cycle, the DocumentSession should be created once per the request (maybe creating it per application level is causing the error above).
One last question I will throw there about Autofac (mildly related to the code above) is about releasing the objects. If you take a look at this tutorial:
http://codeofrob.com/archive/2010/09/29/ravendb-image-gallery-project-iii-the-application-lifecycle.aspx
The last piece of code:
ObjectFactory.ReleaseAndDisposeAllHttpScopedObjects();
and the point of this code is to prevent leaking the sessions. Now is this something I also need to worry about for Autofac and if so, how would I do this in Autofac?
I'm guessing you want something like:
builder.Register(c => c.Resolve<DocumentStore>().OpenSession()).InstancePerLifetimeScope();
"The default ASP.NET and WCF integrations are set up so that InstancePerLifetimeScope() will attach a component to the current web request or service method call." - Autofac: InstanceScope
Basically, in a web app, InstancePerLifetimeScope handles the one per HTTP context aspect, and also disposes any types that implement IDisposable.
There was also the issue that OpenSession returns a IDocumentSession instead of a DocumentSession. Changing my class to look for a IDocumentSession along with doing what Jim suggested worked, thanks.

Resources