I have a Person Class and Inventory can be two types: Sales and CustomerService.
Sales and CustomerService have their unique properties and Peron holds the common properties.
I want to be able to query
So, when creating all three classes how do i create EF relation between them? OR is there a better way to think about the division of classes?
I don't want to have Person as Abstract class because most of the time i would want to query for the common properties.
There are 3 possible approaches you can take here:
1. Store all types in a single table (Table per Heirarchy)
You would have a single Person class that contains all possible properties that would be needed between the three classes. In addition, you would add a PersonType enum to specify different types for each entry.
public class Person
{
public int PersonId { get; set; }
public string Name { get; set; }
// ...
public PersonType Type { get; set; }
}
public enum PersonType
{
Sales,
CustomerService
}
This is generally the simplest and best performing approach. The biggest issue is with specialized fields. Since every type is in this one table, this table will need to contain all of the fields that any type may need. This also means all specialized fields need to be nullable, which makes it difficult to enforce specific types having specific fields.
2. Store each type in a separate table (Table per Concrete Class)
Instead of having a Person table at all, you could instead just have Sales and CustomerService tables that simply repeat the properties that would have been contained in the Person table.
public class Sales
{
public int SalesId { get; set; }
public string Name { get; set; }
// ...
}
public class CustomerService
{
public int CustomerServiceId { get; set; }
public string Name { get set; }
// ...
}
Of course, you can still take advantage of the Person abstraction in code if you want. Using code-first, you can make use of inheritance:
public class Person
{
public string Name { get; set; }
}
public class Sales : Person
{
public int SalesId { get; set; }
// ...
}
public class CustomerService : Person
{
public int CustomerServiceId { get; set; }
// ...
}
Just make sure that you only define entities for Sales and CustomerService in your DbContext subclass:
public class MyContext : DbContext
{
// Do not include a DbSet for Person.
public DbSet<Sales> Sales { get; set; }
public DbSet<CustomerService> CustomerService { get; set; }
// ...
}
The advantage of this approach is that your types are separated into clear, distinct sets. The downside is that there is no easy way to do a universal search through every single "person" since that abstraction doesn't exist as far as the database is concerned. For example, if you wanted to find someone with a specific name, you'll have to do separate searches through the Sales table and the CustomerService table manually, which may not be ideal. Also, if you end up with a person who serves a role in both sales and customer service, you'll be creating redundancy since you need to enter their information for both entries.
3. Store each type and the base type in their own tables (Table per Type)
On top of your Person class, you'll also create Sales and CustomerService classes that each specify their specialized properties and contain a reference to the Person class. This is a common principle known as composition over inheritance; since we can't effectively model inheritance in a database, we can use composition instead.
public class Person
{
public int PersonId { get; set; }
public string Name { get; set; }
// ...
}
public class Sales
{
public int SalesId { get; set; }
public int PersonId { get; set; }
public virtual Person { get; set; }
// ...
}
public class CustomerService
{
public int CustomerServiceId { get; set; }
public int PersonId { get; set; }
public virtual Person { get; set; }
// ...
}
This will allow you to add the specialized properties for each type while still maintaining a universal Person table that you can search through. This will also allow you to reuse a person's information if they serve multiple roles. The downside is that creating a new Sales and CustomerService record is a little more tedious, since you'll also need to also either find an existing Person record or create a new one. This also may not be the best on performance since queries may end up requiring joins.
The approach you should take depends on your needs. If you want to go more in depth with these 3 strategies, check out this tutorial for implementing inheritance in Entity code-first:
http://www.entityframeworktutorial.net/code-first/inheritance-strategy-in-code-first.aspx
With Entity Framework Core you can use inheritance in your database:
public class PeopleContext : DbContext {
public DbSet<Person> Persons { get; set; }
protected override void OnModelCreating(ModelBuilder modelBuilder) {
modelBuilder.Entity<CustomerService>().HasBaseType<Person>();
modelBuilder.Entity<Sales>().HasBaseType<Person>();
}
}
This will create one table with the properties of all derived types. Also, it will create a Discriminator-Column so that if you query your database EF Core instantiates the correct derived types:
context.Users.Add(new Sales() {
Id = 1
});
context.SaveChanges();
// This will actually be of type "Sales"
var salesPerson = context.Persons.Single(u => u.Id == 1);
For more information look here and here.
I prefer to use System.ComponentModel.DataAnnotations to apply attributes to my models such as the database table name to the class, the key(s), the foreign keys, and the inverse properties for navigation. EF will use this to auto-magically create the database table(s) with the inheritance. EF can create one table with all the properties from the derived types, or separate tables for each type including the base class (which can be abstract or not). You can search against the base type and it will return the proper implementation of the derived types. So you can get back a list containing both Sales and CustomerService objects.
Related
I would like to map, in Ef Core, a complex type (in other words a type not directly mapped to a table) that does hold a list of object that is directly mapped to a table.
Consider following example below, where you'll find that the Diary logic (of which I now only show AddEntry) is separated from the Request class, as it has no concern being there.
I have no issues using .OwnsOne in Ef Core when using simpler types where the data is stored in the same table (e.g.: here it would be in the Requests table); but I have no idea or clue how I would map in OnConfiguring this situation.
C# model:
public class Request : DbEntity {
public Diary { get; set; }
}
public class DiaryEntry : DbEntity {
public DateTimeOffset Timestamp { get; set; }
public Request Request { get; set; }
public string Message { get; set; }
}
public class Diary {
private readonly List<DiaryEntry> _entries = new List<DiaryEntry>();
public Request Request { get; set; }
public IEnumerable<DiaryEntry> Entries => _entries;
public Diary AddEntry(DiaryEntry diaryEntry) {
if(diaryEntry != null)
_entries.Add(diaryEntry);
return this;
}
}
SQL Structure
Requests is a table that is mapped in EfCore to the Entity Request
DiaryEntries is a table that exists in EfCore and is indirectly mapped with a DbSet with Request as "HasOne" so that it has a relation.
The table 'Diary' does not exist, it is a 'complex type' that encapsulates logic, so I don't have to deal with it in the 'Request' type in C#
I don't believe anything like this is supported in Entity Framework as complex types are designed to support organizing properties on one table into related classes without relational tables. In your case you would like to take relational tables and map an intermediate class to avoid having rules about managing the child collection in the parent class.
One option I can put forward to help address the separation of concerns you appear to want to have between your classes is to leverage extension methods for the collection-specific logic you want exposed for a Diary.
For example:
[Table("Requests")]
public class Request
{
[Key, DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int RequestId { get; set; }
public string Name { get; set; }
public virtual IEnumerable<DiaryEntry> Diary { get; protected set; } = new List<DiaryEntry>();
}
[Table("DiaryEntries")]
public class DiaryEntry
{
[Key, DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int DiaryEntryId { get; set; }
public string Value { get; set; }
public virtual Request Request { get; set; }
}
Here we expose the Diary as IEnumerable<DiaryEntry> so consumers of the entity cannot modify the collection etc. From there the diary functionality can be managed by extensions:
public static class DiaryExtensions
{
public static DiaryEntry AddEntry(this IEnumerable<DiaryEntry> diary, DiaryEntry newEntry)
{
ICollection<DiaryEntry> diaryList = (ICollection<DiaryEntry>)diary;
if (newEntry != null)
diaryList.Add(newEntry);
return newEntry;
}
}
Personally though I would have this logic in Request as the diary entries are associated to the request. To separate the diary specific logic I would utilize partial classes but maintain responsibility for the supported behaviour in the class that contains them.
Suppose the following structure of classes and relationships:
class Document
{
public List<Version> DocumentVersions { get; set; }
// Other properties
}
class Register
{
public List<Version> RegisterVersions { get; set; }
// Other properties
}
class Version
{
public int VersionNumber { get; set; }
// Other properties
}
When using EF Core, it is going to produce 3 tables, D, R and V respectively where V is going to have 2 FK, one for D and one for R.
My questions are:
Is EF Core default approach correct? Wouldn't it lead to invalid states where V has no FKs because both FKs can be nullable.
I've read this and it almost answered my first question but it leads me to another question:
How can I tell EF to follow that approach: Should I have to create a derived type of V for each of its owners? or is there any way I can map a single entity to multiple tables and tell EF which relationships belong to which table?
Maybe is worth mention that my example is oversimplified and in reality I have 6 entities using the same V entity.
So, the dilemma is:
A) Should I keep two FKs in Version or
B) build two tables DocumentVersion and RegisterVersion instead of just Version?
Well, the truth is you can do both. You just have to decide which approach suits your system better. Let's have a quick look.
Approach A
To answer your question; yes EF's default approach is correct. Among creating two FKs and building two tables, it will create two FKs. It will create an extra table only in case of intermediate table for a many to many relashionship.
I always, though, recommend that we create all FKs ourselves instead of letting EF do it for us. This way we have more control over the behavior of the relationship and can also access the FKs in our application, since they are an entity's property.
public class Version
{
[Key]
public int VersionNumber { get; set; }
public int? DocumentID { get; set; }
public virtual Document Document { get; set; }
public int? RegisterID { get; set; }
public virtual Register Register { get; set; }
//Other properties
}
Since Version has a PK, it can create records without any of the FKs having any value. If this is allowed in your business model then leave it as it is. You can later provide a UI to assign "Versions" to either "Documents" or "Registers".
If you want to add some rules in your Version table; for example each record should have at least one FK or only one FK, you can do that by overriding the ValidateEntity method of your DbContext class (or through some sql constraint in the database probably).
protected override DbEntityValidationResult ValidateEntity(
DbEntityEntry entityEntry, IDictionary<object, object> items)
{
// validate entity from annotations
var result = base.ValidateEntity(entityEntry, items);
// custom validation rules
if (entityEntry.Entity is Version &&
(entityEntry.State == EntityState.Added || entityEntry.State == EntityState.Modified))
{
Version version = ((Version)entityEntry.Entity);
if (version.DocumentID == null && version.RegisterID == null)
result.ValidationErrors.Add(new DbValidationError(null, "A document or register must be specified."));
}
return result;
}
Note that you can create your own annotations to validate your entity properties. But these are restricted to a single property. If you want to add validations that combine more than one property, the ValidateEntity method is the only way I know of.
Approach B
There are two ways to implement this approach. The first is to keep the Version table and add two intermediate tables on top.
public class Document
{
public virtual List<DocumentVersion> Versions { get; set; }
// other properties
}
public class Register
{
public virtual List<RegisterVersion> Versions { get; set; }
// other properties
}
public class Version
{
[Key]
public int VersionNumber { get; set; }
//Other properties
}
public class DocumentVersion
{
public int DocumentID { get; set; }
public virtual Document Document { get; set; }
public int VersionID { get; set; }
public virtual Version Version { get; set; }
// other properties
}
public class RegisterVersion
{
public int RegisterID { get; set; }
public virtual Register Register { get; set; }
public int VersionID { get; set; }
public virtual Version Version { get; set; }
// other properties
}
This actualy allows a many-to-many relationship, but you can use it as a one-to-many.
The second way is to make Version abstract (not a database table) and build two new tables to inherit from Version:
public class Document
{
public virtual List<DocumentVersion> Versions { get; set; }
// other properties
}
public class Register
{
public virtual List<RegisterVersion> Versions { get; set; }
// other properties
}
// don't make this a DbSet
public abstract class Version
{
[Key]
public int VersionNumber { get; set; }
//Other properties
}
public class DocumentVersion : Version
{
public int DocumentID { get; set; }
public virtual Document Document { get; set; }
// other properties
}
public class RegisterVersion : Version
{
public int RegisterID { get; set; }
public virtual Register Register { get; set; }}
// other properties
}
This is a proper and clear one-to-many relationship.
Conclusion
The bottom line is that you can use any of the two approaches and with alterations that suit your needs.
I have used both approaches successfully, but I tend to prefer the second one (and with the abstract class inheritance). The first approach seems more of a way to cut down on database resources or ease of development, but modern databases are not at all stressed by a few tables more and the development could become unnecessarily complex. Further more the second approach allows to extend the functionality of the relationships by adding further properties to each connection table seperatelly. And for the 6 entities you have to deal with, it seems safer to me to go with the second approach. I have used this approach in an application with many file types and relationships and it was always very straight-forward and extendable. Those extra properties in each relashion table came very handy too.
Hope I could help,
merry coding!
I don't think this really is a one-to-many relationship, look here.
It would be a one-to-many relationship if (for example) Document had multiple (e.g. a list of) Versions.
If you want multiple entities refering to the same entity type, you could place the foreign keys explicitly in the Document and Register classes:
class Document
{
public Version DocumentVersion { get; set; }
public int DocumentVersionId { get; set; } // Or whatever datatype your ID is
// Other properties
}
class Register
{
public Version RegisterVersion { get; set; }
public int RegisterVersionId { get; set; } // Or whatever datatype your ID is
// Other properties
}
class Version
{
public int VersionNumber { get; set; }
// Other properties
}
I have an entity called Asset, similar to below:
public class Asset
{
public int Id { get; set; }
public int TypeId { get; set; }
public int AddedById { get; set; }
public DateTime DateTimeAdded { get; set; }
public virtual AssetType Type { get; set; }
public virtual ITUser AddedBy { get; set; }
}
I want to be able to have a navigation property that is linked to a single table, but that table is dependent on what type of Asset it is. For instance, if the Asset is of the type "Printer" then I want the navigation property to link to the PrinterDetail entity. My initial way of going about this was to have unused columns in the Asset entity, but I figured that was wasteful or bad practice. Is there something that I am overlooking or is this just something that cannot be done?
Thanks for any advice given.
if you want navigate printerDetail by type you can use entityfraemwork inheritance strategy:
Table per Hierarchy (TPH)
Table per Type (TPT)
Table per Concrete class (TPC)
you have to create Model per each type and use TPT strategy for that.
and then you can use fluent api for config mapping for that.
parent Model (Asset) must define as abstract class and AssesTypes Must be Drive from the Parent.
more information
I have a model with some inherits and it is using nhibernate to persisti on a Database. The nhibernate mapping with fluent nhibernate is working fine, but I have a scenario where I need to save a child for a existent parent. My model looks like this:
public class Item
{
public long Id { get; set; }
public string Name { get; set; }
// other properties
}
public class ItemCommercial : Item
{
public decimal Value { get; set; }
// other properties
}
In my Database, the respective tables are related by Id <-> Id (one per one).
I would like to know, how to Save just a ItemCommercial instance for a existent Item on database. I have the Id of the Item, but I do not know howt to say to nhibernate to say just the Child, instead creating a new Item, for sample:
session.Save(itemCommercialObj); // will create a Item and ItemCommercial with the same Id
Thank you.
As I also answered here
No, it is not possible to "upgrade" an already persisted object to its subclass. Nhibernate simply doesn't support this.
If you safe the subclass with the same ID as the base class, Nhibernate simply creates a copy with a new ID of the object instead of creating the reference to Member...
So basically you could do either
Copy the data of Customer into Member, delete customer and save Member
Use a different object structure without subclasses where Member is a different table with it's own ID and a reference to Customer
Use native sql to insert the row into Member...
you can not the runtimetype of an object like that hence NH does not support it. Change the design to
public class Item
{
public long Id { get; set; }
public string Name { get; set; }
public CommercialValue CommercialValue { get; set; }
// other properties
}
public class CommercialValue
{
public Item Item { get; set; }
public decimal Value { get; set; }
// other properties
}
and a one-to-one mapping. Then it is as simple as setting the CommercialValue property
I am trying to setup a project using Entity Framework 4, POCO, and Code-Only.
Is it possible in entity framework for type of a navigation property to be an interface?
I have a "Task" class. A Task can be assigned to a user or a group each of which are represented by a separate class and stored in separate tables. The classes look something like this:
public class User : IAssignable
{
public string Name { get; set; }
public int ID { get; set; }
public string Email { get; set; }
public string Password { get; set; }
}
public class Group : IAssignable
{
public string Name { get; set; }
public int ID { get; set; }
public string Manager { get; set; }
public string Department { get; set; }
}
public class Task
{
public string Title { get; set; }
public DateTime DueDate { get; set; }
public string Details { get; set; }
public IAssignable AssignedTo { get; set; }
}
Is there a way to may the AssignedTo property as a navigation property in entity framework? I assume there will have to be some type of discriminator for EF to know if it needs to look in the Users table or the Groups table but I can figure out the mapping using Code-Only or EDMX.
you can use interface in navigation property, take a look at this solution as it's the same as question:
How to use interface properties with CodeFirst
I know this is an old question, but no, there is no feature of Entity Framework (even the latest version 6) that allows you to map a navigation property with an interface type.
You could, however, map multiple navigation properties with concrete types (and a constraint that only one may be set) and provide an unmapped property of your interface type which coalesces the concrete navigation properties into a single property. Unfortunately, this may make your queries more complex because certain queries will need to know which concrete navigation properties to reference (and you can't query against your unmapped interface property).
There is significant complexity around support for polymorphic navigation properties. Consider what would have to happen in order to query your original AssignedTo property if you assume it's mapped to a column such as AssignedToId int. You'd have to union or join both User and Group entity sets and hope that a given AssignedToId appears in just one of them. This is the approach used by the Table-Per-Concrete (TPC) type mapping, but it only works with class inheritance (not interfaces) and careful planning for generating distinct ids across the participating types.
You could save yourself a lot of work by using the Text Template Transformation Toolkit (T4) supported by EF4. I found this one after a good 12 hours of looking for a way around manually creating my POCOs and interfaces,
http://blogofrab.blogspot.com/2010/08/maintenance-free-mocking-for-unit.html
Besides providing a brilliant base for unit testing, it auto-generates navigational properties based on the relationships defined in your model.