c# - NInject con interfaz genérica
generics (4)
He definido una interfaz y una clase:
public interface IRepository<T>
{
}
public class RoleRepository:IRepository<Domain_RoleInfo>
{
}
Inyectar aquí:
public RoleService
{
[Inject]
public RoleService(IRepository<Domain_RoleInfo> rep)
{
_roleRep=rep;
}
}
¿Cómo puedo realizar Dependency Injection With Ninject, decir cómo vincular?
He escrito una clase auxiliar como la que se muestra a continuación, funciona bien con una interfaz no genérica. ¿Pero cómo refactorizarla es compatible con la interfaz genérica como se indicó anteriormente?
public class RegisterNinjectModule : NinjectModule
{
public override void Load()
{
BindServices();
BindRepositories();
}
private void BindServices()
{
FindAndBindInterfaces("RealMVC.Service.Interfaces", "RealMVC.Services");
}
private void BindRepositories()
{
FindAndBindInterfaces("RealMVC.Repository.Interfaces", "RealMVC.Repositories");
}
private void FindAndBindInterfaces(string interfaceAssemblyName, string implAssemblyName)
{
//Get all interfaces
List<Type> interfaces = Assembly.Load(interfaceAssemblyName).GetTypes().AsQueryable().Where(x => x.IsInterface).ToList();
IQueryable<Type> ts = Assembly.Load(implAssemblyName).GetTypes().AsQueryable().Where(x => x.IsClass);
foreach (Type intf in interfaces)
{
Type t = ts.Where(x => x.GetInterface(intf.Name) != null).FirstOrDefault();
if (t != null)
{
Bind(intf).To(t).InSingletonScope();
}
}
}
}
Esto debería ayudar a lograr lo que estás pidiendo.
Primero, definamos dos clases ( InterfaceTypeDefinition
y BindingDefinition
).
InterfaceTypeDefinition
contiene información sobre un tipo concreto y sus interfaces. El método IsOpenGeneric
se define en la clase TypeExtensions
.
public class InterfaceTypeDefinition
{
public InterfaceTypeDefinition(Type type)
{
Implementation = type;
Interfaces = type.GetInterfaces();
}
/// <summary>
/// The concrete implementation.
/// </summary>
public Type Implementation { get; private set; }
/// <summary>
/// The interfaces implemented by the implementation.
/// </summary>
public IEnumerable<Type> Interfaces { get; private set; }
/// <summary>
/// Returns a value indicating whether the implementation
/// implements the specified open generic type.
/// </summary>
public bool ImplementsOpenGenericTypeOf(Type openGenericType)
{
return Interfaces.Any(i => i.IsOpenGeneric(openGenericType));
}
/// <summary>
/// Returns the service type for the concrete implementation.
/// </summary>
public Type GetService(Type openGenericType)
{
return Interfaces.First(i => i.IsOpenGeneric(openGenericType))
.GetGenericArguments()
.Select(arguments => openGenericType.MakeGenericType(arguments))
.First();
}
}
BindingDefinition
contiene información sobre la vinculación entre un servicio y una implementación concreta.
public class BindingDefinition
{
public BindingDefinition(
InterfaceTypeDefinition definition, Type openGenericType)
{
Implementation = definition.Implementation;
Service = definition.GetService(openGenericType);
}
public Type Implementation { get; private set; }
public Type Service { get; private set; }
}
Segundo, implementemos un método de extensión que recupere la información necesaria.
public static class TypeExtensions
{
public static IEnumerable<BindingDefinition> GetBindingDefinitionOf(
this IEnumerable<Type> types, Type openGenericType)
{
return types.Select(type => new InterfaceTypeDefinition(type))
.Where(d => d.ImplementsOpenGenericTypeOf(openGenericType))
.Select(d => new BindingDefinition(d, openGenericType));
}
public static bool IsOpenGeneric(this Type type, Type openGenericType)
{
return type.IsGenericType
&& type.GetGenericTypeDefinition().IsAssignableFrom(openGenericType);
}
}
Estas clases ahora se pueden usar para inicializar los enlaces en el módulo.
public class RepositoryModule : NinjectModule
{
public override void Load()
{
var definitions = Assembly.GetExecutingAssembly().GetTypes()
.GetBindingDefinitionOf(typeof(IRepository<>));
foreach (var definition in definitions)
{
Bind(definition.Service).To(definition.Implementation);
}
}
}
Esto debería funcionar:-
Bind(typeof(IRepository<>)).To(typeof(Repository<>));
dónde:-
IRepository <> es una interfaz de la forma: -
public interface IRepository<T> where T : class
{
//...
}
Repository <> es una clase de la forma: -
public class Repository<T> : IRepository<T> where T : class
{
//...
}
Espero que esto ayude :-)
Si importa la extensión de convenciones Ninject, su GenericBindingGenerator
debería poder ayudarlo. Agrega soporte para interfaces genéricas.
Solo una pregunta sobre su método FindAndBindInterfaces : dentro del foreach ¿no tiene un problema de "cierre" en la variable intf ? Todavía no estoy seguro de haber entendido cómo funciona el problema del cierre.
De todos modos, solo para estar seguro, creo que deberías cambiar tu foreach a algo así como:
foreach (Type intf in interfaces)
{
var tmp = intf;
Type t = ts.Where(x => x.GetInterface(tmp.Name) != null).FirstOrDefault();
if (t != null)
{
Bind(intf).To(t).InSingletonScope();
}
}