How to architect Entity Framework Application (with MEF) - c#

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

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.

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.

Related

Entity Framework - Duplicate Existing Classes, Code First?

I'm trying to think of a way to store actual templates of ticket items in my Entity Framework MVC project. The thing is, I've already done a Code First migration process in the past. What I need to do is create logic in my code to allow someone to save time creating a ticket by using pre-loaded data from a template. My current inheritance model uses an abstract class (MasterTicket) which is used as the parent since to me there can be multiple categories (a Google Calendar based task, "Appointment Task" and a purely internal task, "General Task"). Here's my parent abstract class:
[Table("Ticket")]
[ModelBinder(typeof(MasterTicketBinder))]
public abstract class MasterTicket
{
[Key]
public Guid id{ get; set; }
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int ART { get; set; }
public DateTime openTime{ get; set; }
public DateTime? closeTime { get; set; }
private bool active = true;
public bool Active{ get => active; set => active = value; }
public string summary{ get; set; }
public string description{ get; set; }
public DateTime updateTime{ get; set; }
//TODO: Create foreign key relationship to user model
public Guid userUpdateId{ get; set; }
//TODO: Create foreign key relationship for tickets from other systems
public Guid externalAppId{ get; set; }
//TODO: Create foreign key relationship to user model
public Guid userOpenId{ get; set; }
public Guid? userCloseId { get; set; }
public Guid userOwnerId{ get; set; }
private int timesUpdated = 0;
public int TimesUpdated { get => timesUpdated; set => timesUpdated = value; }
public DateTime expectedCompletionTime{ get; set; }
public DateTime actualCompletionTime{ get; set; }
public List<MasterTicketItem> masterTicketItems{ get; set; }
public MasterTicket()
{
}
}
An here's an example of the concrete Google Calendar-based "Appointment Task" child:
[Table("AppointmentTickets")]
public class ApptTaskTicket : MasterTicket
{
public DateTime currentApptTime { get; set; }
public DateTime? endApptTime { get; set; }
public bool allDay { get; set; }
public string customerName { get; set; }
//TODO: Create foreign relationship
public Guid subjectPrsnlId { get; set; }
public string gCalEventId { get; set; }
public string customerPhone { get; set; }
public string customerEmail { get; set; }
public string preferredContactMethod { get; set; }
public List<ApptConfirmItem> apptConfirmItems { get; set; }
}
I know I can easily create a column for the MasterTicket class to indicate that it's a template, but to me I feel it's cleaner to have a separate "Template Table" if you will that will store pre-existing values that can be filled in with a form. In this case, I think I would WANT to create a duplicate class that would store said templates so that there are only several rows. What would be the best way to do this with Code First? Does someone feel I should take a different approach? Maybe DB First is a better way to go?
In case it matters, here's my DBContext for the Tickets:
// Code-Based Configuration and Dependency resolution
[DbConfigurationType(typeof(MySqlEFConfiguration))]
public class TicketDB : DbContext
{
public TicketDB(): base("AffirmativeServiceSystem.Properties.Settings.AffirmTaskManager")
{
}
public DbSet<MasterTicket> tickets { get; set; }
public DbSet<MasterTicketItem> ticketItems { get; set; }
}

Using Entity Framework for relating 3 entitites where 1 of them can only be related to either one of remaining two

Suppose I have two entities, bungalows and apartments. Both of them have varying fields and cannot be interchanged however both these entities have multiple tenants. Each tenant can only be part of either one bungalow or one apartment. How do I achieve this using Entity Framework?
I was thinking of creating 2 more entities bungalowTenants and apartmentTenants and using these to map. Each bungalowTenant would have one instance of a bungalow and a tenant and similarly for apartmentTenant.
Bungalows would have a collection of bungalowTenants and apartment of apartmentTenants.
public class Bungalow
{
public int Id { get; set; }
public int HouseNumber { get; set; }
public string Street { get; set; }
public ICollection<BungalowTenants> Tenants { get; set; }
}
public class Apartment
{
public int Id { get; set; }
public int ApartmentNumber{ get; set; }
public string Wing{ get; set; }
public string Building{ get; set; }
public ICollection<ApartmentTenants> Tenants { get; set; }
}
public class Tenant
{
public int Id{ get; set; }
public string Name{ get; set; }
}
public class ApartmentTenants
{
public int ApartmentId { get; set; }
public Apartment Apartment{ get; set; }
public int TenantId{ get; set; }
public Tenant Tenant{ get; set; }
}
public class BungalowTenants
{
public int BungalowId{ get; set; }
public Bungalow Bungalow{ get; set; }
public int TenantId{ get; set; }
public Tenant Tenant{ get; set; }
}
The problem with this approach is that it does not restrict in any way the same tenant to be a part of both, a bungalow and an apartment. I am unable to figure out how to do that using Entity Framework. I'd appreciate any help on this matter.
Not every business rule can be or needs to be translated to database constraints or model constraints.
And if you would enforce this, through model rules or programmed rules:
What if the tenants decide to move from a bungalow to an apartment? They will most probably want to start renting the new home days or even weeks before the cancellation date of the old one - or do you expect them to vacate the old home before midnight and enter the new home after midnight, with all their belongings packed in boxes on the pavement for a certain period? That does not seem very realistic.

Database model name change and table change Visual Studio still looks for at old table name

I am having a weird issue with my project. I have 2 models in my project and they are called Job and Jobs. Originally when I started the project it created the tables in SQL as Jobs and Jobs1. I am confused as to why it called the tables these names. When I deleted the tables in SQL and recreated them to add changes I named them Job and Jobs. When I run the project and try to retrieve the data it is telling me it cannot find dbo.Jobs1. I do not have anything in my code that would make entity framework create these tables in the first place. Also my AspNet _Migration table does not exist anymore. I deleted it due to changes and the system usually puts a new one back but it doesn't appear to do this either. Not important to me unless someone can tell me that it will break the system if not there or that the problem I am having is related. I have done a search in the project for dbo.Jobs1 and just Jobs1 and nothing comes up. Could someone direct me as to where to correct this and maybe why it created these tables in that fashion in the first place. Thank you.
These are the models of question in my project:
public class Job
{
public Job()
{
SubJobs = new List<Jobs>();
}
[Key]
public string TopLvlJob { get; set; }
public string Description { get; set; }
public string ExtDescription { get; set; }
public string PartNumber { get; set; }
public string Drawing { get; set; }
public bool UpgradeAvailable { get; set; }
public virtual IEnumerable<Jobs> SubJobs { get; set; }
}
}
public class Jobs
{
public Jobs()
{
Parts = new List<MaterialRequired>();
}
[Key]
public string JobNumber { get; set; }
public string Drawing { get; set; }
public string PartNumber { get; set; }
public string Description { get; set; }
public string ExtDescription { get; set; }
public string TopLvlJob { get; set; }
public bool IsViewable { get; set; }
public virtual IEnumerable<MaterialRequired> Parts { get; set; }
}

C# MVC Code First Complex Model

I have one table "Adverts" which stores basic info about adverts (eg: Name, Excerpt, Creation date...), and I need to store more detailed info in a separate table, But, here's my problem. Adverts can be different by type (sell, buy, rent, ...), category (residential, commercial, ...), so, detailed info is also different (eg: Commercial Advert don't need kitchen area property). I want to make few models which will describe detailed info for specific type or category
Here's my Adverts model:
[Table("Adverts_Adverts")]
public class Advert {
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public Guid AdvertId { get; set; }
public virtual Metadata Metadata { get; set; }
[Required]
[DataType(DataType.Text)]
public String Name { get; set; }
[DataType(DataType.Html), AllowHtml]
public String Content { get; set; }
[ForeignKey("Section")]
public Guid SectionId { get; set; }
public virtual Section Section { get; set; }
[ForeignKey("Category")]
public Guid CategoryId { get; set; }
public virtual Category Category { get; set; }
[ForeignKey("Type")]
public Guid TypeId { get; set; }
public virtual Type Type { get; set; }
public Decimal Price { get; set; }
[DataType("Enum")]
public Currency Currency { get; set; }
[ForeignKey("Details")]
public Guid DetailsId { get; set; }
public virtual ?????????? Details { get; set; }
[ForeignKey("User")]
public String UserId { get; set; }
public virtual User User { get; set; }
[ReadOnly(true)]
[DataType(DataType.DateTime)]
public DateTime Added { get; set; }
[ReadOnly(true)]
[DataType(DataType.DateTime)]
public DateTime Updated { get; set; }
public Int32 Views { get; set; }
[ReadOnly(true)]
public Status Status { get; set; }
...
}
here's my detailed info model for residential adverts:
[Table("Adverts_Details")]
public class ResidentialDetails {
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public Guid DetailsId { get; set; }
[ForeignKey("Advert")]
public Guid AdvertId { get; set; }
public virtual Advert Advert { get; set; }
[Required]
public Int32 Storeys { get; set; }
[Required]
public Int32 Floor { get; set; }
[Required]
public Int32 Rooms { get; set; }
[Required]
public Decimal TotalArea { get; set; }
[Required]
public Decimal LivingArea { get; set; }
[Required]
public Decimal KitchenArea { get; set; }
...
}
and this may be for commercial adverts:
[Table("Adverts_Details")]
public class CommercialDetails {
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public Guid DetailsId { get; set; }
[ForeignKey("Advert")]
public Guid AdvertId { get; set; }
public virtual Advert Advert { get; set; }
[Required]
public Int32 OfficesCount { get; set; }
[Required]
public Int32 Floor { get; set; }
[Required]
public Decimal TotalArea { get; set; }
...
}
So, how can I access both, ResidentialDetails and CommercialDetails, data within advert's property "Details"?
(Thank in advance)
This is an architecture problem, which is hard to answer without a complete understanding of your business rules. I can give you some general advice that will hopefully help you along.
As much as possible, remove complexity. I'm not sure what a "kitchen area property" is, but can you generalize it at all? Based upon context, you can call it something different, use it differently, etc. but if it's just a text field, then you can repurpose it in other contexts. Maybe for a residential advert it's "kitchen area" while maybe for commercial it's "break room area". (I really have no idea what this property is for, but I'm just trying to make the point that the same property can have a similar but slightly different meaning in different contexts).
If you can't generalize, then you'll need to start working on inheritance strategies. Create an object graph. How are these types and categories of adverts related. How are they different. Which ones are supergroups of others, etc.? Again, I don't know anything about the business rules at play, but maybe you need classes like Advert, ResidentialAdvert : Advert and CommercialAdvert : Advert. Then, you can add additional properties to these subclasses as necessary.
You'll also need to decide on a relational strategy. By default, EF will implement simple inheritance as STI (single-table inheritance, aka table per hierarchy or TPH for short). In other words, with the classes above, you would end up with an Adverts table with a Discriminator column. The value for this column would be one of "Advert", "ResidentalAdvert", or "CommercialAdvert", indicating which class should be instantiated, but all of the columns for all of the subclasses would reside in the same table. The benefit is that no joins are necessary, but the detriment is that all additional columns on your subclasses must be nullable or have default values. Other possible strategies would include, table per type (TPT), a compositional strategry, or table per concrete type (TPC), where every subtype gets its own unique table with all the fields from all supertypes.

Many to Many self Join with Entity Framework Code First

Consider this Poco:
public class User
{
public int Id { get; set; }
public string Username { get; set; }
public string Fullname { get; set; }
}
Now i want to implement a follow technique where a user may follow other users so basically its self Many to Many relationship
problem is i don't know how exactly i can achieve this in Entity Framework Code-First ?
I thought of a linker Table :
public class UserFollow
{
public int Id { get; set; }
public int Follower { get; set; }
public int Following { get; set; }
public DateTime FollowDate { get; set; }
}
i want to be able to get All Followers and Following from every User Object?
This is quite simple using EF code-first as you only need the User POCO:
public class User
{
public int Id { get; set; }
public string Username { get; set; }
public string Fullname { get; set; }
public ICollection<User> FollowedUsers { get; set; }
}
The collection means that a User is related to other users.
PS: I noted you added a timestamp in your solution example. To achieve that you should still add the collection changing the generic type to whatever suits your needs.
Hope it helps.

Categories

Resources