c# - Resolver automáticamente la interfaz<T> a la implementación<T> en StructureMap(diferencie solo por el tipo genérico T)
asp.net generics (5)
Tengo una interfaz ( IRepository<T>
) que se está extendiendo actualmente para cada repositorio específico, es decir: IUserRepository : IRepository<User>
.
Cada una de estas interfaces tiene clases concretas correspondientes, es decir: UserRepository : Repository<User>, IUserRepository
.
Estos repositorios individuales no agregan ninguna funcionalidad adicional, son todas interfaces / clases vacías que se usan simplemente para pasar los genéricos.
Utilizo StructureMap para resolver IUserRepository
en UserRepository
usando un Registro con un escáner de ensamblaje y algunas convenciones de nomenclatura.
Me gustaría que esto se mueva a un estado más optimizado, donde en lugar de pasar las instancias de IUserRepository
y resolverlo en UserRepository
, puedo pasar a IRepository<User>
y resolverlo en Repository<User>
.
Esto eliminaría la necesidad de crear estas interfaces y clases extra vacías.
No puedo encontrar una manera de usar la configuración de StructureMap para configurar este mapeo genérico. Algo como esto:
For(typeof(IRepository<>).Use(typeof(Repository<>)).WithTheGenericTypeFromTheInterfaceSuppliedAsATypeParameter();
Editar
Después de obtener el primer par de respuestas, quiero aclarar esto un poco más.
No quiero crear clases individuales para el bit For
de la configuración. Quiero tener las siguientes clases / interfaces en mi código:
-
IRepository<T> where T : Entity
-
Repository<T> : IRepository<T> where T : Entity
-
Person : Entity
-
Product : Entity
-
Order : Entity
-
Whatever : Entity
Y tener las siguientes asignaciones logradas con la convención:
IRepository<Person> => Repository<Person>
IRepository<Product> => Repository<Product>
IRepository<Order> => Repository<Order>
IRepository<Whatever> => Repository<Whatever>
Pero no quiero tener que crear un mapeo para cada uno, ala:
For<IRepository<Person>>().Use<Repository<Person>>();
For<IRepository<Product>>().Use<Repository<Product>>();
For<IRepository<Order>>().Use<Repository<Order>>();
For<IRepository<Whatever>>().Use<Repository<Whatever>>();
Quiero una asignación única que funcione para cualquier IRepository:
For<IRepository<>>().Use<Repository<>>().WithTheSameGenericType();
Luego usaría esto para inyectar los repositorios en los servicios:
public MyService(IRepository<User> userRepository)
Y espere que se resuelva en un Repository<User>
en tiempo de ejecución.
Aquí hay un ejemplo. Mapa de estructura le permitirá hacer ambas cosas también.
//IRepository
For<IMemberRepository>().Add<MemberRepository>();
//IRepository<T>
For<IRepository<Member>>().Add<MemberRepository>();
Entonces es útil solicitar los tipos simplemente al conocer el tipo genérico en el tiempo de ejecución:
Type repositoryType = typeof(IRepository<>).MakeGenericType(modelType);
IocResolve.Resolve(repositoryType);
Echa un vistazo a la interfaz IRegistrationConvention.
public class DomainRegistry : Registry
{
public DomainRegistry()
: base()
{
Scan(y =>
{
y.AssemblyContainingType<IRepository>();
y.Assembly(Assembly.GetExecutingAssembly().FullName);
y.With(new RepositoryTypeScanner());
});
Donde RepositoryTypeScanner:
public class RepositoryTypeScanner : IRegistrationConvention
{
public void Process(Type type, Registry registry)
{
...
Le sugiero que eche un vistazo al método AddAllTypesOf
. Tuve un código similar y logré mis objetivos al usarlo (y mantuve la función de registro automático en funcionamiento).
En tu caso, solo debes cambiar
For<IRepository<Person>>().Use<Repository<Person>>();
For<IRepository<Product>>().Use<Repository<Product>>();
For<IRepository<Order>>().Use<Repository<Order>>();
For<IRepository<Whatever>>().Use<Repository<Whatever>>();
a
AddAllTypesOf(typeof(IRepository<>));
Al final, su contenedor será similar a:
return new Container(x =>
{
x.Scan(y =>
{
y.TheCallingAssembly();
y.AddAllTypesOf(typeof(IRepository<>));
y.WithDefaultConventions();
});
});
Resulta que no hay un método sofisticado al que llamar, o que no hay un cableado elegante que hacer, simplemente utiliza For
y Use
(las versiones no genéricas):
public class DataRegistry : Registry
{
public DataRegistry()
{
For(typeof (IRepository<>)).Use(typeof(Repository<>));
}
}
Cuando IRepository<Person>
un IRepository<Person>
, se está resolviendo como un Repository<Person>
ahora.
Encontré el error 104 diciendo que el repositorio no era conectable para IRepository. Esto fue porque el repositorio estaba marcado como abstract
. Haciendo que no sea abstracto se corrigió ese error y está funcionando como se desea.