2012-07-31 57 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