Entity Framework 4.1/4.3 之六 (DBContext 之 3 状态跟踪)
咱们接着来讲DBContext,这回内容将与DBContext 2中所讲的内容连贯起来。
二、DBContext对于实体的增、删、改 (Adding, Changing, and Deleting Entities)
DBContext 2中我们讲到了“增、删、改”,其中已经讲了“增、删、改”。还有部分内容没讲,我们补全。
(1)、查找增加模式 (The “Find or Add” Pattern)
场景:我们常常会增加一条记录,增加的步骤是先查找有没有相同记录存在,如果不存在内里将新记录新增到数据库中。在老版EF中我们会执行两次操作:即先找,在增。这无形中要与数据库交互两。
DBContext为我们提供了新的解决方案,即Find or Add,我们来看看代码:
private static void FindOrAddPerson() { using (var context = new BreakAwayContext()) { var ssn = 123456789; var person = context.People.Find(ssn)?? context.People.Add(new Person { SocialSecurityNumber = ssn, FirstName = "jerry", LastName = "tom" }); Console.WriteLine(person.FirstName); } }
代码看起来非常的直观,如果Find(ssn)在缓存或者数据库中找到了该记录,则输出,否则则新增一条记录。
(2)、新增关系数据(Adding a Relationship Between Objects)
说实话,这个理解起来有点绕,不好解释。我们还是直接来看下面的代码吧!
private static void NewGrandCanyonResort() { using (var context = new BreakAwayContext()) { var resort = new Resort { Name = "Pete's Luxury Resort" }; context.Lodgings.Add(resort); var canyon = (from d in context.Destinations where d.Name == "Grand Canyon"
select d).Single(); canyon.Lodgings.Add(resort); context.SaveChanges(); } }
Destinations 与 Lodgings 是一对多的关系,首先我们创建一个 resort 对象,将它加入到 Lodgings 集合中。然后我们找到父集合Destinations ,将resort 对象加入到Destinations包含的Lodgings集合中,然后SaveChanges,这种关联关系就建立起来了,相应的数据也保存到了数据库中。就是这么个意思,通俗的讲,就是先在从表里增加一条记录,然后将新增的记录与主表关系起来。你明白了吗?
这里我要强调一点,两表关联,对应该的关系ID要统一。比如主表中的主键是 classId ,那么相关系的从表关联id 也应该是classId,如果不统一的话,EF很难自动进行Map,除非人为的在Map文件中重改映射关系。
(3)、修改关系数据 (Changing a Relationship Between Objects)
private static void ChangeLodgingDestination() { using (var context = new BreakAwayContext()) { var hotel = (from l in context.Lodgings where l.Name == "Grand Hotel"
select l).Single(); var reef = (from d in context.Destinations where d.Name == "Great Barrier Reef"
select d).Single(); hotel.Destination = reef; context.SaveChanges(); } }
这是生成Sql语句:
exec sp_executesql N'update [dbo].[Lodgings]
set [destination_id] = @0
where ([LodgingId] = @1)
',N'@0 int,@1 int',@0=4,@1=1
这里Lodgings依然扮演着从表的角色,代码暂示了主从表关系的变更。当然,这只是DBContext中给我们提供的一个解决方案。我个人习惯直接修改关联关系ID的值。
(4)、删除关系(Removing a Relationship Between Objects)
private static void RemovePrimaryContact() { using (var context = new BreakAwayContext()) { var davesDump = (from l in context.Lodgings where l.Name == "Dave's Dump"
select l).Single(); context.Entry(davesDump).Reference(l => l.PrimaryContact).Load(); davesDump.PrimaryContact = null; context.SaveChanges();
} }
private static void RemovePrimaryContact() { using (var context = new BreakAwayContext()) { var davesDump = (from l in context.Lodgings where l.Name == "Dave's Dump"
select l).Single(); davesDump.PrimaryContactId = null; context.SaveChanges(); } }
删除关系没什么好讲的,一种方式是关联子集置空,一种方式是将关联id置空。
(5)、简单描述一下跟踪状态,这里只是简单的了解,后续会有专门的详解。(Working with Change Tracking)
状态清单:• DbSet.Add
• DbSet.Find
• DbSet.Remove
• DbSet.Local
• DbContext.SaveChanges
• Running any LINQ query against a DbSet
• DbSet.Attach
• DbContext.GetValidationErrors
• DbContext.Entry
• DbChangeTracker.Entries
正是有了这些跟踪状态(Tracking),SaveChanges才知道它应该干些什么。
DetectChanges:变化检测(Controlling When DetectChanges Is Called) 控制程序将在 变化检测时被触发。
大多数情况下(或者说大多说时间),在SaveChanges期间 Entity Framework 都需要知道 Tracking Change(即跟踪状态的改变情况)。 这里有个不太好理解的定义:Automatic DetectChanges 下面是一段应用代码,因为目前我对Automatic DetectChanges还不太准确。所以只把代码展示出来,大家分析分析。
private static void ManualDetectChanges() { using (var context = new BreakAwayContext()) { context.Configuration.AutoDetectChangesEnabled = false; var reef = (from d in context.Destinations where d.Name == "Great Barrier Reef"
select d).Single(); reef.Description = "The world's largest reef."; Console.WriteLine( "Before DetectChanges: {0}", context.Entry(reef).State); context.ChangeTracker.DetectChanges(); Using Snapshot Change Tracking | 61 Console.WriteLine( "After DetectChanges: {0}", context.Entry(reef).State); } }
private static void AddMultipleDestinations() { using (var context = new BreakAwayContext()) { context.Configuration.AutoDetectChangesEnabled = false; context.Destinations.Add(new Destination { Name = "Paris", Country = "France" });
context.Destinations.Add(new Destination { Name = "Grindelwald", Country = "Switzerland" }); context.Destinations.Add(new Destination { Name = "Crete", Country = "Greece" }); context.SaveChanges(); } }
我总觉得这个很难理解。
(6)、使用detectchanges触发关系 (Using DetectChanges to Trigger Relationship Fix-up) (这个也是我目前不太理解的,下面是实例中的代码)
private static void DetectRelationshipChanges() { using (var context = new BreakAwayContext()) { var hawaii = (from d in context.Destinations where d.Name == "Hawaii"
select d).Single(); var davesDump = (from l in context.Lodgings where l.Name == "Dave's Dump"
select l).Single(); context.Entry(davesDump) .Reference(l => l.Destination) .Load(); hawaii.Lodgings.Add(davesDump); Console.WriteLine( "Before DetectChanges: {0}", davesDump.Destination.Name); context.ChangeTracker.DetectChanges(); Console.WriteLine( "After DetectChanges: {0}", davesDump.Destination.Name); } }
希望代码能让我们明白其中的道理。
(7)、启用和使用更改跟踪代理 (Enabling and Working with Change Tracking Proxies)
(8)、确保新实例获取代理 (Ensuring the New Instances Get Proxies)
(9)、创建派生类型的代理实例(Creating Proxy Instances for Derived Types)
(10)、不更改跟踪获取实体 (Fetching Entities Without Change Tracking)
小述:6、7、8、9、10 这几种方式我没有用到,也不知道他们有什么好处和优点。如果大家了解的话,请告诉我。我先将自己用过和理解的写错来。对于一些未知的领域,也希望大家不吝指教。好了,就写到这里,我是百灵。
百灵注:本文版权由百灵和博客园共同所有,转载请注明出处。
助人等于自助! [email protected]