I am building an asp.net MVC2 web app using StructureMap. I have created a custom MembershipProvider. I get it properly initialized at startup by calling:
x.For<MembershipProvider>().Use(Membership.Provider);
x.For<IMembershipProvider>().Use<CPOPMembershipProvider>();
In Initialize(), I am thinking of instantiating a repository to use for access to User data in the database.
So, I put in a private property in my custom MembershipProvider:
private IUserRepository userRepository;
And, inside Initialize(), I call:
IUserRepository userRepository = ObjectFactory.GetInstance<IUserRepository>();
First, is it "good practice" to instantiate a repository in my custom MembershipProvider?
Second, when I implement it, it seems like I can't access any StructureMap configuration that was properly setup in global.asax. When I call Debug.WriteLine(ObjectFactory.WhatDoIHave()) just before the above GetInstance() line, none of the configuration data is present (which I do see from the same Debug line placed in global.asax) and I get a "No Default Instance defined for PluginFamily CPOP.Domain.Contract开发者_JAVA百科s.Repositories.IUserRepository" error when calling GetInstance(). Why is that? Seems like I have a whole different container inside MembershipProvider.
Ended up using setter injection in StructureMap. The userRepository property in the custom MembershipProvider can be configured by calling:
CPOPMembershipProvider member =
(CPOPMembershipProvider) Membership.Providers["CPOPMembershipProvider"];
ObjectFactory.Configure(x =>
{
...
x.For<MembershipProvider>().Use(Membership.Provider);
x.For<IMembershipProvider>().TheDefault.Is.Object(member);
...
x.For<IPatientRepository>().Use<PatientRepository>();
x.For<IUserRepository>().Use<UserRepository>();
x.SetAllProperties(y => { y.OfType<IUserRepository>(); });
...
});
And, then calling BuildUp() on a target instance. I do this in Application_Start(), after all the registries have been called and StructureMap has all the configuration data.
ObjectFactory.BuildUp(Membership.Providers["CPOPMembershipProvider"]);
Done.
Thank you very much for answering your question...it was exactly what I was looking for.
Modified slightly for me, the trick was configuring everything via ObjectFactory (just as above) and using that ObjectFactory's container for DependencyResolver:
UserMembershipProvider provider =
(UserMembershipProvider)Membership.Providers["UserMembershipProvider"];
ObjectFactory.Configure(x =>
{
x.For<ISessionFactory>()
.Singleton()
.Use(() => NHibernateInitializer.Initialize().BuildSessionFactory());
x.For<IEntityDuplicateChecker>().Use<EntityDuplicateChecker>();
x.For(typeof(IRepository<>)).Use(typeof(Repository<>));
x.For(typeof(IRepositoryWithTypedId<,>)).Use(typeof(RepositoryWithTypedId<,>));
x.SetAllProperties(y =>
{
y.WithAnyTypeFromNamespaceContainingType<IEntityDuplicateChecker>();
});
});
ObjectFactory.BuildUp(provider);
DependencyResolver.SetResolver(
new StructureMapDependencyResolver(ObjectFactory.Container));
This way, everything works from a Controller factory standpoint, and the MembershipProvider is "built up" after the fact (since it's already been constructed before Application_Start thanks to .NET.) This code above is called from Application_Start.
The membership provider is basically singleton which means only single instance will be created by ASP.NET. So don't keep anything in private fields/properties unless you want some surprises in production when two threads are using the same membership provider same time.
I only needed to implement ValidateUser since we don't use any other methods and I did it like this:
public bool ValidateUser(string userName, string password)
{
var session = DependencyResolver.Curret.GetService<ISession>();
using (session)
{
using (var tx = session.BeginTransaction())
{
// load the user from db and validate the password etc.
tx.Commit();
}
}
}
Of course you need to configure the DependencyResolver, StructureMap etc. but the point is that the ISession is not kept in private field/property.
精彩评论