2013-10-26 5 views
5

Version Info:DbSet.Cast <TEntity>() Ошибка: Не удается создать DbSet <IEntity> из необщего DbSet для объектов типа 'организации'

Я использую C# 4.5, Entity Framework 6.0, и MEF ,

Код и блок испытаний

Я создал проект испытания, чтобы объяснить эту проблему: https://skydrive.live.com/redir?resid=E3C97EC293A34048!2234

Пожалуйста, откройте проект UnitTest и попытаться запустить TestIfItWorks модульного тестирования().

Проблема

Я хочу, чтобы преобразовать необщего DbSet его дженерика, но я получаю следующее исключение: InvalidCastException: Cannot create a DbSet<IUser> from a non-generic DbSet for objects of type 'User':

var nonGeneric = context.Set(typeof(User)); 
var generic = nonGeneric.Cast<IUser>(); //Exception in here 

Класс Пользователь реализующий IUser так что вы бы подумайте, что приведение не должно быть проблемой, если только код DbSet не ограничен конкретными классами (я надеюсь, что в противном случае мне не нужно создавать оболочку вокруг не-общего DbSet, чтобы преобразовать его в общий DbSet или найти альтернативу текущей реализации DbSet) ,

Если вам интересно, почему я использую интерфейсы, даже если они в настоящее время не поддерживаются Microsoft, я даю вам небольшое объяснение (надеюсь, это отфильтровало бы ответы, которые говорят «Do not Do That» вместо предоставления решения) :

Я использую MEF и EntityFramework для создания слабо связанного механизма уровня данных, через который я могу предоставить Entities (и их соответствующие конфигурации) для каждого проекта. Я очень интенсивно использую интерфейсы для определения движка. Метаданные и конкретная реализация объектов в контексте обнаруживаются во время выполнения с использованием MEF.

Отрывок из кода

[TestMethod] 
public void TestIfItWorks() 
{ 
    //TODO: Please open the App.Config and change the PluginsPath to match the Plugins folder in your machine. 

    using (var dbContext = new MyContext()) //Please ignore this line for now. This was UnitOfWork which I replaced with Context to create a simple unit test 
    { 
     dbContext.Setup(); //Please ignore this line for now. This was part of UnitOfWork which I moved to here to create a simple unit test 

     //The purpose of all these is to be able to read and write user info from/to database while User class is defined in an external assembly 
     //but we can import it by MEF using IUser interface. 

     //Failed Attempt# 1: Use User class directly! This doesnt work because User is in an external class which we dont have reference to 
     //var failedAttempt1 = dbContext.Set<User>(); 

     //Failed Attempt# 2: But the good thing is that we have access to IUser and its exports 
     //then lets get a DbSet<IUser> instead 
     var failedAttempt2 = dbContext.Set<IUser>(); 
     try 
     { 
      var throwsException2 = failedAttempt2.FirstOrDefault(); 
     } 
     catch (InvalidOperationException ex) 
     { 
      //InvalidOperationException: 
      // The entity type IUser is not part of the model for the current context. 
      // It also didnt work when I tried to define a class that inherits from EntityTypeConfiguration<IUser>at TestImplementation 
     } 




     //Ok then lets do it differently this time. Lets get User type (that we know we have good configuration for) 
     //from our Container and ask Context to give us the nonGeneric version 
     var userImplementationType = Logic.Instance.GetExportedTypes<IUser>().FirstOrDefault(); 
     Assert.IsNotNull(userImplementationType, "We havn't been able to load TestImplementation into catalog. Please ensure the PluginsPath is set correctly at App.Config"); 
     var nonGeneric = dbContext.Set(userImplementationType); 
     // 
     // This is working so far, we can add and remove records from database using 
     // the nonGeneric version of DbSet. You can uncomment the following code block provide a unique ID 
     // and test it yourself. 
     // 
     var newUser = Logic.Instance.New<IUser>(); 
     newUser.Id = "99"; 
     newUser.UserName = "Aidin Sadighi"; 
     nonGeneric.Add(newUser); 
     try 
     { 
      dbContext.SaveChanges(); 
     } 
     catch (DbUpdateException ex) 
     { 
      //This is OK because most probably this is a duplicate user. Just increase the Id to make it unique. 
     } 



     //Failed Attempt#3: Cast non generic DbSet to generic 
     try 
     { 
      //TODO: I need to fix this. Help me please 
      var genericSet = nonGeneric.Cast<IUser>(); 
     } 
     catch (InvalidCastException ex) 
     { 
      //Cannot create a DbSet<IUser> from a non-generic DbSet for objects of type 'User'. 
      throw; 
     } 
    } 
} 
+0

Как о 'IQueryable родовое = nonGeneric.Cast ();'? – haim770

+0

Я знаю jack о Entity Framework, но, глядя на код, я довольно скептически отношусь к context.Set(). эта работа: Пользователь nonGeneric = context.Set (typeof (Пользователь)); ..если нет, то его нормальное, что приведение тоже не является, и если это так, просто используйте IUser generic = context.Set (typeof (User)); – Robert

+0

Спасибо за ответ, но это не работает для меня. Я редактировал свой вопрос, чтобы предоставить пример кода и более подробную информацию. Спасибо – Aidin

ответ

8

Для этого, я бы на самом деле предлагают с помощью отражения. В конструкторе DbContext, вы можете установить свойство указателя функции:

method = this.GetType().GetMethod("Set", new Type[0]).MakeGenericMethod(typeof(UserImplementation)); 

Вы можете вызвать это с помощью:

method.Invoke(this, new object[0]); 

И это должно возвращать объект типа DbSet<UserImplementation> которого. Включить метод <>().

+0

Спасибо за ответ, но это не работает для меня. Я редактировал свой вопрос, чтобы предоставить пример кода и более подробную информацию. Спасибо – Aidin

+0

Мне очень нравится ваш подход. Это очень гениально. Однако я получаю исключение AmbiguousMatchFound при вызове следующей строки: 'var method = typeof (MyContext) .GetMethod (« Set »). MakeGenericMethod (userImplementationType);' – Aidin

+0

Удивительно, исправил его. Мне пришлось немного изменить этот код, но ваш подход был ключевым. Большое вам спасибо, что вы только что сделали свой день. Правильный код: 'var method = typeof (MyContext) .GetMethod (« Установить », новый тип [0]). MakeGenericMethod (userImplementationType); var genericItem = метод.Invoke (dbContext, новый объект [0]); ' – Aidin

0

Ok я ничего не знаю о рамках Entity, но от глядя на документы

http://msdn.microsoft.com/en-us/library/gg696521%28v=vs.103%29.aspx

DbSet<TEntity> item = DbContext.Set<TEntity>; 

так на самом деле ваш код будет таким же, как это:

DbSet<User> nonGeneric = context.Set<User>(); 

и получить a IUser

DbSet<IUser> nonGeneric = context.Set<User>(); 

или, может быть

var generic = nonGeneric.Cast<DbSet<IUser>>(); 
+0

Спасибо за ответ, но это не работает для меня. Я редактировал свой вопрос, чтобы предоставить пример кода и более подробную информацию. Спасибо – Aidin

1

заменить

nonGeneric.Cast<IUser>(); 

по

Enumerable.Cast<IUser>(nonGeneric); 
+0

Не знаю, почему это было опущено, этот совет помог мне. – spottedone

+0

Я не знаю, почему 'nonGeneric.Cast ();' не работает, но это такое элегантное решение. Благодарю. – ron4ex

Смежные вопросы