2012-07-31 36 views
1

我已经实现了一个通用资源库,但有一个要求是在运行时动态地创建适当的资源库,并将实体类型设置为字符串。如何投射到MyInterface <TEntity>

我已成功地创建使用存储库:

 Assembly common = Assembly.LoadFrom(@"CommonLibrary.dll"); 
     Type entityType = common.GetType("Models.OneOfManyEntities"); 
     Type repoType = typeof(TradeSiftRepository<>).MakeGenericType(entityType); 

     var repo = Activator.CreateInstance(repoType, new UnitOfWork()); 

然而repo是一个对象,我真的想将它转换为IRepository<TEntity>我已经试过IRepository<entityType>但这是不正确的

这是接口用于回购:

public interface IRepository<TEntity> : IDisposable where TEntity : class 
{ 
    IQueryable<TEntity> FindAll(); 
    IQueryable<TEntity> Find(Expression<Func<TEntity, bool>> filter = null, Func<IQueryable<TEntity>, IOrderedQueryable<TEntity>> orderBy = null, string includeProperties = ""); 
    TEntity FindById(object Id); 
    void Insert(TEntity entity); 
    void Update(TEntity entity); 
    void Delete(object id); 
    void Delete(TEntity entity); 
} 

回购的活化实例是正确的类型,但甲ctivator返回一个对象类型,所以我需要将它转换为使用方法。

+0

你什么类型,如果你使用' repo.GetType()'? – Xharze 2012-07-31 11:12:38

+0

回购的创建实例是正确的类型,但我必须将其转换为使用方法,例如repo.FindById(1) – HadleyHope 2012-07-31 11:15:30

回答

0

我不认为你在问什么是可能的。如果您的代码正在接收表示泛型类型参数的字符串,则它所表示的类型只能在运行时确定。但是,泛型类型参数应该在编译时指定。

我认为你必须接受,而不是实体类型为泛型类型参数,如果你想用它作为返回类型的类型参数:

public IRepository<TEntity> MakeRepository<TEntity>() where TEntity : class 
{ 
    var repo = new TradeSiftRepository<TEntity>(new UnitOfWork()); 
    return repo; 
} 

IRepository<User> userRepository = MakeRepository<User>(); 

如果你一定要接受实体类型的字符串名字,结果铸在编译时的实际类型的责任应该通过调用代码来完成,因为调用代码是什么指定类型:

public object MakeRepository(string entityTypeName) 
{ 
    Assembly common = Assembly.LoadFrom(@"CommonLibrary.dll"); 
    Type entityType = common.GetType(entityTypeName); 
    Type repoType = typeof(TradeSiftRepository<>).MakeGenericType(entityType); 

    var repo = Activator.CreateInstance(repoType, new UnitOfWork()); 
    return repo; 
} 

IRepository<User> userRepository = (IRepository<User>)MakeRepository("User"); 

然而,上面的代码可以通过使用我的通用版本来简化的ThOD;减少重复。如果调用代码也不知道它要求的类型,我认为你没有选择。

一种可能的解决办法是返回动态类型,这会使调用代码假设它有它的作者想要的类型的对象:

public dynamic MakeRepository(string entityTypeName) 
{ 
    Assembly common = Assembly.LoadFrom(@"CommonLibrary.dll"); 
    Type entityType = common.GetType(entityTypeName); 
    Type repoType = typeof(TradeSiftRepository<>).MakeGenericType(entityType); 

    var repo = Activator.CreateInstance(repoType, new UnitOfWork()); 
    return repo; 
} 

dynamic userRepository = MakeRepository("ConsoleApplication1.User"); 
User user = userRepository.FindById(1); 
+1

我认为这个需求的确有意义;我自己遇到过这种情况 - 每次以某种方式处理列表/图表时,实体类型仅在稍后确定。唯一不能解决的部分是需要知道实体类型的代码和不需要的代码之间的分离。看到我的解决方案#2将这两段代码之间的界限移到了更有意义的地方。 – 2012-07-31 12:03:04

+0

我刚刚重读了这个问题,我注意到将结果转换为正确的类型不是要求的一部分,所以我认为你是对的。谢谢。 – Sam 2012-07-31 12:11:50

+0

谢谢,是的,“动态”是这样做的,我不需要将存储库转换为正确的类型,并且每个存储库返回的实体都符合相同的接口。 – HadleyHope 2012-07-31 12:58:12

1

你说你要的对象转换为IRepository<TEntity>,但(你在entityType变量的Type对象的引用)TEntity仅在运行时可用。

如果IRepository<entityType>是可能的,你会期望从诸如FindById等方法中获得什么?我猜想可能是一个entityType实例?现在,因为entityType可以是任何类型,并且仅在运行时确定,所以编译器不可能知道如何编译涉及entityType变量的任何代码。因此,IRepository<entityType>是不可能的。

有两种可能的解决方案,这取决于你想要达到什么目的:

  1. 如果您不想运行需要了解你的实体对象的任何代码,实现额外的IRepository样的界面,处理object类型的实体(并且如果添加了与当前entityType不匹配的任何内容,则可能抛出ArgumentException)。将您的Activator.CreateInstance调用的结果投射到该新的接口类型。
  2. 创建一个新的泛型类,将实体类型作为泛型参数。将处理实体和存储库的所有代码移到该新类中,并始终使用提供的实体类型作为通用参数。通过Activator.CreateInstance实例化该泛型类,与您现在直接使用TradeSiftRepository<T>类的过程类似。
+0

是的我期望回到一个entityType实例。在这种特殊情况下,所有实体都将实现特定的接口。 – HadleyHope 2012-07-31 11:22:23

+0

@HadleyHope:编译器不可能知道,因为'entityType'可以是任何东西。但是,您可以使用您的实体接口类型而不是'object'来实现我的解决方案#1。 (当然,解决方案#2的工作方式如上所述,您可以将您的实体接口作为通用约束提供。) – 2012-07-31 11:23:31