Reprint http://www.th7.cn/Program/net/201301/122153.shtml
How Code first handles inheritance between classes. Entity Framework Code First has three methods for handling inheritance relationships between classes, and we will describe each of the three processing methods.
1.Table Per Hierarchy (TPH): Create only one table that maps all the properties in the base class and subclass to the columns in the table.
2.Table per Type (TPT): Establishes a table for the base class and each subclass, and each table corresponding to the subclass contains only the columns that correspond to the attributes that are unique to the child class.
3.Table per concrete Type (TPC): Establishes a table for each subclass that corresponds to the column and subclass-specific properties of the table that corresponds to the child class that contains the properties of the base class.
1.Table Per Hierarchy (TPH)
In this approach, the Entity Framework Code first establishes a table for the base class and all subclasses, and all properties in the base class and subclass are mapped to one column in the table. The Entity Framework Code first establishes a column called discriminator in this table by default, with a type of nvarchar and a length of 128. The Entity Framework Code first stores the class as the value of the discriminator column when the base class or subclass is stored.
In our previous example program, we added a salesperson class because we wanted to record who created the order and who approved it.
public class Salesperson {
public string EmployeeID {get; set;}
public string Name {get; set;}
public string Gender {get; set;}
Public DateTime hireddate {get; set;}
}
And two instances of salesperson are added to the order class to record the creator and approver of orders.
Public salesperson CreatedBy {get; set;} Public salesperson Approvedby {get; set;}
We later refined our business process: The order was created by the salesperson, and the sales manager's approval was required when the customer requested a discount for the order, and the manager had a fixed discount total for each month. Both the salesperson and the sales manager belong to the sales staff. This is a typical inheritance relationship.
Based on our refined business process, we created two new classes, salesman and SalesManager
public class Salesman:salesperson {
}
public class Salesmanager:salesperson {
Public decimal Discountamountpermonth {get; set;}
}
Because of the complex business logic involved when the order was created and the need to specify customer and salesman for the order, we created a new factory class to create the order.
public static Class orderfactory{
public static order Createneworder (Customer customer, salesman CreateUser) { Order order = New Order ();
Order. Customer = customer;
Order. CreatedDate = DateTime.Now;
Order. CreatedBy = CreateUser;
Order. Approvedby = null;
return order; }
}
We created a new unit test method to test our new salesperson inheritance and the New Order factory class.
[TestMethod]
public void Canaddorderwithsalesman () {
Ordersystemcontext unitofwork = new Ordersystemcontext (); Productrepository productrepository = new Productrepository (unitofwork); Orderrepository orderrepository = new Orderrepository (unitofwork); Customerrepository customerrepository = new Customerrepository (unitofwork); Salesman salesman = new Salesman {
EmployeeID = "2012001",
Gender = "M",
Hireddate = DateTime.Parse ("2010-5-19")};
Customer customer = Customerrepository.getcustomerbyid ("120104198403082113");
Order order = Orderfactory.createneworder (customer, salesman); Order. Addneworderitem (Productrepository.getproductcatalogbyid (1). Getproductinstock (), 5100);
Orderrepository.addneworder (order);
Unitofwork.commitchanges (); }
After executing our test program, we can open SQL Server to see how code first handles the inheritance relationship between classes by default.
Code first maps all the properties of the base class and subclass to a column in a table, and adds an instance of the class that the Discriminator column identifies to be stored in.
If you don't like Discriminator, a strange name, you can define the name of the discriminator column and its type. We use the Map method to define the name and type of the column. We can name it title.
public class salespersonvalueobjectconfiguration:entitytypeconfiguration<salesperson> {
Public Salespersonvalueobjectconfiguration ()
{ Haskey (p = p.employeeid). Property (P = P.employeeid). Hasdatabasegeneratedoption (databasegeneratedoption.none); Property (P = p.name). IsRequired (). Hasmaxlength (100);
Property (P = p.gender). IsRequired (). Hasmaxlength (1);
Map<salesman> (salesman = {salesman. Requires ("Title"). HasValue ("salesman"); });
Map<salesmanager> (Manager = {manager). Requires ("Title"). HasValue ("Sales Manager"); }); } }
The type parameter passed in the map method is the class name of the subclass, requires is used to specify the name of the discriminator column, and HasValue is used to specify its type and the value corresponding to each subclass.
We can re-execute our test program and open SQL Server to see the New database table structure.
The type of this column can be not only a string, but also a bit flag bit, for example, we set the distinction between salesman and Salemanager column as bit type, the name of the column is called Ismanager.
Map<salesman> (salesman = {salesman. Requires ("Ismanager"). HasValue (FALSE); });
Map<salesmanager> (Manager = {manager). Requires ("Ismanager"). HasValue (TRUE); });
We only need to change the value passed in HasValue to true and False,code first will automatically set the type of Ismanager column to bit.
2.Table Per Type (TPT)
In this approach, Entity Framework Code first establishes a table for each base class and subclass that contains only the attributes that are unique to the subclass.
We can use the Map method to force code first to use TPT, because code first uses the TPC method by default.
public class salespersonvalueobjectconfiguration:entitytypeconfiguration<salesperson> { public Salespersonvalueobjectconfiguration () {
Property (P = p.name). IsRequired (). Hasmaxlength (100);
Property (P = p.gender). IsRequired (). Hasmaxlength (1);
Map<salesman> (salesman = {salesman. ToTable ("salesman"); }); Map<salesmanager> (Manager = {manager). ToTable ("Manager"); });
}
By using the TOtable method, let code first create a table for each subtype, the name of the table is the parameter value passed in the TOtable method, the primary key in the corresponding table of the subclass is the same as the primary key name in the table corresponding to the base class, and it is also a foreign key to the table corresponding to the base class.
We also used the test method above to test the structure of the data table that code first established in TPT.
3.Table Per Concrete Type (TPC)
In this approach, the Entity Framework Code first establishes a table for each subclass, and a table for the properties of the base class in the corresponding table of the child class, in addition to the attributes of the subclass.
Like TPT, we also need to set it through the map method.
public class salespersonvalueobjectconfiguration:entitytypeconfiguration<salesperson> {
Public salespersonvalueobjectconfiguration () {
Haskey (p = p.employeeid). Property (P = P.employeeid). Hasdatabasegeneratedoption (Databasegeneratedoption.none);
Property (P = p.name). IsRequired (). Hasmaxlength (100);
Property (P = p.gender). IsRequired (). Hasmaxlength (1);
Salesman. Mapinheritedproperties (); });
Map<salesmanager> (Manager = {manager). ToTable ("Manager"); Manager. Mapinheritedproperties (); }); } }
By using the Mapinheritedproperties method, you can force code first to use the TPC method.
After we recompile and execute our original test method, we can get a different data table structure, Code first does not create a table for the base class, but creates a table for each subclass, storing the contents of the subclass and the contents of the base class in the corresponding table for each subclass.
PS: If your base class is abstract, the effect is the same.
The last question to be explored is which method should we use in the actual project?
1. TPC (Type Per concrete type) is not recommended because an instance or instance collection of other classes contained in a neutron class in TPC mode cannot be mapped to a relationship between tables. You have to let code first perceive the relationship between them by manually adding the primary key properties of the dependent classes to the class, which is the opposite of the original intent of using code first.
2. In terms of query performance, TPH is better because all of the data exists in a table and does not need to be used for data queries.
3. From a storage space, TPT will be better because all the columns in the TPH are in one table, and the records in the table cannot use all the columns, so many columns have null values and a lot of storage space is wasted.
4. From the point of view of data validation, TPT is better, because the columns of many sub-class attributes in TPH are nullable, adding complexity to data validation.
Entity Framework Code First mapping inheritance relationships