One to Many mapping models inside DbContext - c#

Currently I am new to MVC and practicing with some demo applications.
I have 2 models.
Employee - (with data members employeeId,name,gender,city,deptId)
Department - (with data members id,name,Collection employees )
I have put these two classes inside a context class inheriting from DbContext.
Problem is when I try to get employee data based on deptId , it throws error that department_Id column is not defined. If I remove the employees data member from Department then everything works fine. What exactly is happening here, why is it automatically adding a column, and how to tackle it?

Don't add deptId property to your Employee class. Just add a navigation property like this:
public virtual Department Department { get; set; }
And your Department class:
public virtual ICollection<Employee> Employees { get; set; }
Then Entity Framework will create all necessary relationships automatically for you.
For more informatin about Navigation Properties take a look at here: http://msdn.microsoft.com/en-us/data/jj713564.aspx

Related

Save record to multiple tables using UoW pattern

I am using MVC 5 with Entity Framework 6 and Unit of Work pattern.
Tables: and fields
Customer - Id, Name
ContactType - Id, Name (Home contact, work contact etc)
ContactDetails - Id, CustomerId, ContactTypeId, ContactValue
One Customer can have multiple Contact Details (so Customer1 has a home contact, work contact etc).
The ContactType table is a look-up table so it just displays the types of contacts available (home, work, emergency, mobile etc)
I have created the Interfaces and Classes as required to carry out the basic Add, Edit functionality, then created an Unit Of Work class to hold all these Repositories.
Tested it out and everything works as expected when i hard code values in.
When i created my MVC application, i added the below lines to add this entry into a database using the Unit of Work class
public ActionResult SaveContactDetails(CustomerContactType viewModel)
{
_unitOfWork.Customers.Add(viewModel.Customer);
_unitOfWork.ContactDetails.Add(viewModel.ContactDetail);
//_unitOfWork.SaveAllChanges();
return View();
}
I created a new ViewModel called CustomerContactType which is a class containing the tables i require in order to save the data successfully
public class CustomerContactType
{
public ContactType ContactType { get; set; }
public IEnumerable<ContactType> ContactTypes { get; set; }
public Customer Customer { get; set; }
public ContactDetail ContactDetail { get; set; }
}
I realised how to assign a dropdown value to the model within the .cshtml, so the ContactDetails table knows which ContactType is associated with that contact number (Home, emergency etc).
The problem i have is the ContactDetails requires a customer ID. This customer ID doesnt generate until the customer is saved so im not sure how i should be doing this?
These two lines carry out the task but i can see the customerID is null in the second line where i would have preferred it to contain the ID
_unitOfWork.Customers.Add(viewModel.Customer);
_unitOfWork.ContactDetails.Add(viewModel.ContactDetail);
I can provide additional code if required but wasnt sure if theres an easy fix or not.
Instead of using CustomerId in ContactDetails, why not change CustomerId to be of a complex type Customer and decorate it with a foreign key attribute? That way you can add a ContactDetails object along with a Customer object assigned to its property and a single call to SaveChanges should persist both entities to the DB.

Map entity to view and table

Consider the model below. I have an Order class and an OrderLine class. The Order.TotalAmount is calculated through a view which performs an OUTER APPLY across all the Order.OrderLines.
[Table("SelectOrder")]
public class Order
{
public decimal TotalAmount { get; set; }
public virtual ICollection<OrderLine> OrderLines { get; set; }
}
[Table("SelectOrderLine")]
public class OrderLine
{
public decimal Amount { get; set; }
public virtual Order Order { get; set; }
}
I have decorated my classes with the TableAttribute to enable Entity Framework Core to get the data from the views to the entity. The TableAttribute actually points to the view instead.
Now I would like to perform inserts, updates and deletes. This poses a problem as it's not possible to use a view with an OUTER APPLY for these changes. I've tried using query types for this but you cannot define an entity as both a query type and an entity type. Doing so results in an error for me. So adding a TableAttribute with the actual table e.g. Order in combination with modelBuilder.Query<Order>().ToView("SelectOrder"); does not work.
I could create a separate class SelectOrder which is mapped to the view and map my Order entity to the table. Or I could build a custom attribute and perform some custom SQL generation by overriding the SqlServerQuerySqlGenerator.
But before I go down these roads... Is it really not possible to map an entity to both a view for selects and a table for inserts, updates and deletes?
Maybe not the answer you're looking for, but you could add a calculated OrderTotal attribute to the Order table in the database.
Since .NET 5 Preview version was released it's possible to support a separation of query and update the mapping
ref. https://github.com/dotnet/efcore/issues?q=is%3Aissue+milestone%3A5.0.0-preview2+is%3Aclosed+label%3Atype-enhancement+is%3Aclosed

Inheritance with EF Code First - Table per Concrete Type (TPC)

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.

One-To-Many AND Recursive relation - Force value to be set

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.

How to map a Value Type which has a reference to an entity?

I'm having a problem with a mapping in Entity Framework.
I have the following classes (simplified):
public class Building
{
public int ID { get; set; }
// *.. snip..* other properties
public Location Location { get; private set; }
}
public class Location
{
public string Street {get; set;}
public Country country {get; set}
}
public class Country
{
public int ID { get; set; }
public string Name { get; set; }
}
Building and Country are entities, they are saved in the database. Location is a value type and should map to the same table as Building.
However, when I map it this way, entity framework wants to map Location to a table as well and is complaining it has no Key. I don't want to give it a key since it belongs to the Building and should not be an entity at all.
I have seen workarounds which say you need to put Country on the Building-class, but that just doesn't feel good (and is semantically just plain wrong).
I'm using Entity Framework 5
Since the release of Entity Framework Core 2, it is now possible to achieve this using owned entities.
Your configuration would look like:
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
// ...
modelBuilder.Entity<Building>().OwnsOne(x => x.Location);
modelBuilder.Entity<Location>().HasOne(x => x.Country);
// ...
}
This way, properties from Location class will be a part of the table Building class is mapped to. This means you will only have tables for Building and Country classes and the Building table will have a foreign key to the Country table.
I know it's been a long since you posted the question, but I thought this answer might be helpful to someone who comes across this question.
In my opinion the Entity Framework shouldn't allow such a case.
I understand that you don't consider the Location as an Entity but adding entity references to complex types doesn't seem like a solid approach either. The relationship of a building to a country is quite straight forward. A building belongs to one country. Thus a building model should include a country id. What would you expect to be mapped?
If you would expect the table Building to have just three columns ID, Street, CountryId and you still want to hold the Location model then you should use the following complex type.
public class Location
{
public string Street {get; set;}
public int countryId {get; set}
}
If however you would expect your Building table to have all the fields from the model Country then that could lead to some tricky situations like what would happen If you wanted to add new fields to the Country model or If you wanted to add other complex types or entities to your Country model according to a new Business Case.
Those cases would mess with the relational concept and would over-complicate your structure without any meaningful reason. (in my opinion of course)
You may mark Location property in Building class with [NotMapped] Attribute.
using System.ComponentModel.DataAnnotations.Schema;
public class Building
{
[NotMapped]
public Location Location { get; private set; }
}
Hope that solves your problem!

Categories

Resources