Плагины MEF и EF CodeFirst - как? - PullRequest
17 голосов
/ 04 января 2012

Справочная информация:
У нас есть проект со многими модулями.Мы используем EntityFramework 4.2 с FluentAPI (CodeFirst).

Существует центральный проект с именем Diverto.ORM.EntityFramework.SQLServer, который содержит частичные классы, которые создают контекст с использованием FluentAPI (и который имеет ссылки на все остальные).проект на решение).

Недавно мы получили запрос от клиента на реализацию многих других функций, и для решения потребуется несколько других проектов.Некоторые из этих проектов придут из другой системы (Human Resources), а некоторые будут созданы.Ядром существующего решения является финансовая система.

Мы хотим включать и отключать эти новые проекты (и графический интерфейс, бизнес-логику и все остальное) «на лету» с помощью MEF.Они будут взаимодействовать как плагины, а главное меню приложения будет заполняться также с использованием MEF.
Однако мы не имеем понятия о том, как включить / отключить эти модули / проекты (новые и HR) из-заданные, которыми они должны поделиться.

Учтите это:
- DivertoContext (основной контекст) с DbSet и DbSet .
- PluginContext (из плагина) с DbSet .

Теперь учтите, что внутри GUI у меня должен быть доступ к данным из ClassA, ClassB и ClassC (если плагин есть).

Решение найдено!См. Ниже

ЭЙ, ВЫ ТАК, ПРОЧИТАЙТЕ ЭТО ДО ОТВЕТА!

Я заметил, что некоторые люди проверяют это и отмечают это как избранное или голосование.Пожалуйста, имейте в виду, что этот ответ относится к 2012 году, и EntityFramework изменился много с тех пор.

Также, пожалуйста, пожалуйста , ПОЖАЛУЙСТА , помните, что у каждого проекта есть свои потребности.Мне нужна была эта функция, таким образом, в то время.Вашему проекту это может вообще не понадобиться, или просто некоторые его части!

Наконец, просто чтобы убедиться, что все скрыто, да, возможно сделать это с EF 6.1 и EF Migrations, и это можетвозможно также с другими ORM и инфраструктурой миграции.

Вам могут потребоваться некоторые другие интерфейсы, например, для загрузки, для миграции и для правильной обработки миграции определенных плагинов (не смешивайте его с другими плагинами, поэтому попробуйтереализовать какой-то уникальный токен для каждого плагина).

1 Ответ

28 голосов
/ 18 января 2012

Решение найдено!

Хорошо, я попытаюсь объяснить здесь, так как я не мог найти это в другом месте.Это интересно для людей, которым необходимо создать одно базовое программное обеспечение, которое получит несколько плагинов, и эти плагины должны взаимодействовать с существующими данными в одной базе данных.

Прежде всего, я буду работать с Entity Framework сCodeFirst API и все.Поэтому, если вы собираетесь это сделать, я рекомендую прочитать EntityTypeConfiguration из MSDN и Code First Fluent API также из MSDN.

Теперь давайте разберемся с некоторыми вещами:

  • У вас должен быть только один контекст, чтобы все работало правильно.Я пойду в это и покажу способ иметь классы из плагинов внутри контекста из приложения, но для того, чтобы это работало, вы ДОЛЖНЫ понимать шаблон общего репозитория.Здесь я покажу лишь немного, но я рекомендую вам тщательно изучить этот вопрос и попытаться создать лучший интерфейс для вашего приложения.
  • MEF будет нашим другом здесь.Я считаю, что вы уже знаете, как создать простой плагин с MEF и как получить доступ к методу внутри этого плагина.Кроме того, я постараюсь не углубляться в MEF, потому что это не так, и потому что вы можете использовать другое решение.На самом деле, я использую MEF только потому, что я как-то уже знаком с ним.
  • Если вы собираетесь "О, мне нужно обработать несколько контекстов, которые будут указывать на одну базу данных.и все "ты делаешь это неправильно.Все дело в простых конфигах и просто в беглом API.Поверьте мне: я искал в интернете неделю и, наконец, после разговора с другом мы нашли это решение, и оно действительно простое =).


Перво-наперво

Решение: MEFTest
Проекты:

  • Base.ORM (который будет содержать интерфейсы для ORM)
  • Base.ORM.EntityFramework.SQLServer (будет содержать базовые классы для EF)
  • SampleApplication.ORM.EntityFramework.SQLServer (будет содержать контекст для приложения)
  • SampleApplication (исполняемый файл)
  • MEFPlugin (будет содержать наш плагин)

Теперь кодировка

Внутри проекта Base.ORM создайте свой интерфейс универсального репозитория с методами, которые вы считаете нужными, но интерфейс не напечатан.Это будет похоже на это:

public interface IRepository
{
   bool Add<T>(T item);
}

С этого момента я буду просто называть его IRepository для простоты.
Я рассмотрю один метод под названием Add (T item) дляПример кодирования.

Внутри Base.ORM.EntityFramework.SQLServer создайте класс BaseContext, который наследуется от DbContext и который реализует IRepository.Он должен выглядеть следующим образом:

public class BaseContext : IRepository
{
   public bool Add<T>(T item)
   {
      try { Set<T>().Add(item); return true; }
      catch { return false; }
   }
}

Вы можете добавить собственную базовую реализацию IDatabaseInitializer здесь для управления версиями базы данных.Я сделал это с помощью файлов SQL, находящихся в стандартной папке, но это старая кодировка, поскольку EF теперь поддерживает миграции.

Если вам все еще придется обрабатывать это вручную, не забудьте установить базу данных в однопользовательский режим ДО и вернуться к многопользовательскому режиму ПОСЛЕ.Помните: попробуйте ... поймать ..., наконец, здесь помогут, потому что вы можете вернуться к многопользовательскому внутри, наконец, так что даже при ошибке не останется никаких проблем.

Внутри проекта SampleApplication добавьте:
ClassA (int Id, имя строки) и ClassB (int Id, DateTime TestDate).

Внутри SampleApplication.ORM.EntityFramework.SQLServer создайте свой стандартный контекст.
Я буду использовать три класса здесьс очень интересными именами: ClassA, ClassB и ClassC.
На ClassA и ClassB ссылаются из этого проекта, и это будет выглядеть так:

public class Context : BaseContext
{
   public DbSet<ClassA> ClassA { get; set; }
   public DbSet<ClassB> ClassB { get; set; }

   protected override void OnModelCreating(DbModelBuilder modelBuilder)
   {
      /* I'll talk about this later. Just override the OnModelCreating and leave it */
      base.OnModelCreating(modelBuilder);
   }
}

Теперь забавная часть: у плагина будет методвот так:

public void Setup(DbModelBuilder modelBuilder)
{
   modelBuilder.Entity<ClassC>().ToTable("ClassC");
   modelBuilder.Entity<ClassC>().HasKey(_classC => _classC.Id);
   modelBuilder.Entity<ClassC>().Property(_classC => _classC.Date2).HasColumnType("datetime2").HasPrecision(7);
}

Конечно, ClassC находится внутри проекта плагина.У вас нет ссылок на него из основного проекта.
Вам нужно будет найти этот метод (Setup), используя MEF и, конечно, интерфейсы.Я просто покажу, ГДЕ разместить это и как заставить это работать =)

Возвращаясь к классу Context, метод OnModelCreating будет выглядеть следующим образом:

protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
   modelBuilder.Entity<ClassA>().ToTable("ClassA");
   modelBuilder.Entity<ClassA>().HasKey(_classA => _classA.Id);

   modelBuilder.Entity<ClassB>().ToTable("ClassB");
   modelBuilder.Entity<ClassB>().HasKey(_classB => _classB.Id);
   modelBuilder.Entity<ClassB>().Property(_classB => _classB.TestDate).HasColumnType("datetime2").HasPrecision(7);

   /* Use MEF to load all plugins. I'll use the mock interface IPlugin */
   foreach (IPlugin plugin in MefLoadedPlugins)
      plugin.Setup(modelBuilder);
}

Использование

Внутри вашего приложения вы будете иметь только один контекст. Этот контекст наследуется от BaseContext, который реализует IRepository. Имея это в виду, вам нужно кодировать свой GUI и бизнес-уровень для использования IRepository (из Base.ORM) и определенного класса (внутри dll, специфичного для бизнеса).

Работает!

Ну, это работает здесь.

Я думаю, что я показал все соответствующие части здесь.
Конечно, внутри классов больше кода, но это не так. Я пытаюсь показать только то, что вам действительно нужно создать / реализовать, чтобы сделать это.

Не забудьте:

  1. Не забывайте, что вам придется написать собственный код для заполнения базы данных. Для моего случая здесь, внутри того же интерфейса для плагина, у меня есть что-то вроде Seed (IRepository), и я просто обрабатываю все там.
  2. Не забывайте, что у вас нет ссылок из основного проекта на плагины. Это означает, что вы ДОЛЖНЫ найти способ загрузки меню, графического интерфейса пользователя, бизнеса и данных через интерфейсы и поставщиков. Мне удалось решить это, используя что-то вроде IPlugin (бизнес), IFormPlugin (GUI - Winforms) и IPluginRepository (данные). Вам нужно будет найти свои собственные имена и методы, которые могут соответствовать вашим потребностям, но это должно стать хорошей отправной точкой.
  3. Не забывайте, что если вы загружаете плагин, вы должны создать таблицы внутри базы данных, иначе EF CodeFirst не сможет инициализироваться. Помните, что вам могут понадобиться файлы SQL и запускать их вручную для создания таблиц по мере необходимости.
  4. Не забывайте, что если вы выгружаете плагин, вы тоже должны удалять таблицы, иначе EF тоже потерпит неудачу.
  5. Не забывайте, что вам действительно нужны резервные копии. Я еще этого не делал, но уже отметил, где это будет сделано (до и после команд DDL).
  6. Это МОЕ решение для МОЕГО случая. Это должно быть хорошим началом для новых проектов, но только это. Не думайте, что следуя тому, что я здесь сделал, это будет работать на 100% для каждого случая.

Спасибо

Спасибо людям из SO и из MSDN, которые очень помогли мне с комментариями и другими постами, которые я нашел.
Спасибо Caio Garcia (BR), который помог мне с некоторыми инструкциями о других системах на основе плагинов.

Примеры кодов (полностью)

Вот некоторые примеры кодов:

ДЛЯ ОСНОВНЫХ ПУНКТОВ (предопределенные пункты решения)

public class ClassA
{
   public int Id { get; set; }
   public string Name { get; set; }
}

public class ClassB
{
   public int Id { get; set; }
   public string OtherName { get; set; }
}

public interface IRepository
{
   bool Add<T>(T item);
   bool Save();
}

public class BaseContext : DbContext, IRepository
{
   public bool Add<T>(T item)
   { 
      try { Set<T>().Add(item); return true; } catch { return false; }
   }
   public bool Save()
   {
      try { SaveChanges(); return true; } catch { return false; }
   }
}

public class Context : BaseContext
{
   // Fill this list using MEF - check for the IPluginContext interface on assemblies
   public List<IPluginContext> MefLoadedPlugins;

   protected override void OnModelCreating(DbModelBuilder modelBuilder)
   {
      modelBuilder.Entity<ClassA>().ToTable("TableB", "Schema_1");
      modelBuilder.Entity<ClassA>().HasKey(_a => _a.Id);

      modelBuilder.Entity<ClassB>().ToTable("TableB", "Schema_1");
      modelBuilder.Entity<ClassB>().HasKey(_b => _b.Id);

      if (MefLoadedPlugins != null)
         foreach (var pluginContext in MefLoadedPlugins)
            pluginContext.Setup(modelBuilder);
   }
}

ДЛЯ ПЛАГИНА

public class ClassC
{
   public int Id { get; set; }
   public string Description { get; set; }
}

public interface IPluginContext
{
   void Setup(DbModelBuilder modelBuilder);
}

public class Just_A_Sample_Plugin_Context : IPluginContext
{
   public void Setup(DbModelBuilder modelBuilder)
   {
      modelBuilder.Entity<ClassC>().ToTable("TableC", "Schema_2");
      modelBuilder.Entity<ClassC>().HasKey(_c => _c.Id);
   }
}

В РЕГУЛЯРНОМ КОДЕ

public void DoSomething(IRepository repo)
{
   var classA = new ClassA() { Name = "First Name" };
   repo.Add(classA);
   repo.Save();
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...