Programming Entity Framework-dbContext 学习笔记第五章

### Programming Entity Framework-dbContext 学习笔记 第五章

将图表添加到Context中的方式及容易出现的错误
方法结果警告
Add Root图标中的所有实体将被跟踪,并标记为AddedSaveChage 将试图将所有实体插入数据库,即使数据库中已存在该实体
Attach Root所有实体将被跟踪并标记为Unchanged新添加的实体将不会被插入数据库,并容易造成主键冲突
Add or Attach Root,then paint state throughout graph所有的实体将拥有正确的状态值建议使用 Add Root 而不是Attach Root,以避免新实体的主键冲突

DbEntityEntry类型包含属性来记录实体各个状态的值:

  1. CurrentValues 包含实体所有属性的当前值。
  2. OriginalValues 包含实体属性未被修改前的值。
  3. GetDatabaseValues() 方法返回实体属性目前在数据库中的值。
    注:对于状态为Added 和 Deleted 的实体不包含后两个取值,试图读取时,将引发异常。

用于打印各个属性的代码:

private static void PrintPropertyValues(DbPropertyValues values) 
{
    foreach (var propertyName in values.PropertyNames)
    {
        Console.WriteLine(" - {0}: {1}", propertyName,values[propertyName]);  
    } 
}
Working with DbPropertyValues for Complex Types
  1. Code First 的默认约定是将不包含主键的实体理解为“复杂类型”,当需要将包含主键的实体定义为“复杂类型”时,可借助[ComplexType]特性进行标注。
  2. 当我们从DbPropertyValues 中获取的值为一个复杂对象时,它将被表示为一个新的 DbPropertyValues 对象。

    下面是一个读取含有复杂属性的 DbPropertyValues 的代码

private static void PrintPropertyValues(DbPropertyValues values, int indent = 1)
{
    foreach (var propertyName in values.PropertyNames)
    {
        var value = values[propertyName];
        if (value is DbPropertyValues)
        {
            Console.WriteLine("{0}- Complex Property: {1}", string.Empty.PadLeft(indent),propertyName);
            PrintPropertyValues((DbPropertyValues)value, indent + 1);
        }
        else
        {
            Console.WriteLine("{0}- {1}: {2}", string.Empty.PadLeft(indent), propertyName, values[propertyName]);
        }
    }
}
Copying the Values from DbPropertyValues into an Entity
  1. DbPropertyValues 包含一个 ToObject 方法,可以在不覆盖原有实例的情况下,将所有的值复制到一个新的实例对象。

    注:ToObject 方法只复制标量属性,忽略导航属性。

Changing Values in a DbPropertyValues
  1. DbPropertyValues 不是只读的,可以被修改。当你修改CurrentValues 的值的时候,将改变当前实例的值。

    修改将自动触发 Changes Tracking.

  2. Clone() 方法可以复制所有的DbPropertyValues ,新克隆出来的对象将不会被Change Tracker 跟踪。

Using the SetValues method
  1. 用户在修改了实体的属性值之后,想撤销所有的修改,最简单的方式就是利用 SetValues() 方法,将OriginalValues 中的数据拷贝到 CurrentValues 中。

    代码如下:

 entry.CurrentValues.SetValues(entry.OriginalValues); 
 entry.State = EntityState.Unchanged;//因为SetVaues方法不会自动修改实体的状态,所以需要手动修改。
  1. SetValues方法不尽可以接受DbPropertyValues做为参数,也可以接受其他类型,该方法会自动查找同名属性的值进行覆盖,找不到任何值时,引发异常。
  2. 可以利用SetValues方法实现实体对象的克隆。直接上代码:
private static void CreateDavesCampsite()
{
    using (var context = new BreakAwayContext())
    {
        //从数据库中读取d.Name == "Dave's Dump"的实体
        var davesDump = (from d in context.Lodgings where d.Name == "Dave's Dump" select d).Single();
        //新建实体
        var clone = new Lodging();
        //添加到追踪,这样才可以进行复制
        context.Lodgings.Add(clone);
        //复制,这里传入的就不是DbPropertyValues 的实例
        context.Entry(clone).CurrentValues.SetValues(davesDump);
        //修改名称
        clone.Name = "Dave's Camp";
        //保存
        context.SaveChanges();
        Console.WriteLine("Name: {0}", clone.Name); //output:Dave's Camp
        Console.WriteLine("Miles: {0}", clone.MilesFromNearestAirport); //32.65
        Console.WriteLine("Contact Id: {0}", clone.PrimaryContactId); //1
        //只有名称被修改了。
    }
}
Working with Individual Properties

你可以使用 Property, Complex, Reference, and Collection 方法去获取或者操作单独的属性:

  • Property 方法可以用来处理 Scalar 和 Complex 属性
  • Complex 方法提供对复杂属性的附加特殊操作
  • Reference 和 Collection 方法用于导航属性
  • 还有一个Member方法,可以用于任何类型的属性。该方法不是强类型的,仅提供对属性通用信息的访问
Working with Scalar Properties

Property方法可以访问属性的原始值,当前值,和是否被修改等信息,该方法具有弱类型和强类型的两个重载。直接上代码:

private static void WorkingWithPropertyMethod()
{
    using (var context = new BreakAwayContext())
    {
        var davesDump = (from d in context.Lodgings where d.Name == "Dave's Dump" select d).Single();
        //此处会调用强类型的泛型方法,所以后面可以使用lambda表达式
        var entry = context.Entry(davesDump);
        //使用lambda表达式访问Name属性
        entry.Property(d => d.Name).CurrentValue = "Dave's Bargain Bungalows";
        Console.WriteLine("Current Value: {0}", entry.Property(d => d.Name).CurrentValue);
        Console.WriteLine("Original Value: {0}", entry.Property(d => d.Name).OriginalValue);
        Console.WriteLine("Modified?: {0}", entry.Property(d => d.Name).IsModified);
    }
}

输出结果:
Current Value: Dave's Bargain Bungalows
Original Value: Dave's Dump
Modified?: True

Working with Complex Properties
  1. 提供对复杂属性的操作。

Example 5-20. Accessing change tracking information for a complex property.

private static void WorkingWithComplexMethod()
{
    using (var context = new BreakAwayContext())
    {
        //从数据库检索实体
        var julie = (from p in context.People where p.FirstName == "Julie" select p).Single();
        //获取Entry
        var entry = context.Entry(julie);
        //操作复杂属性,这里使用了Property方法操作复杂属性的属性。
        entry.ComplexProperty(p => p.Address).Property(a => a.State).CurrentValue = "VT";
        //以上方法可以用以下方法代替
        entry.Property(p => p.Address.State).CurrentValue = "VT";
        //又或者
        entry.Property("Address.State").CurrentValue = "VT";

        Console.WriteLine("Address.State Modified?: {0}", entry.ComplexProperty(p => p.Address).Property(a => a.State).IsModified); //true
        Console.WriteLine("Address Modified?: {0}", entry.ComplexProperty(p => p.Address).IsModified); //true
        //链式调用访问复杂属性中包含的复杂属性
        Console.WriteLine("Info.Height.Units Modified?: {0}", entry.ComplexProperty(p => p.Info).ComplexProperty(i => i.Height).Property(h => h.Units).IsModified);//false
    }
}

当操作复杂属性的时候,Entity Framework 会跟踪它的状态变化,但是不会追踪它的个别属性的变化,当修改其中任一个单独的属性时,所有属性的状态将变为Modified.

  1. 可以修改复杂属性的值
entry.ComplexProperty(p => p.Address).CurrentValue = new Address { State = "VT" };

该操作将会把整个复杂属性标记为:Modified.

Working with Navigation Properties

Reference and Collection 方法被用户访问导航属性

  • Reference 方法用于访问单个实体
  • Collection 方法用于访问集合属性

这些方法提供以下功能:

  1. 读写导航属性的当前值
  2. 从数据库加载关联数据
  3. 获取导航属性代表的查询(query)
Modifying the value of a navigation property

Example 5-21. Change tracking information for a reference navigation property

private static void WorkingWithReferenceMethod()
{
    using (var context = new BreakAwayContext())
    {
        //从数据库检索Name == "Dave's Dump"的实体对象
        var davesDump = (from d in context.Lodgings where d.Name == "Dave's Dump" select d).Single();
        //获取entry
        var entry = context.Entry(davesDump);
        //获取并加载Destination导航属性,Load之前改属性为null
        entry.Reference(l => l.Destination).Load();
        var canyon = davesDump.Destination;
        //输出导航属性destination 属性Name的当前值
        Console.WriteLine("Current Value After Load: {0}", entry.Reference(d => d.Destination).CurrentValue.Name);
        //从数据库检索 Name == "Great Barrier Reef" 的Destination 对象
        var reef = (from d in context.Destinations where d.Name == "Great Barrier Reef" select d).Single();
        //将导航属性修改为 reef
        entry.Reference(d => d.Destination).CurrentValue = reef;
        //输出修改后导航属性destination 属性Name的值
        Console.WriteLine("Current Value After Change: {0}", davesDump.Destination.Name);
    }
}

输出结果:
Current Value After Load: Grand Canyon Current Value
After Change: Great Barrier Reef

Modifying navigation properties with the change tracker

之前,我们在处理标量属性的时候,我们发现,当我们通过Change Tracker修改属性时,不必显示的调用DetectChanges()方法。改变就被跟踪到了。
对于导航属性,同样如此!

Working with collection navigation properties

Example 5-22. Method to explore interacting with a collection property

private static void WorkingWithCollectionMethod()
{
    using (var context = new BreakAwayContext())
    {
        //从数据库检索 Description == "Trip from the database" 的Trip对象
        var res = (from r in context.Reservations where r.Trip.Description == "Trip from the database" select r).Single();
        var entry = context.Entry(res);
        //获取并加载集合导航属性
        entry.Collection(r => r.Payments).Load();
        //输出导航属性包含记录数
        Console.WriteLine("Payments Before Add: {0}", entry.Collection(r => r.Payments).CurrentValue.Count);
        //添加一条新记录
        var payment = new Payment { Amount = 245 };
        //添加到Payments集合,确认被跟踪,这里容易出坑
        context.Payments.Add(payment);
        //将新对象添加到导航属性
        entry.Collection(r => r.Payments).CurrentValue.Add(payment);
        //输出导航属性包含记录数
        Console.WriteLine("Payments After Add: {0}", entry.Collection(r => r.Payments).CurrentValue.Count);
    }
}

输出结果:
Payments Before Add: 1
Payments After Add: 2

和之前的其他属性不同,修改集合导航属性后必须手动调用DetectChanges()方法来跟踪变化

Refreshing an Entity from the Database

Entity Framework 的 DbEntityEntry 对象包含Reload 方法来从数据库中加载最新数据。

Example 5-23. Reloading an entity from the database

private static void ReloadLodging()
{
    using (var context = new BreakAwayContext())
    {
        //从数据库检索数据
        var hotel = (from d in context.Lodgings where d.Name == "Grand Hotel" select d).Single();
        //使用原始的SQL语句修改数据库中的值
        context.Database.ExecuteSqlCommand(@"UPDATE dbo.Lodgings  SET Name = 'Le Grand Hotel' WHERE Name = 'Grand Hotel'");

        Console.WriteLine("Name Before Reload: {0}", hotel.Name);
        Console.WriteLine("State Before Reload: {0}", context.Entry(hotel).State);
        //重新加载数据
        context.Entry(hotel).Reload();
        Console.WriteLine("Name After Reload: {0}", hotel.Name);
        Console.WriteLine("State After Reload: {0}", context.Entry(hotel).State);
    }
}

数据结果

Name Before Reload: Grand Hotel
State Before Reload: Unchanged
Name After Reload: Le Grand Hotel
State After Reload: Unchanged

如果在从新加载数据之前对实体进行了修改 比如

hotel.Name = "A New Name";  

输出结果将变为:

Name Before Reload: A New Name
State Before Reload: Modified
Name After Reload: Le Grande Hotel
State After Reload: Unchanged

Change Tracking Information and Operations for Multiple Entities

前面操纵的都是单个的实体,接下来我们介绍DbContext.ChangeTracker.Entries 方法,该方法包含两个
重载,一个是泛型的,一个是非泛型的。泛型的方法返回指定类型的记录结合。非泛型的重载
返回一个DbEntityEntry类型的集合,包含所有被追踪的实体。

Example 5-24. Iterating over all entries from the change tracker

private static void PrintChangeTrackerEntries()
{
    using (var context = new BreakAwayContext())
    {
        //从数据库检索Description == "Trip from the database"的Reservations类型的对象
        var res = (from r in context.Reservations where r.Trip.Description == "Trip from the database" select r).Single();
        //加载它的集合属性 Payments
        context.Entry(res).Collection(r => r.Payments).Load();
        //添加一个新的Payment
        res.Payments.Add(new Payment { Amount = 245 });
        //使用非泛型的方法返回所有被追踪的对象
        var entries = context.ChangeTracker.Entries();
        //迭代输出类型和状态
        foreach (var entry in entries)
        {
            Console.WriteLine("Entity Type: {0}", entry.Entity.GetType());
            Console.WriteLine(" - State: {0}", entry.State);
        }
    }
}

输出结果

Entity Type: Model.Payment
- State: Added
Entity Type: Model.Reservation
- State: Unchanged
Entity Type: Model.Payment
- State: Unchanged

Using the Change Tracker API in Application Scenarios
Resolving Concurrency Conflicts

默认情况下 Entity Framework 总是更新所有的改变,而不管是不是存在并发冲突,但是你可以
配置你的Model,当并发冲突发生时,抛出一个异常。你可以将一个特定的属性指定为 concurrency token

转载于:https://www.cnblogs.com/jameszh/p/6715460.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值