2013年8月21日星期三

EF in-one, one-to-many relationship between the configuration andcascading deletes

 EF

beginning of this chapter to understand the various relationships. If you EF various relationships between the entities is not very familiar, you can see my thinking, can help you quickly understand.

 

I.-one relationship between the entities

 

we add a PersonPhoto class that represents the user's avatar informational

 
  
    /// <summary> 
/// 用户头像类
/// </summary>
public class PersonPhoto
{
[Key]
public int PersonId { get; set; }
public byte[] Photo { get; set; }
public string Caption { get; set; } //标题
public Person PhotoOf { get; set; }
}
 
 

course, also need to add PersonPhoto Person class navigation properties, representation and PersonPhoto one relationship:

 
  
public PersonPhoto Photo { get; set; }
 
 

directly run the program will be reported a fault:

 

Unable to determine the principal end of an association between the types 'Model.Per-sonPhoto' and 'Model.Person'. The principal end of this association must be explicitly configured using either the relationship fluent API or data annotations. < / p>  

thinking: Why the first section of Destination and Lodging categories directly in the class plus a navigation property can generate foreign key relationship not do this now?
Answer: Before the Destination and Lodging article is to-many relationship, since it is one to many, EF naturally know to set Destination class DestinationId primary key, and set Lodging class of DestinationId for the foreign key; But now This Person class and PersonPhoto class is one to one relationship, if not manually specified, then EF certainly do not know what to set the primary key which is a foreign key, this is not difficult to understand. In accordance with the logic of the Person class PersonId certainly is the primary key, and direct labeling [ForeignKey ("PhotoOf")] can be, which is Data Annotation mode configuration, we can naturally also Fluent API that I prefer this way.

 

talking Fluent API how to configure the Person class and PersonPhoto to-one relationship, we first systematic study under EF Lane entity relationship configurations. EF in the entity-relationship configuration divides Has methods and With Series: Optional Optional, Required necessary, Many more. For example:

 
  
A.HasRequired(a => a.B).WithOptional(b => b.A);
 
 

added: Here's a => aB lambda expression is written, is to find the property B mean. Naming a not fixed, free, q => qB is also possible. But B is a Class A property, so accustomed to using lowercase a.

 

Has Methods:

 
      
  1. HasOptional: the former includes the latter as an instance or null
  2.   
  3. HasRequired: the former (A) includes the latter (B) an instance is not null
  4.   
  5. HasMany: the former includes the latter a collection of instances
  6.  
 

With Methods:

 
      
  1. WithOptional: the latter (B) may include the former (A) an instance or null
  2.   
  3. WithRequired: the latter contains the former one instance is not null
  4.   
  5. WithMany: the latter contains a collection of the former instance
  6.  
 

From here This is relatively good understanding of way. The above configuration means a Class A Class B contains an instance is not null, B class contains an instance of class A, it can not contain. One most standard configuration.

 

ok, now look at our Person class and upper class one to one relationship PersonPhoto how to configure:

 
  
this.HasRequired(p => p.PhotoOf).WithOptional(p => p.Photo);
 
 

pitted under the program, the database is generated, is a one to one relationship. Person table can not have a corresponding PersonPhoto table data, but PersonPhoto table data must correspond to each one a Person table data. Meaning that people may not have photos, but some photographs must belong to somebody. Relations configuration is such an effect, in fact, can easily be changed, can also be configured Everyone must have a corresponding photo. The above WithOptional into WithRequired, corresponding to the database is null into a not null.

 

thinking: We do not add an entity as before class on the same time added to the BreakAwayContext class, but why can still generate PersonPhotos in a database table?
Answer: Add to BreakAwayContext class is to allow database context to track to this class, to facilitate CRUD (add change search deleted). Here we do not put PersonPhoto class to BreakAwayContext class because we and not to separate CRUD PersonPhoto class of PersonPhoto class Person class operations are to go first, and then through one to one relationship found PersonPhoto class, the more realistic. The database can generate PersonPhotos on a better understanding, because we have this entity class thing.

continue to think: I am sure you would want to be here, if only need to add the primary key class to BreakAwayContext class, then what other many, many relationships are not all as long as plus the primary key table to BreakAwayContext class do?
continue to answer: still need to consider the actual situation, the above PersonPhoto class already explained, the actual situation is unlikely to operate independently PersonPhoto class. Accommodation in Logding many relationship class is the foreign key table, you must also know that this Think tracking database context to make, because we're too may operate directly Lodging Accommodation type it. Like the receptionist add a search function accommodations, it is not necessary to directly manipulate foreign key table of it? Certainly need it. They still need to consider the actual situation. This is only personal opinion, if flawed, kindly correct me.

 

 

II. many relationship between entities

 

previous article, attractions and accommodation class class Destination Lodging many relationship, this is well understood: an attraction that there are multiple places to stay, and a place to stay only belong to one attraction. Of course you can not, an interest that is not a place to stay on, a place to stay does not belong to any attractions, this is also possible. Before the program is not part of each other, can all be empty. Let configuration must belong to a place to stay Attractions:

 

Data Annotations

 

directly in the accommodation category Lodging navigation properties to add [Required] annotation can:

 
  
[Required] 
public Destination Destination { get; set; }
 
 

Fluent API

 
  
this.HasMany(d => d.Lodgings).WithRequired(l => l.Destination).Map(l => l.MapKey("DestinationId"));
 
 

This line is written in the DestinationMap class, corresponding to the above description, the former is the Destination, the latter Lodging. The sentence means: Destination class contains multiple (HasMany) Lodging collection class instance, Lodging class contains the former one is not null (WithRequired) instance. . MapKey is to specify the name of the foreign key. Stay here if you do not have to belong to a class attractions, then you can directly WithRequired replaced WithOptional. When the query former uses Inner join, which uses the Left join. Understand Inner, Left and Cross Join differentiated point here

 

above is Destination for the former, of course, we can also Lodging for the former, go LodgingMap write down the following configuration, in fact, a meaning:

 
  
this.HasRequired(d => d.Destination).WithMany(l => l.Lodgings).Map(l => l.MapKey("DestinationId"));
 
 

heavy ran down procedures, the resulting database Lodging table foreign key is already set up as a non-empty, and the foreign key name is specified in the "DestinationId":

 

 

official explanation is given so many, in fact, not easy to understand my explanation, send a map that you feel under the bar:

 

 

ok, above said many relationship, is the standard-to-many relationship between two tables were navigation properties. But if the column does not follow this rule?
we add a new class InternetSpecial, record some accommodation with the usual price is not the same class, holidays and so on. This class is not only a navigation property Accommodation, there are primary key columns AccommodationId:

 
  
    /// <summary> 
/// 住宿特殊价格类(节假日等)
/// </summary>
public class InternetSpecial
{
public int InternetSpecialId { get; set; }
public int Nights { get; set; } //几晚
public decimal CostUSD { get; set; } //价钱
public DateTime FromDate { get; set; }
public DateTime ToDate { get; set; }

public int AccommodationId { get; set; }
public Lodging Accommodation { get; set; }
}
 
 

while giving your class Lodging add a InternetSpecial class navigation attributes:

 
  
public List<InternetSpecial> InternetSpecials { get; set; }
 
 

configured to run under the program to generate database table:

 

 

seen from the table, not only AccommodationId column, there is a foreign key column Accommodation_LodgingId, obviously this is because we do not set the foreign key reasons, EF did not know what we have to give the foreign key property when we were using the Data Annotation and Fluent API set to try:

 

Data Annotation:

 
  
[ForeignKey("Accommodation")] 
public int AccommodationId { get; set; }
 
 

Or this:

 
  
[ForeignKey("AccommodationId")] 
public Lodging Accommodation { get; set; }
 
 

Fluent API:

 
  
this.HasRequired(s => s.Accommodation) 
.WithMany(l
=> l.InternetSpecials)
.HasForeignKey(s
=> s.AccommodationId); //外键
//如果实体类没定义AccommodationId,那么可以使用Map方法直接指定外键名:.Map(s => s.MapKey("AccommodationId"))
 
 

this is not explained in detail, if they do not understand, look at the article and the beginning of my analysis Has With series method. Configured, we re-ran down the program, the foreign key is AccommodationId, no extra Accommodation_LodgingId out the.

 

 

III. many relationship between entities

 

we add an event class Activity, Class Trip with travel many relationship. This is not difficult to understand: a trip with multiple activities, an activity can belong to multiple trips.

 
  
    /// <summary> 
/// 活动类
/// </summary>
public class Activity
{
public int ActivityId { get; set; }
//[Required, MaxLength(50)]
public string Name { get; set; }

public List<Trip> Trips { get; set; } //和Trip类是多对多关系
}
 
 

did before, we need to add in BreakAwayContext class Activity class, so that the database context to know the Activity class:

 
  
public DbSet<CodeFirst.Model.Activity> Activitys { get; set; }
 
 

while Trip Travel category to add navigation properties on the form with the Activity ACTIVITY class-many relationship

 
  
 public List<Activity> Activitys { get; set; }
 
 

ok, already, and we ran down the procedures are as follows database:

 

 

can be seen, EF Lane-many relationship is determined by the third table to connect the two tables. ActivityTrips tables and table joins the Activityes Trips tables. Table names are ranked default naming, we can configure their own, beginning of the article has been said so much, many-certainly with HasMany and WithMany methods, ActivityMap class wrote the following Fluent API:

 
  
            this.HasMany(a => a.Trips).WithMany(t => t.Activitys).Map(m => 
{
m.ToTable(
"TripActivities"); //中间关系表表名
m.MapLeftKey("ActivityId"); //设置Activity表在中间表主键名
m.MapRightKey("TripIdentifier"); //设置Trip表在中间表主键名
});
 
 

Similarly, we can also TripMap in configuration, the order is not the same Bale:

 
  
            this.HasMany(t => t.Activities).WithMany(a => a.Trips).Map(m => 
{
m.ToTable(
"TripActivities"); //中间关系表表名
m.MapLeftKey("TripIdentifier"); //设置Activity表在中间表的主键名
m.MapRightKey("ActivityId"); //设置Trip表在中间表的主键名
});
 
 

two only need to configure one can. Re-run the next program. The effect is what we want. Configured, we in the program on how to read the data on it more simple to write one:

 
  
var tripWithActivities = context.Trips.Include("Activities").FirstOrDefault();
 
 

obviously used the Include greedy load the related foreign key table data (if any) also got a memory:

 

 

is not also need to consider the performance of the problem then? Naturally, if we only need to modify the primary key of a property, and that the foreign key data lazy to do? Will send a lot of redundant sql to the database. Of course, if you want to modify the foreign key database, so loading is a good thing, super convenient. EF group's original words were: Entity Framework took care of the joins to get across the join table without you having to be aware of its presence. In the same way, any time you do inserts, updates, or deletes within this many-to -many relationship, Entity Framework will work out the proper SQL for the join without you having to worry about it in your code.
means that if you have configured a primary foreign key relationship, EF will help you generate the appropriate Lian table query (join) sql, will not bother you more. About-to-many, many-EF query and efficiency issues, follow-up will be a special series of articles explaining.

 

 

IV. cascade delete

 

EF configured by default to all foreign key relationships are cascading deletes, the meaning is to remove the primary key, foreign key values ​​are associated automatically deleted:

 

 

To demonstrate, we add a method:

 
  
        //级联删除(服务端延迟加载) 
private static void DeleteDestinaInMemoryAndDbCascade()
{
int destinationId;
using (var context = new CodeFirst.DataAccess.BreakAwayContext())
{
var destination = new CodeFirst.Model.Destination
{
Name
= "Sample Destination",
Lodgings
= new List<CodeFirst.Model.Lodging>
{
new CodeFirst.Model.Lodging {Name="Lodging One"},
new CodeFirst.Model.Lodging {Name="Lodging Two"}
}
};
context.Destinations.Add(destination); //添加测试数据
context.SaveChanges();
destinationId
= destination.DestinationId; //记住主键id
}
using (var context = new CodeFirst.DataAccess.BreakAwayContext())
{
//这里用了贪婪加载,把主键和相关的外键记录都加载到内存中了
var destination = context.Destinations.Include("Lodgings").Single(d => d.DestinationId == destinationId);
var aLodging = destination.Lodgings.FirstOrDefault();
context.Destinations.Remove(destination);

context.SaveChanges();
}
}
 
 

very simple, add a primary key record Sample Destination, while adding this primary key-based two foreign key record: Lodging One and Lodging Two, namely: Added a tourist attraction, but also added this tourist attraction under Two places to stay. After the delay loading the primary key of the two records and related records and delete the foreign key, we can use sql profiler to monitor the following sql:

 

 

The first is to delete the primary key record is deleted after two foreign key records sql. Clear point that this does not use cascading deletes to the database, because the foreign key record delete program instructions are sent to the database. So wrote the above method is not good, because we use a greedy load (eager Loading), which the Include method. Load primary key Destination class, while the two foreign keys Include record is also loaded into memory, and then perform all three records deletion. In fact, we only need to load the primary key record into memory can be, because the database has been opened cascading deletes, we only need to send a command to delete the primary key record to the database, the database will automatically help us remove the associated foreign key records. We can monitor the following sql:

 
        
   
exec sp_executesql N'SELECT  
[Project2].[DestinationId] AS [DestinationId],
[Project2].[Name] AS [Name],
[Project2].[Country] AS [Country],
[Project2].[Description] AS [Description],
[Project2].[image] AS [image],
[Project2].[C1] AS [C1],
[Project2].[LodgingId] AS [LodgingId],
[Project2].[Name1] AS [Name1],
[Project2].[Owner] AS [Owner],
[Project2].[IsResort] AS [IsResort],
[Project2].[MilesFromNearestAirport] AS [MilesFromNearestAirport],
[Project2].[PrimaryContact_PersonId] AS [PrimaryContact_PersonId],
[Project2].[SecondaryContact_PersonId] AS [SecondaryContact_PersonId],
[Project2].[DestinationId1] AS [DestinationId1]
FROM ( SELECT
[Limit1].[DestinationId] AS [DestinationId],
[Limit1].[Name] AS [Name],
[Limit1].[Country] AS [Country],
[Limit1].[Description] AS [Description],
[Limit1].[image] AS [image],
[Extent2].[LodgingId] AS [LodgingId],
[Extent2].[Name] AS [Name1],
[Extent2].[Owner] AS [Owner],
[Extent2].[IsResort] AS [IsResort],
[Extent2].[MilesFromNearestAirport] AS [MilesFromNearestAirport],
[Extent2].[PrimaryContact_PersonId] AS [PrimaryContact_PersonId],
[Extent2].[SecondaryContact_PersonId] AS [SecondaryContact_PersonId],
[Extent2].[DestinationId] AS [DestinationId1],
CASE WHEN ([Extent2].[LodgingId] IS NULL) THEN CAST(NULL AS int) ELSE 1 END AS [C1]
FROM (SELECT TOP (2)
[Extent1].[DestinationId] AS [DestinationId],
[Extent1].[Name] AS [Name],
[Extent1].[Country] AS [Country],
[Extent1].[Description] AS [Description],
[Extent1].[image] AS [image]
FROM [dbo].[Destinations] AS [Extent1]
WHERE [Extent1].[DestinationId] = @p__linq__0 ) AS [Limit1]
LEFT OUTER JOIN [dbo].[Lodgings] AS [Extent2] ON [Limit1].[DestinationId] = [Extent2].[DestinationId]
) AS [Project2]
ORDER BY [Project2].[DestinationId] ASC, [Project2].[C1] ASC
',N'@p__linq__0 int',@p__linq__0=3
  
   View Code  
 

execute queries directly to the database and found that it will return a primary key record and two foreign key record. Unless we have to find out before using the Include foreign key record greedy load, or do not, EF ado in with us before hand is not the same, it is easy to generate very redundant sql. Here, we really only need a primary key of the record on it, modify the following methods:

 
  
     //级联删除(仅加载主键记录) 
private static void DeleteDestinationInMemeryAndDbCascade()
{
int destinationId;
using (var context = new CodeFirst.DataAccess.BreakAwayContext())
{
var destination = new CodeFirst.Model.Destination
{
Name
= "Sample Destination",
Lodgings
= new List<CodeFirst.Model.Lodging>
{
new CodeFirst.Model.Lodging {Name="Lodging One"},
new CodeFirst.Model.Lodging {Name="Lodging Two"}
}
};
context.Destinations.Add(destination);
context.SaveChanges();
destinationId
= destination.DestinationId;
}
using (var context = new CodeFirst.DataAccess.BreakAwayContext())
{
var destination = context.Destinations
.Single(d
=> d.DestinationId == destinationId); //只取一条主键记录
context.Destinations.Remove(destination); //然后移除主键记录,外键记录又数据库级联删除
context.SaveChanges();
}

}
 
 

monitor sql clean, it will only detect the primary key record.

 
  
exec sp_executesql N'SELECT TOP (2)  
[Extent1].[DestinationId] AS [DestinationId],
[Extent1].[Name] AS [Name],
[Extent1].[Country] AS [Country],
[Extent1].[Description] AS [Description],
[Extent1].[image] AS [image]
FROM [dbo].[Destinations] AS [Extent1]
WHERE [Extent1].[DestinationId] = @p__linq__0
',N'@p__linq__0 int',@p__linq__0=1
 
 

added: here only check a record is to use SELECT TOP (2) ... is to ensure that records can be found.

 

delete sql cleaner, delete only the primary key record, the related foreign key to delete the database cascade delete completed:

 
  
exec sp_executesql N'delete [dbo].[Destinations] 
where ([DestinationId] = @0)
',N'@0 int',@0=1
 
 

cascading deletes Although convenient, but it is not commonly used. Imagine our blog wrote many essays garden for different essays plus a good distinction between different labels and management. Day before we found unreasonable given a label, but this label is already in use in many essays, and if this time we remove a label, database cascade essays to mark this label are deleted, this is definitely inappropriate . Tag should be deleted, before the label affixed to the article had gone to this label, the only logical.

 

database settings can be visualized without cascading deletes, Fluent API configuration key relationship can not set cascade delete:

 
  
this.HasMany(d => d.Lodgings).WithRequired(l => l.Destination) 
.Map(l
=> l.MapKey("DestinationId")) //一对多并指定外键名
.WillCascadeOnDelete(false); // 关闭级联删除
 
 

pitted under the program, go to the next database naturally gone the foreign key cascade delete.

 
  @ Guoming Feng: Good article, long time no see such a good EF articles, and recommended   
EF cascade delete enabled by default, the setting is really quite messed up, so my approach is the method in the context of OnModelCreating   
modelBuilder.Conventions.Remove ();   
remove this default convention, then the need to open the cascade delete FluentAPI relational mapping used. WillCascadeOnDelete (true) to open a separate  
 

Park Friends Guoming Feng provides a good suggestion: Considering the EF The cascading deletes are not commonly used, so it can be turned off in all primary in the global foreign key relationships cascading deletes, if you need to open a primary foreign key cascade delete.

 

ok, this concludes this article, there are more user-friendly follow-up article with you to understand EF, please stay tuned. This chapter source

 

 
   series of articles Navigation :   
       
  1. EF Code First First Experience
  2.    
  3. EF's default mapping and how to use Data Annotations and the Fluent API configuration database mapping
  4.    
  5. EF Lane Guid types of data from the growth in the use of timestamps and complex types
  6.    
  7. EF Lane one on one, many, many relationships configuration and cascade delete
  8.    
  9. EF Lane inheritance mappings TPH, TPT and TPC explanations as well as some specific Examples
  10.   
 

没有评论:

发表评论