Решение найдено!
Хорошо, я попытаюсь объяснить здесь, так как я не мог найти это в другом месте.Это интересно для людей, которым необходимо создать одно базовое программное обеспечение, которое получит несколько плагинов, и эти плагины должны взаимодействовать с существующими данными в одной базе данных.
Прежде всего, я буду работать с 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, специфичного для бизнеса).
Работает!
Ну, это работает здесь.
Я думаю, что я показал все соответствующие части здесь.
Конечно, внутри классов больше кода, но это не так. Я пытаюсь показать только то, что вам действительно нужно создать / реализовать, чтобы сделать это.
Не забудьте:
- Не забывайте, что вам придется написать собственный код для заполнения базы данных. Для моего случая здесь, внутри того же интерфейса для плагина, у меня есть что-то вроде Seed (IRepository), и я просто обрабатываю все там.
- Не забывайте, что у вас нет ссылок из основного проекта на плагины. Это означает, что вы ДОЛЖНЫ найти способ загрузки меню, графического интерфейса пользователя, бизнеса и данных через интерфейсы и поставщиков. Мне удалось решить это, используя что-то вроде IPlugin (бизнес), IFormPlugin (GUI - Winforms) и IPluginRepository (данные). Вам нужно будет найти свои собственные имена и методы, которые могут соответствовать вашим потребностям, но это должно стать хорошей отправной точкой.
- Не забывайте, что если вы загружаете плагин, вы должны создать таблицы внутри базы данных, иначе EF CodeFirst не сможет инициализироваться. Помните, что вам могут понадобиться файлы SQL и запускать их вручную для создания таблиц по мере необходимости.
- Не забывайте, что если вы выгружаете плагин, вы тоже должны удалять таблицы, иначе EF тоже потерпит неудачу.
- Не забывайте, что вам действительно нужны резервные копии. Я еще этого не делал, но уже отметил, где это будет сделано (до и после команд DDL).
- Это МОЕ решение для МОЕГО случая. Это должно быть хорошим началом для новых проектов, но только это. Не думайте, что следуя тому, что я здесь сделал, это будет работать на 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();
}