s0ftimage
s0ftimage

Reputation: 175

How to architect Entity Framework Application (with MEF)

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; }
}

Upvotes: 3

Views: 2308

Answers (2)

Gladys Yelland
Gladys Yelland

Reputation: 31

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.

Upvotes: 0

Chris Hogan
Chris Hogan

Reputation: 868

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.

Upvotes: 1

Related Questions