[索引页]
[×××]


再接再厉VS 2008 sp1 + .NET 3.5 sp1(5) - Entity Framework(实体框架)之ObjectContext


作者:webabcd


介绍
以Northwind为示例数据库,ADO.NET Entity Framework之详解ObjectContext, 以及事务和并发
  • ObjectContext - 以对象(这些对象是 EDM 中定义的实体类型的实例)的形式与数据进行交互
  • CreateObjectName - 实体类 的 CreateObjectName 静态方法用于创建实体类的新实例
  • AddToEntitySetName() - 将需要添加的对象添加到对象上下文中
  • SaveChanges() - 将所有更新保存到相关存储区中
  • Attach()/AttachTo() - 附加外部实体到上下文中
  • ObjectContext.Refresh() - 更新上下文数据
  • ObjectStateEntry - 维护实体状态的类
  • ObjectStateManager - 实体状态管理器


示例
1、详解ObjectContext
ObjectContext.aspx
<%@ Page Title="ObjectContext" Language="C#" MasterPageFile="~/Site.master" AutoEventWireup="true"
        CodeFile="ObjectContext.aspx.cs" Inherits="EntityFramework_ObjectContext" %>

<asp:Content ID="Content1" ContentPlaceHolderID="head" runat="Server">
</asp:Content>
<asp:Content ID="Content2" ContentPlaceHolderID="ContentPlaceHolder1" runat="Server">
        <div id="result" runat="server" />
</asp:Content>
 
ObjectContext.aspx.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;

using System.Data.Objects;
using System.Data.Objects.DataClasses;
using System.Data;

using VS2008SP1.Business;

public partial class EntityFramework_ObjectContext : System.Web.UI.Page
{
void Page_Load() void Page_Load(object sender, EventArgs e)
        {
                if (!Page.IsPostBack)
                {
                        Demo();

                        result.InnerHtml += "<br />";

                        Demo2();

                        result.InnerHtml += "<br />";

                        Demo3();

                        result.InnerHtml += "<br />";

                        Demo4();

                        result.InnerHtml += "<br />";

                        Demo5();

                        result.InnerHtml += "<br />";

                        Demo6();
                }
        }

void Demo() void Demo()
        {
                // ObjectContext -    以对象(这些对象是 EDM 中定义的实体类型的实例)的形式与数据进行交互
                using (var ctx = new NorthwindEntities())
                {
                        // CreateObjectName - 实体类 的 CreateObjectName 静态方法用于创建实体类的新实例
                        Region region = Region.CreateRegion("RegionDescription", 100);

                        // System.Data.EntityState - 实体状态
                        // System.Data.EntityState.Detached - 被分离
                        // System.Data.EntityState.Unchanged - 未发生变化
                        // System.Data.EntityState.Added - 被增加
                        // System.Data.EntityState.Deleted - 被删除
                        // System.Data.EntityState.Modified - 被修改

                        result.InnerHtml += region.EntityState + "<br />"; // Detached
                        // AddToEntitySetName() - 将需要添加的对象添加到对象上下文中
                        // AddObject(string entitySetName, object entity) - 将需要添加的对象添加到对象上下文中
                        // ctx.AddObject("Region", region);
                        ctx.AddToRegion(region);
                        result.InnerHtml += region.EntityState + "<br />"; // Added

                        // SaveChanges() - 将所有更新保存到相关存储区中。将所有实体的 EntityState 标记为 EntityState.Unchanged    
                        // SaveChanges(bool acceptChangesDuringSave) - acceptChangesDuringSave 指定是否将所有实体的 EntityState 标记为 EntityState.Unchanged 。 如果指定为 false 则不会修改实体的 EntityState
                        ctx.SaveChanges();

                        result.InnerHtml += region.EntityState + "<br />"; // Unchanged
                }
        }

void Demo2() void Demo2()
        {
                using (var ctx = new NorthwindEntities())
                {
                        Region region = ctx.Region.First(p => p.RegionID == 100);

                        result.InnerHtml += region.EntityState + "<br />"; // Unchanged
                        region.RegionDescription = "RegionDescriptionUpdated";
                        result.InnerHtml += region.EntityState + "<br />"; // Modified

                        ctx.SaveChanges(false);
                        
                        // ObjectStateEntry - 维护实体状态的类
                        //         GetModifiedProperties() - 获取被修改的属性。返回值 IEnumerable<string>    
                        // ObjectStateManager - 实体状态管理器
                        //         GetObjectStateEntry()/TryGetObjectStateEntry() - 获取指定实体的 ObjectStateEntry
                        //         GetObjectStateEntries(EntityState state) - 获取所指定状态的 ObjectStateEntry 集合。返回值 IEnumerable<ObjectStateEntry>
                        //         ObjectStateManagerChanged事件 - 将实体添加到 ObjectStateManager 中或从中移除实体时发生
                        ObjectStateEntry ose = ctx.ObjectStateManager.GetObjectStateEntry(region);

                        // ObjectStateEntry.State - 实体状态
                        // ObjectStateEntry.OriginalValues - 原始值
                        // ObjectStateEntry.CurrentValues - 当前值
                        result.InnerHtml += ose.State + "<br />"; // Modified (region.EntityState)
                        result.InnerHtml += ose.OriginalValues["RegionDescription"] + "<br />"; // RegionDescription
                        result.InnerHtml += ose.CurrentValues["RegionDescription"] + "<br />"; // RegionDescriptionUpdated

                        // ObjectStateEntry.AcceptChanges()/ObjectContext.AcceptAllChanges() - 将相关的实体状态置为 EntityState.Unchanged
                        ose.AcceptChanges();

                        result.InnerHtml += ose.State + "<br />"; // Unchanged
                }
        }

void Demo3() void Demo3()
        {
                using (var ctx = new NorthwindEntities())
                {
                        // 加载指定的 Region 到上下文中
                        Region regionRead = ctx.Region.First(p => p.RegionID == 100);
                        // 创建一个需要更新的 Region
                        Region regionUpdate = Region.CreateRegion("RegionDescriptionUpdatedSecond", 100);

                        result.InnerHtml += regionRead.EntityState + "<br />"; // Unchanged
                        result.InnerHtml += regionUpdate.EntityState + "<br />"; // Detached
                        // ApplyPropertyChanges(string entitySetName, object changed) - 更新指定的实体(其所对应的主键实体需要加载到上下文中)
                        ctx.ApplyPropertyChanges("Region", regionUpdate);
                        result.InnerHtml += regionRead.EntityState + "<br />"; // Modified
                        result.InnerHtml += regionUpdate.EntityState + "<br />"; // Detached

                        ctx.SaveChanges();
                }
        }

void Demo4() void Demo4()
        {
                using (var ctx = new NorthwindEntities())
                {
                        Region region = new Region() { RegionID = 100, RegionDescription = "RegionDescriptionUpdatedThird" };

                        result.InnerHtml += region.EntityState + "<br />"; // Detached

                        // Attach()/AttachTo() - 附加外部实体到上下文中
                        // ctx.Attach(region);
                        ctx.AttachTo("Region", region);

                        ObjectStateEntry ose = ctx.ObjectStateManager.GetObjectStateEntry(region);

                        // SetModified() - 标记实体状态为 EntityState.Modified
                        // SetModifiedProperty() - 标记需要修改的属性,从而完成对指定属性的修改
                        ose.SetModifiedProperty("RegionDescription");

                        // 以当前数据为准更新存储模型
                        ctx.Refresh(RefreshMode.ClientWins, region);    

                        result.InnerHtml += region.EntityState + "<br />"; // Modified

                        ctx.SaveChanges();
                }
        }

void Demo5() void Demo5()
        {
                using (var ctx = new NorthwindEntities())
                {
                        Region region = new Region() { RegionID = 100 };

                        // CreateEntityKey(string entitySetName, object entity) - 创建 EntityKey
                        EntityKey ek = ctx.CreateEntityKey("Region", region);

                        // ObjectContext.GetObjectByKey()/TryGetObjectByKey() - 根据指定的 EntityKey 获取实体
                        Region r = ctx.GetObjectByKey(ek) as Region;

                        ctx.SaveChanges();

                        result.InnerHtml += r.RegionDescription + "<br />"; // RegionDescriptionUpdatedThird    
                }
        }

void Demo6() void Demo6()
        {
                using (var ctx = new NorthwindEntities())
                {
                        Region region = ctx.Region.First(p => p.RegionID == 100);

                        result.InnerHtml += region.EntityState + "<br />"; // Unchanged

                        // ObjectStateEntry.Delete() - 标记实体的状态为删除。同 DeleteObject()
                        ObjectStateEntry ose = ctx.ObjectStateManager.GetObjectStateEntry(region);
                        ose.Delete();

                        // DeleteObject() - 删除实体
                        // ctx.DeleteObject(region);

                        result.InnerHtml += region.EntityState + "<br />"; // Deleted

                        ctx.SaveChanges();
                }
        }
}
 
 
2、事务和并发处理
ObjectContext2.aspx
<%@ Page Title="事务和并发处理" Language="C#" MasterPageFile="~/Site.master" AutoEventWireup="true"
        CodeFile="ObjectContext2.aspx.cs" Inherits="EntityFramework_ObjectContext2" %>

<asp:Content ID="Content1" ContentPlaceHolderID="head" runat="Server">
</asp:Content>
<asp:Content ID="Content2" ContentPlaceHolderID="ContentPlaceHolder1" runat="Server">
        <div id="result" runat="server" />
</asp:Content>

ObjectContext2.aspx.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;

using System.Data.Objects;
using System.Data.Objects.DataClasses;
using System.Data;

using VS2008SP1.Business;

public partial class EntityFramework_ObjectContext2 : System.Web.UI.Page
{
void Page_Load() void Page_Load(object sender, EventArgs e)
        {
                if (!Page.IsPostBack)
                {
                        // 演示事务的 Demo
                        Demo();

                        result.InnerHtml += "<br />";

                        // 演示并发的 Demo
                        Demo2();
                }
        }

void Demo() void Demo()
        {
                // ObjectContext - SaveChanges 中的逻辑会自动做事务处理

                // 通吃的事务处理
                // using (System.Transactions.TransactionScope tc = new TransactionScope())
                // {
                //         code
                //         tc.Complete();    
                // }

                // 同一 ObjectContext 的多个 SaveChanges() 的事务处理
                using (var ctx = new NorthwindEntities())
                {
                        Region region = Region.CreateRegion("Test", 101);
                        ctx.AddToRegion(region);

                        if (ctx.Connection.State != ConnectionState.Open)
                        {
                                ctx.Connection.Open();
                        }

                        // 开始一个事务
                        System.Data.Common.DbTransaction tran = ctx.Connection.BeginTransaction();

                        // 第一次对数据的操作
                        ctx.SaveChanges();

                        try
                        {
                                Region region2 = Region.CreateRegion("Test2", 101);
                                ctx.AddToRegion(region2);
                                // 第二次对数据库的操作
                                ctx.SaveChanges();

                                // 提交事务(第一次插入主键为 101 的记录,成功;第二次再次插入主键为 101 的记录,失败。所以此处会报错)
                                tran.Commit();
                        }
                        catch (Exception)
                        {
                                result.InnerHtml += "回滚" + "<br />";

                                // 回滚事务(第一次插入成功的主键为 101 的记录会被删除)
                                tran.Rollback();
                        }
                }
        }

void Demo2() void Demo2()
        {
                var ctx = new NorthwindEntities();
                var ctx2 = new NorthwindEntities();

                var region = ctx.Region.First();
                var region2 = ctx2.Region.First();

                // 需要做并发处理的字段,要将其“并发模式”属性设置为 Fixed
                region.RegionDescription = "Eastern" + Guid.NewGuid().ToString();
                region2.RegionDescription = "Eastern" + Guid.NewGuid().ToString();

                ctx.SaveChanges();

                try
                {
                        // ctx 已经修改了 Region 的 RegionDescription 属性
                        // ctx2 再次修改 Region 的 RegionDescription 属性,由于 RegionDescription 在 ctx2 读取之后发生了变化,所以会出现乐观并发(Optimistic Concurrency)问题
                        ctx2.SaveChanges();
                }
                catch (System.Data.OptimisticConcurrencyException)
                {
                        result.InnerHtml += "OptimisticConcurrencyException" + "<br />";

                        // ObjectContext.Refresh(RefreshMode refreshMode, object entity) - 更新上下文数据
                        //         RefreshMode.StoreWins - 以数据库中的值为准
                        //         RefreshMode.ClientWins - 以当前数据为准
                        //         object entity - 需要刷新上下文数据的实体
                        ctx2.Refresh(RefreshMode.StoreWins, region2);
                        // ctx2.Refresh(RefreshMode.ClientWins, region2);

                        ctx2.SaveChanges();
                }

                // 可以不通过 try catch 处理并发,而是通过 Refresh() 直接处理更新逻辑
                // 即若是 RefreshMode.ClientWins 则永远以当前值为准;若是 RefreshMode.StoreWins 则永远以数据库中的值为准(不会更新数据)
                // ctx2.Refresh(RefreshMode.StoreWins, region2);
                // ctx2.SaveChanges();

                ctx.Dispose();
                ctx2.Dispose();
        }
}