如何构建实体框架应用程序(使用 MEF)
我非常想知道如何构建我的 Entity Framework 4(代码优先)应用程序。
我有一个 VS 项目来处理对我的数据的访问。它是一个 MEF 导出部件 [MyData]
,基于接口 [IDataExport]
。该项目有我的 EF 类(客户、订单等)、上下文初始值设定项等,所有这些都已经像梦想一样工作。
我有一个 VS 项目,它有我的接口(我的所有接口)。所有项目都有对此接口项目的引用。
我有一个 VS 项目来完成我所有的日志记录。它也是一个 MEF 导出部件 [MyLog]
,基于接口 [ILogging]
。该类实际上只是写入控制台。
我有三个 VS 项目,我们将其称为“零件”(用 MEF 术语)。它们是插件。他们需要数据才能工作(客户、订单等)。实际上,他们同时需要来自三个不同表的数据作为输入。
我有一个项目,即主机应用程序。它当前作为控制台应用程序运行,但很快将转换为 Windows 服务。
我希望这能让您对现有的架构有一个很好的了解。现在我在尝试弄清楚如何正确进行数据访问时遇到了麻烦。
当主机需要将数据传递给插件时,它需要从3个不同的表中获取数据。实际上,按照 EF 的设置方式,将同时检索三个表。当 MEF 实例化插件时,如何将该数据传递给插件?插件可以引发事件来与主机应用程序交互吗?
此外,随着插件的运行,表中的数据需要更新。如何使数据库中的数据保持向上三层更新? Host可以调用Plugin,但是Plugin没有办法调用Host。只有 [MyData]
项目有权访问数据库。
根据我描述的场景,有人可以告诉我如何最好地构建这个应用程序吗?
更让我困惑的是,一些示例代码显示了调用应用程序(在本例中为主机),为对数据库的每次搜索调用启动全新的模型。例如,
public List<Customer> FindCustomerList(string companyName)
{
return new CustomerManager().FindCustomerList(companyName);
}
public List<Customer> FindCustomerList(string companyName)
{
var q = from c in context.Customers
where c.CompanyName.StartsWith(companyName)
select c;
return q.ToList();
}
下面是我的三张桌子。请注意,它们具有外键关系,导致子项目嵌入到主作业记录中。就像一个有很多订单的客户。
public class pcJobAction : IVersionTracking, IpcIdentity
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public long Id { get; set; }
//IpcIdentity
[Required]
[MaxLength(75)]
public string name { get; set; }
[MaxLength(1000)]
public string description { get; set; }
[Required]
[MaxLength(30)]
public string ServerName { get; set; }
[MaxLength(20)]
public string ServerIP { get; set; }
public int JobEnabled { get; set; }
public virtual ICollection<pcPlugInValue> PlugInText { get; set; }
//JobActions holds a list of Schedules
public virtual ICollection<pcJobSchedule> JobSchedules { get; set; }
//FK to the JobTypes table (Delete Files, Verify Backups, Ping, etc)
public long pcJobTypeId { get; set; }
public virtual pcJobType pcJobType { get; set; }
//IVersionTracking
public DateTime DateCreated { get; set; }
public DateTime LastUpdated { get; set; }
[Timestamp]
public byte[] Version { get; set; }
}
public class pcPlugInValue : IVersionTracking, IpcIdentity
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public long Id { get; set; }
//IpcIdentity
[Required]
[MaxLength(75)]
public string name { get; set; }
[MaxLength(1000)]
public string description { get; set; }
public string PlugInText { get; set; }
public int ExecuteOrder { get; set; }
//FK to the JobAction table
public long pcJobActionId { get; set; }
public virtual pcJobAction pcJobAction { get; set; }
//FK to the codes table (to indetify the schedule type: daily, weekly, etc)
public long pcCodeId { get; set; }
public virtual pcCode pcCode { get; set; }
//IVersionTracking
public DateTime DateCreated { get; set; }
public DateTime LastUpdated { get; set; }
[Timestamp]
public byte[] Version { get; set; }
}
public class pcJobSchedule : IVersionTracking, IpcIdentity
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public long Id { get; set; }
//IpcIdentity
[Required]
[MaxLength(75)]
public string name { get; set; }
[MaxLength(1000)]
public string description { get; set; }
//FK to the JobAction table
public long pcJobActionId { get; set; }
public virtual pcJobAction pcJobAction { get; set; }
//FK to the codes table (to indetify the schedule type: daily, weekly, etc)
public long pcCodeId { get; set; }
public virtual pcCode pcCode { get; set; }
public DateTime StartDate { get; set; }
public Boolean dayMonday { get; set; }
public Boolean dayTuesday { get; set; }
public Boolean dayWednesday { get; set; }
public Boolean dayThursday { get; set; }
public Boolean dayFriday { get; set; }
public Boolean daySaturday { get; set; }
public Boolean daySunday { get; set; }
public Boolean ThisJobIsNext { get; set; }
public DateTime EndDate { get; set; }
public int DateOfMonth { get; set; }
public int DayOfWeek { get; set; }
public DateTime ScheduleHour { get; set; }
public int EveryHowMany { get; set; }
public DateTime RunTimeLast { get; set; }
public DateTime RunTimeNext { get; set; }
//IVersionTracking
public DateTime DateCreated { get; set; }
public DateTime LastUpdated { get; set; }
[Timestamp]
public byte[] Version { get; set; }
}
I am desperate to find out how to architect my Entity Framework 4 (code first) application.
I have one VS project that will handle access to my data. Its a MEF-Exported Part [MyData]
, based on an Interface [IDataExport]
. That project has my EF classes (customer, order, etc), the context initializer, etc and all that already works like a dream.
I have one VS project that has my interfaces (all my interfaces). All projects have a reference to this Interface project.
I have one VS project that does all my logging. It is also a MEF-Exported Part [MyLog]
, based on an interface [ILogging]
. That class really just writes to the Console.
I have Three VS projects that we will call Parts (in MEF terms). They are plugins. They need data to work (customers, orders, etc). Actually, they need data as an Input from three different tables, all at once.
I have one project that is the Host application. It is currently running as a console application but will soon be converted to a Windows Service.
I hope that gave you a good idea of the architecture that is in place. Now I am having troubles trying to figure out how to do my data access correctly.
When the host needs data to pass to the plugins, it needs to get data from 3 different tables. Actually, the way it is setup with EF, the three tables will be retrieved at once. How do I pass that data to the plug-in, when the plugin was instantiated by MEF? Can Plug-Ins raise events to interact with the Host application?
In addition, as the plug-ins run, data in the tables will need to be updated. How do I keep my data in the database updated three layers up? The Host can call the Plug-In, but the Plugin doesn't have a way to call the Host. Only the [MyData]
project has access to the Database.
Based on the scenario that I described, could someone please tell me how to best architect this application?
Adding further to my confusion, some sample code shows the calling application (in this case the host), starting brand new Models for each search call to the database. e.g.
public List<Customer> FindCustomerList(string companyName)
{
return new CustomerManager().FindCustomerList(companyName);
}
public List<Customer> FindCustomerList(string companyName)
{
var q = from c in context.Customers
where c.CompanyName.StartsWith(companyName)
select c;
return q.ToList();
}
Below are my three tables. Please note that they have foreign key relationships, resulting in sub-items being embedded inside of the main job record. Like a customer with many orders.
public class pcJobAction : IVersionTracking, IpcIdentity
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public long Id { get; set; }
//IpcIdentity
[Required]
[MaxLength(75)]
public string name { get; set; }
[MaxLength(1000)]
public string description { get; set; }
[Required]
[MaxLength(30)]
public string ServerName { get; set; }
[MaxLength(20)]
public string ServerIP { get; set; }
public int JobEnabled { get; set; }
public virtual ICollection<pcPlugInValue> PlugInText { get; set; }
//JobActions holds a list of Schedules
public virtual ICollection<pcJobSchedule> JobSchedules { get; set; }
//FK to the JobTypes table (Delete Files, Verify Backups, Ping, etc)
public long pcJobTypeId { get; set; }
public virtual pcJobType pcJobType { get; set; }
//IVersionTracking
public DateTime DateCreated { get; set; }
public DateTime LastUpdated { get; set; }
[Timestamp]
public byte[] Version { get; set; }
}
public class pcPlugInValue : IVersionTracking, IpcIdentity
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public long Id { get; set; }
//IpcIdentity
[Required]
[MaxLength(75)]
public string name { get; set; }
[MaxLength(1000)]
public string description { get; set; }
public string PlugInText { get; set; }
public int ExecuteOrder { get; set; }
//FK to the JobAction table
public long pcJobActionId { get; set; }
public virtual pcJobAction pcJobAction { get; set; }
//FK to the codes table (to indetify the schedule type: daily, weekly, etc)
public long pcCodeId { get; set; }
public virtual pcCode pcCode { get; set; }
//IVersionTracking
public DateTime DateCreated { get; set; }
public DateTime LastUpdated { get; set; }
[Timestamp]
public byte[] Version { get; set; }
}
public class pcJobSchedule : IVersionTracking, IpcIdentity
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public long Id { get; set; }
//IpcIdentity
[Required]
[MaxLength(75)]
public string name { get; set; }
[MaxLength(1000)]
public string description { get; set; }
//FK to the JobAction table
public long pcJobActionId { get; set; }
public virtual pcJobAction pcJobAction { get; set; }
//FK to the codes table (to indetify the schedule type: daily, weekly, etc)
public long pcCodeId { get; set; }
public virtual pcCode pcCode { get; set; }
public DateTime StartDate { get; set; }
public Boolean dayMonday { get; set; }
public Boolean dayTuesday { get; set; }
public Boolean dayWednesday { get; set; }
public Boolean dayThursday { get; set; }
public Boolean dayFriday { get; set; }
public Boolean daySaturday { get; set; }
public Boolean daySunday { get; set; }
public Boolean ThisJobIsNext { get; set; }
public DateTime EndDate { get; set; }
public int DateOfMonth { get; set; }
public int DayOfWeek { get; set; }
public DateTime ScheduleHour { get; set; }
public int EveryHowMany { get; set; }
public DateTime RunTimeLast { get; set; }
public DateTime RunTimeNext { get; set; }
//IVersionTracking
public DateTime DateCreated { get; set; }
public DateTime LastUpdated { get; set; }
[Timestamp]
public byte[] Version { get; set; }
}
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
根据您的架构描述,我是否可以假设您的主机应用程序在某个地方有一个 [ImportMany] 导致您的所有插件都由 MEF 实例化?
如果是这种情况,一种选择是(正如我相信您所要求的那样)将事件添加到您的插件接口,并从主机应用程序附加到每个插件中的该事件。我自己已经这样做过并且效果很好。
另一种选择(如果适合您的体系结构)是将 EF 类放入单独的程序集中,在插件程序集中引用该程序集,然后直接从插件进行数据访问。
From your architecture description, can I assume that your host application has, somewhere, an [ImportMany] that causes all of your plugins to be instantiated by MEF?
If that is the case, one option is (as I believe you asked) to add an event to your plugin interfaces and attach to that event in each plugin from your host application. I have done that myself and it works fine.
Another option, if it fits into your architecture, is to put your EF classes in a separate assembly, reference that assembly in your plugin assemblies, and do your data access directly from the plugins.
我自己完成了第二个选项,其中我将 EF code-firstclasses 放入单独的程序集中,并有一些用于连接到 contextclass 和查询 ef 存储库的帮助程序类。
但是,如果您不希望插件直接访问整个数据库,那么最好选择选项 1。特别是如果将来您决定将数据库表拆分为不同的模式,并且您只需要某些模式插件仅与数据库中的特定模式进行交互。
I've done the second option myself, where I have placed my EF code-firstclasses into a seperate assembly, and have some helper classes that are used to connect to the contextclass, and query the ef repository.
However, if you don't want your plugins to have direct access to the entire database, then its probably best to do option 1. Especially if in the future you decided to have your database tables split into different schemas, and you want only certain plugins to be only to interact with specific schema within your database.