Unity and Auto Factories with Generics
We are trying to implement repository pattern in using Linq2SQl and the data context is initialized lazily.
Repository class implements IRepository
public class Repository<T> : IRepository<T> where T : class
{
private readonly Table<T> _table;
public Repository(IDataContextAdapter dataContextAdapter)
{
_table = dataContextAdapter.Context.GetTable<T>();
}
...
}
Data Access uses delegates to Repository
public class UserDataAccess : IUserDataAccess
{
private readonly IRepository<User> _userRepository;
public UserDataAccess(IDataContextAdapter dataContextAdapter,
Func<IDataContextAdapter, IRepository<User>> userRepository)
{
_userRepository = userRepository(dataContextAdapter);
}
}
I like to know how to define the repositories generically in unity container.
Currently I have the following, but I don't want to repeat it for every concrete repository class like User, Employer etc.
UnityContainer.RegisterType<Func<IDataContextAdapter, IRepository<User>>>(
new InjectionFactory(c =>
new Func<IDataContextAdapter, IRepository<User>>(
context => new Repository<User>(context))
)
);
I am looking for a way to define the types generically, something like
UnityContainer.RegisterType<Func<IDataContextAdapter, IRepository<T>>>(
new InjectionFactory(c =>
new Func<IDataContextAdapter, IRepository<T>>(
context => new Repository<T>(context))
)
);
I have tried applying typeof operation with on '<>', but 开发者_C百科didn't had much success.
.NET does not allow for partially closed types. http://www.lostechies.com/blogs/jimmy_bogard/archive/2009/09/01/partially-closed-generic-types.aspx This is why you can't do what you are attempting to achieve in a generic way. In runtime you either can have open type like Func<,> or closed type like Func<IDataContextAdapter, IRepository<User>>
Simplifying, Unity maintains a dictionary of type mappings where from type is a non-generic type or generic open type or generic closed type. Thus it is impossible to map a type that does not fall into these categories.
If we could come up with a good and fast way to match against Func<IDataContextAdapter, IRepository<T>>
at run time, it would be possible to write an extension to Unity that does the resolution. However it does not seem that it would be possible to do such matching without using reflections, and you normally try to avoid using reflection every time you resolve a type for performance reasons. At worst case you use reflection during the first resolution only to generated IL on the fly that would do the object creation without reflection afterwards. Unfortunately in our case this would be impossible.
The only way to resolve this on Unity level that I see is every time Func<IDataContextAdapter, IRepository<Something>>
is requested is to check if this is already registered and if not register it with generated on the fly factory.
In other words there are no simple ways of doing exactly this.
An alternative I can suggest is do this multiple registrations in a bootstrapper style, but you are already aware of this option.
You also can define a generic factory like this:
public class GenericDataServiceFactory<TResult, TModel>
{
private UnityContainer container;
private Func<IDataContextAdapter, IRepository<TModel>> _repositoryFactory;
public GenericDataServiceFactory(Func<IDataContextAdapter, IRepository<TModel>> repositoryFactory)
{
_repositoryFactory = repositoryFactory;
}
public TResult CreateUserDataAccess(IDataContextAdapter dataContextAdapter)
{
Type targerType = typeof (TResult);
// Via reflection find constructor like this
// TResult(IDataContextAdapter dataContextAdapter, Func<IDataContextAdapter,IRepository<Something>> repositoryFactory)
// Invoke it with dataContextAdapter and _repositoryFactory and return the result
return newObject;
}
}
But then again, you are going through reflection every time, and I'm sure you don't want to do that.
I can't understand what you are trying to achieve, but I make a suggestion as to what is the closes semi-logical thing in my mind is.
First loose your delegate. (Or explain what it's for). Define your UserDataAccess this way:
public class UserDataAccess : IUserDataAccess
{
private readonly IRepository<User> _userRepository;
public UserDataAccess(IRepository<User> userRepository)
{
_userRepository = userRepository;
}
}
Now your registration will look something like this:
DataContextAdapter context = new DataContextAdapter();
UnityContainer unityContainer = new UnityContainer();
unityContainer.RegisterType(typeof (IRepository<>), typeof (Repository<>));
unityContainer.RegisterType<IUserDataAccess, UserDataAccess>();
unityContainer.RegisterInstance<IDataContextAdapter>(context);
And then you resolve like this:
IUserDataAccess test = unityContainer.Resolve<IUserDataAccess>();
UserDataAccess will be created and the right closed type Repoistory<> will be injected into the constructor. In turn, DataContextAdapter will be injected in the constructor of Repoistory<>.
UPDATE 1
Based on the information in the original question, link to that you provided above, is there a reason why the following won't work for you?
unityContainer.RegisterType(typeof (IRepository<>), typeof (Repository<>));
unityContainer.RegisterType<IUserDataAccess, UserDataAccess>();
DataContextAdapter context1 = new DataContextAdapter();
DataContextAdapter context2 = new DataContextAdapter();
IUnityContainer container1 = unityContainer.CreateChildContainer();
IUnityContainer container2 = unityContainer.CreateChildContainer();
container1.RegisterInstance<IDataContextAdapter>(context1);
container2.RegisterInstance<IDataContextAdapter>(context2);
IUserDataAccess test1 = container1.Resolve<IUserDataAccess>();
IUserDataAccess test2 = container2.Resolve<IUserDataAccess>();
I still see no reason for the complexity with delegates and injection factory. You need different data contexts in different code path, so just inject different instances in your container as shown below. You can dispose of a container when you no longer need it.
Is there is anything else from your requirements that left unfulfilled by this solution?
UPDATE 2
In response to yor comment above, I'd like to clarify, that you don't have to register or instantiate contexts upfront with the approach above. You can do it right at the site you are using them:
// When you need context, obtain it
DataContextAdapter context1 = new DataContextAdapter();
//Register it for the local scope
IUnityContainer container1 = unityContainer.CreateChildContainer();
container1.RegisterInstance<IDataContextAdapter>(context1);
// Resolve the dependency
IUserDataAccess test1 = container1.Resolve<IUserDataAccess>();
// Use your interface
......
// Release and dispose your context
container1.Dispose
The main point to take away is that's you who control where and when instantiate them
精彩评论