I'm having a hard time defining the relations I want using EF Core(1 to many) .
E.G:
I am an entity of Task and Employee , each task is given by an employee and also is appointed to an employee . I've created the Task class as follow:
public class Task
{
public int TaskId { get; set; }
[ForeignKey("RequestedBy")]
[Required]
public int RequestedById { get; set; }
[Required]
[ForeignKey("TaskedTo")]
public int TaskedToId { get; set; }
public virtual Employee RequestedBy { get; set; }
public virtual Employee TaskedTo { get; set; }
}
I think I've done it correctly, but I have a problem with my Employee class. Usually (When there's only one join) I would simply create virtual collection of Task Property in Employee .. but what am I supposed to do now ? Is this enough to set the relation or should I add virtual properties for these two tasks?
And another thing, when I have an owned entity, with 1-Many relation , is it enough to add the property in the owner entity, and do nothing in the owned one? Or do I have to specify the [Owned] Annotation?
The issue is that you have two one-to-many relationships between the same two entities. For the Employee class you would need two collections, one for each relationship. Additionally, you'll need to use the InverseProperty attribute to tell EF which foreign key goes with which collection:
public class Employee
{
...
[InverseProperty(nameof(Task.RequestedBy))]
public ICollection<Task> RequestedTasks { get; set; }
[InverseProperty(nameof(Task.TaskedTo))]
public ICollection<Task> AssignedTasks { get; set; }
}
You don't need virtual. That's to enable lazy-loading. For the lazy-loading functionality, EF creates a dynamic proxy class that inherits from your entity and overrides the getter on the navigation property. The virtual keyword is required in C# to allow a class member to be overridden.
Also, the Owned attribute is for value objects. It's a way of having a related class whose properties are literally mapped onto the same table or if given its own table, inherently tied to the entity that "owns" it, such that you access that data through the entity, not separately. Neither of which applies here.
Finally, you should reconsider the name Task for this class. .NET already has a Task class, and it's used very frequently. If you name your class Task as well, you'll be having to specify namespaces virtually every time you use either one, which is a pain.
Related
I created such a kind of Table per Concrete Type (TPC) structure as shown below:
Here are the entities used here:
public abstract class BaseModel : MyOtherBaseClass
{
[Key]
public int Id { get; set; }
//Foreign key for Project
public int ProjectId { get; set; }
public int Sequence { get; set; }
public string Name { get; set; }
public string IconUrl { get; set; }
//Navigation Properties ####################
public virtual Project Project { get; set; }
}
[Table("Tool")]
public class Tool : BaseModel
{
[Key]
public int Id { get; set; }
public string ToolBrand { get; set; }
//Navigation Properties ####################
//public virtual Project Project { get; set; }
}
[Table("Priority")]
public class Priority : BaseModel
{
[Key]
public int Id { get; set; }
public string PriorityCode { get; set; }
//Navigation Properties ####################
//public virtual Project Project { get; set; }
}
At this point I am confused about the issues below:
1) Can I use Id for Tool and Priority entities? And will it be absolutely unnecessary?
2) I use FK (ProjectId) and related table Project in the BaseModel class. But, as the ProjectId columns will be created in the Tool and Priority tables, how can the relation be created? May there be any problem?
3) If I do not prefer Fluent API, should I add Tool and Priority entities besides BaseModel entity in the context? Because in some resources the child classes are added while in some others not. Which one is true?
public class EntityContext : DbContext
{
public DbSet<BaseModel> BaseModel { get; set; }
// ? public DbSet<Tool> Tool { get; set; }
// ? public DbSet<Priority> Priority { get; set; }
}
If there is any problem regarding to this usage could you also please let me know? Thanks...
Whenever you design classes you should be aware of what the classes really represent.
Your DbSet<...> represents one table your database. The class inside the DbSet represents the columns of this table as non-virtual properties, and the relations between the tables as virtual properties.
It seems to me that you want two tables in your database: a table with Tools and a table with Priorities. You currently think that some columns of Tools are also in Priorities. Therefore you intend to make a common base class of which you have no better name than BaseModel.
The fact that you can't invent a proper name for the common base class should warn you that maybe there is nothing really common between those two. Are you sure, that if you describe a Tool, that is is not an incident that it has some properties with the same name and type as a Priority
For example. If you would define rows of Tools and rows Priorities, you would say that every Tool is used in a Project, and every Priority is the priority of a Project. This is part of the design of your system. According your definition it is meaningless to have Tools without its Project
Even so, according to your definition, every tool should have a "method of unique Identification", you decided to use an integer Id for this. Similarly you decided to have an integer Id for priorities. However, is it intrinsic to a row in your Tool table that the type of the identification equals the type of identification of a Priority? Would you want your design be worthless if someone told you that a Tool has a Guid Id and an Priority has an integer Id?
No of course not: your design should be so robust that small changes in your database tables should lead to small changes in your design!
Which properties should be in the base class
1) Can I use Id for Tool and Priority entities?
Answer: Yes you can put the Id in the base class and omit it from the derived classes. However, this would add a constraint that every derived type should have the same name and type for the column in the table that represents the Id.
Hence: if you don't want to limit yourself to this: don't do this, remove the Id from the base class and put it in the derived class. If later someone decides a small change to rename the column, or give it a different type your changes will be similar small.
Of course this is also for all other properties: if they are identical "by coincidence", put the values in the derived class. If it is typical something that both Tools and Priorities have in common, put it in the base class.
It is not easy for me to detect similarities between Tools of a Project and Priorities of a Project, so I'll switch to a different example.
Suppose you have a database with Teachers and Students. Some properties are unique for the Teachers, and some are unique for the Students. But they also have something in common that is not by coincidence: Teachers and Students are both Persons, with a Name, a Birthday, possibly an Address etc. If later someone decides that an Address has an extra field indicating the GPS coordinates of the address, then you'll only have to change one class.
Conclusion: only put properties in the base class that are intrinsic common to all derived classes, not by coincidence
Where to put the foreign key?
According to your design, both Tools and Priorities are things belonging to a Project. If after the previous step you decided that this is the only thing they have in common, then you'll seldom take a bunch of objects that contain both Tools and Priorities.
In the school database it would be quite normal to put Students and Teachers into a bunch of Persons, where every Person would have an Address, and in every Address would be living zero or more Persons (one-to-many)
// this will be a row in a table, hence it has an Id
class Address
{
public int Id {get; set;}
public string ZipCode {get; set;}
public string Street {get; set;}
...
// on every address live zero or more Persons (one-to-many)
public virtual ICollection <Person> Persons {get; set;}
}
// this will not be a row in a separate table, hence it has no ID
class Person
{
public string Name {get; set;}
public DateTime Birthday {get; set;}
...
// every Person lives at an Address, using foreign key
public int AddressId {get; set;}
public virtual Address Address {get; set;}
}
class Teacher : Person
{
public int Id {get; set;}
...
}
class Student: Person
{
public int Id {get; set;}
...
}
So you will have three tables: Addresses, Teachers, Students. Both Teachers and Students will have the properties of a Person. They both live at exactly one Address.
See how little changes need to be made if we decide to add a column to a Teacher, or a Person? How little needs to be changes if you want a Teacher Id to be a GUID, or the Address.Id be a String (expect changes in primary Key of Address and foreign key inside Person). See how little needs to be changed if you want to add a new type of Person: a Parent?
Conclusion: if you have a base class where every derived class should have a reference to an item in a different table: put the foreign key in the base class. However, if this relation is not intrinsic to all derived items, put the foreign key in the derived classes.
3 should I add entities besides BaseModel entity in the context?
Remember: every DbSet in your DbContext will become a table. If you don't specify Tools and Priorities as separate tables, you won't be using Table per concreate class (TPC) but table per hierarchy (TPH): both Tools and Priorities will be in one table
I seldom use TPH because of all unused null values I get in my tables.
If you most often will ask for "Teachers that ..." and "Students which ...", you should keep them in separate tables. The columns of the base class are also in these separate tables.
If you most often will ask for "Persons who ...", where the Persons might be Students or Teachers, consider using table per type (TPT): a Persons table, and a Teachers table with a foreign key to the Persons tables, as well as a Students table with a foreign key. All base class properties are in the Persons table.
It is easy to see that if you ask for "Persons that..." TPT will only query one table, whild for TPC you'll need to query the Teachers table as well as the Students table and concatenate the result.
However if you ask for "Students that ..." TPT will need to join the Persons table and the Students table. TPC is here faster: only one table is accessed.
I inherited a project with all the one to many relationships created in this fashion
[Table("A")]
public partial class A
{
public int Id { get; set; }
public int Something {get; set; }
public virtual ICollection<B> B { get; set; }
}
[Table("B")]
public partial class B
{
[Key]
public int Id { get; set; }
public int Something {get; set; }
[Required]
public virtual A A { get; set; }
}
What struck me was the lack of a int Foreign Key property in the B model.
Entity Framework must create it because they exist in our database.
Can anyone explain first why this is happening and two if this can cause problems with lazy loading?
EntityFramework by default looks for the name "id" and makes it a key. You could specify the decoration to make it faster, since it does not have to guess the key of the table.
I don't believe it affects lazy loading since lazy loading is achieved by creating instances of derived proxy types and then overriding virtual properties to add the loading hook
Sources:
Microsoft Key
Microsoft Lazy Loading
Foreign key properties are not required by the EF. EF can build hidden fields basing on object relations (and I usually use this configuration because I think is more POCO). The only issue is that in some cases, the exception raised during validation or SaveChanges is more cryptic, otherwise everything works fine.
About foreign key column names, you can configure them using fluent api (Map + MapLeftKey, MapRightKey and MapKey methods).
I'm trying to understand what I need to do in order to introduce the same Foreign Key in multiple derived classes when the Foreign Key is not present in the base class. The Foreign Key is to the same type, and I'm able to make the various derived classes all use the same column name, but when I try to introduce the Foreign Key annotation, Entity Framework 6 silently fails to create any Foreign Key at all.
It's worth mentioning that, if I allow EF to create Bar_Name1 instead of reusing the existing column, it adds the Foreign Key appropriately. But I come from a relational database background, and it offends my sensibilities to have multiple columns for the same thing.
I would prefer to be able to stick to using Annotations to mark up my code, but if this is something that can't be done with Annotations but can be done with the Fluent API, I'm willing to delve into that.
public class Foo
{
[Key]
public string Name { get; set; }
}
public class FooSub1 : Foo
{
[Required, Column("Bar_Name")]
public string Bar_Name { get; set; }
[ForeignKey("Bar_Name")]
public Bar Bar { get; set; }
}
public class FooSub2 : Foo
{
[Required, Column("Bar_Name")]
public string Bar_Name { get; set; }
[ForeignKey("Bar_Name")]
public Bar Bar { get; set; }
}
public class Bar
{
[Key]
public string Name { get; set; }
}
The resolution to EF issue 1964 explains: "However, having an association in s-space here doesn't work anyway because it results in two database constraints which can only be satisfied if the dependent also matches the PK for the other relationship type. Such a match would usually only happen accidentally. The solution is to remove the associations from s-space like we do for similar TPC mappings." (emphasis mine)
EF drops the FK's on the merged column. In your case, the two FK's are logically the same, but EF doesn't know (or care about) that.
Environment
Framework 4.5
Entity Framework 6 (code First)
Model
MainEntity One to many ChildEntity relationship
ChildEntity One to many recursive ChildEntity relationship
Model in code Snippet
public class MainEntity
{
public long Id { get; set; }
public virtual Collection<ChildEntity> ChildEntities { get; set; }
}
public class ChildEntity
{
public long Id { get; set; }
public MainEntity ParentMainEntity { get; set; }
public virtual Collection<ChildEntity> ChildEntities { get; set; }
}
Note : A ChildEntity can only ONE level deep again ChildEntities as childeren.
Problem
I am able to persist this model. This seems to work fine. Only one issue. When I store a ChildEntity that has a parent ChildEntity. The MainEntity_id field is NULL, only the ParentChildEntityId is set. The reason I want to have the MainEntity_Id field always set is for performance queries.
How can I force that the MAINENTITY_ID field has always a value set in the deeper level?
First:
You are giving yourself a hard time by leaving the foreign keys out of your POCO's. Adding a MainEntityId property to your ChildEntity will enable you to set the relation of new ChildEntities to a MainEntity.
Second:
var newChild =new ChildEntity();
parentEntity.ChildEntities.Add(newChild);
parentEntity.ParentMainEntity.ChildEntities.Add(newChild);
should work depending on how you have loaded the entities and which entities are or will be attached to the dbContext.
You just need to define your relationship between MainEntity and ChildEntity to be required. You may do it in two ways:
place [Required] attribute over ParentMainEntity property in ChildEntity
use fluent api. In your DbContext class override OnModelCreating method and in it place code:
modelBuilder.Entity().HasRequired(e => e.ParentMainEntity).WithMany(e => e.ChildEntities);
I would like as well to recommend you to make all your entities properties virtual. When all will be virtual then ef instead of working with your entity classes will create its own DynamicProxy classes deriving from your classes. They will provide additional tracking functionalities, they automatically change values of navigation properties if related objects changes etc. EF seems to deal much better with them. To use that functionality for newly created objects you will need to create them with context.ChildEntities.Create() method instead of using constructor. Of course as this adds constrain on your ChildEntity objects you may encounter exception during persisting data to db in SaveChanges. If the above change is the only one, that you've applied it is very probable that there is at least one ChildEntity object that do not have MainEntity object assigned to it.
I'm experimenting with EF5 Code First and I am using the models (show below).
When I look at the database that is created, I am confused because I do not see anything in the Track table that points to the Category table. Category has a FK pointing back to Track but that means that there are going to be duplicates of the categories?
A little background: I am trying to build a model that has tracks and every track can have 1 to N Categories. All of the categories are already defined, that is they are basically a lookup and I plan to create them in the seed method when database is created.
I think I am not understanding something obvious... When I query a track, how will I know what category it contains?
Thx
public class Track : IAuditInfo
{
public Int32 Id { get; set; }
public String Name { get; set; }
public String Description { get; set; }
public String Data { get; set; }
public DateTime CreatedOn { get; set; }
public DateTime ModifiedOn { get; set; }
public ICollection<Category> Categories { get; set; }
public Track()
{
Categories = new List<Category>();
}
}
public class Category
{
public Int32 Id { get; set; }
public Boolean IsVisible { get; set; }
public String DisplayName { get; set; }
}
Your current model is a one-to-many relationship between tracks and categories.
This usually implemented, as you have noted that entity framework does, using a foreign key on the many side (category) to the one side (track).
If I understand you correctly, what you want is a many-to-many relationship. Many tracks can be related to the same category, and a single track can belong to many categories.
To let entity framework understand that you want a many-to-many relationship you can simply add a ICollection property to your category class.
So both your classes should have a collection of the other class.
I.e. tracks have many categories and categories have many tracks.
For more information you can also see: http://msdn.microsoft.com/en-us/data/hh134698.a.nospx
Olav is right, your data model at the moment is not telling Entity Framework that there is a many-to-many relationship in there.
The simplest way to resolve this is to add
public virtual ICollection<Track> Tracks { get; set; }
to your Category class.
However... You may not want to pollute your domain model with artefacts that are not relevant to your domain. More importantly, when you do it this way, it is up to Entity Framework to figure out what to call the binding table. Prior to EF6 this naming is non deterministic (see http://entityframework.codeplex.com/workitem/1677), which may mean that two different machines compiling the same code will decide on different names for that table and cause some interesting migration problems in your production system.
The answer to both problems is to always explicitly manage many-to-many relationships with Fluent Configuration.
In your Data Context class, override the OnModelCreating, something like this:
public class MyDb : DbContext
{
public IDbSet<Track> Tracks { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<Track>()
.HasMany(t => t.Categories)
.WithMany()
.Map(c => c.ToTable("CategoriesForTracks"));
}
}
If you do this, you don't need to add a navigation property to your Category class, though you still can (if you do, you should use the overload for WithMany that allows you to specify a property).
Relationships between entities and how to map that to a relational database is inherently hard. For anything other than the simplest parent-child relationships you will want to use the fluent API to make sure you actually get what you want.
Morteza Manavi has a really good blog series describing relationships in EF Code First in exhaustive detail.
NOTE
You should usually make navigation properties virtual. So, you should change your Category class like this:
public virtual ICollection<Category> Categories { get; set; }
In theory, not making it virtual should just cause eager loading rather than lazy loading to happen. In practice I have always found lots of subtle bugs appearing when my navigation properties are not virtual.