Asp.Net教程,WinForm教程,Asp.Net MVC,vs2008教程,vs2010教程,Silverlight技术,源码下载,Asp.Net视频教程
全站热门标签
vs2010 Silverlight 存储过程 水晶报表 LINQ ADO.NET JavaScript DataGridView GridView AjaxPro DevExpress 面向对象 Extjs XML HTML教程 Oracle jQuery WPF MVC 分页 Office2010 GDI+ Visual C++2010 Dojo MySQL VB.NET WCF4.0 ASPxGridView WinForm textbox Sql2005 cookie Discuz!NT checkbox WCF SQL经典语句 T-SQL F# asp.net VS2008 SQL Ajax DropDownList VS2008新特性 TreeView Access Flex 页面执行时间 字符串 DataSet VB2005 回调 动画 C#时间 游戏 ASP.NET MVC
当前位置: 主页 > Silverlight >

Silverlight+WCF使用Linq to SQL以及ADO.NET Entity Data Model更新数据库子表方法(2)

时间:2010-05-16 10:34来源:未知 作者:admin 点击:
 
在这里,看看默认情况下LINQ to SQL的并发控制所产生的SQL,以对子实体TDetail进行Update为例,打开SQL Server Profile,看到运行的SQL如下:
exec sp_executesql N'UPDATE [dbo].[TDetail]
SET [DetailName] = @p6
WHERE ([DetailKey] = @p0) AND ([MasterKey] = @p1) AND ([DetailName] = @p2) AND ([Fa] = @p3) AND ([Fb] = @p4) AND ([Fc] = @p5)'
,N'@p0 varchar(4),@p1 varchar(4),@p2 varchar(4),@p3 int,@p4 varchar(4),@p5 datetime,@p6 varchar(4)'
,@p0='D001',@p1='Key1',@p2='DN A',@p3=100,@p4='Fb 1',@p5='2010-08-08 00:00:00:000',@p6='DN B'
只是Update了一个DetailName字段,就要把全部的字段都作为Where条件,这是开放式并发的SQL。
 
修改子实体TDetail的主键DetailKey的时间戳为True,
 

修改后“自动生成的值”会自动变为True,由于是Varchar型,不是Int Identity,所以把它改回False

然后再运行测试,得出的SQL为:
exec sp_executesql N'UPDATE [dbo].[TDetail]
SET [DetailName] = @p2
WHERE ([DetailKey] = @p0) AND ([DetailKey] = @p1)'
,N'@p0 varchar(4),@p1 varchar(4),@p2 varchar(4)',@p0='D001',@p1='D001',@p2='DN C'
仅有主键字段使用了开放式并发控制。
 
不过这里似乎有一个Bug,也可能是我设置得不对,在对TDetail进行插入时,虽然“自动生成的值”已经设为False,可是居然还是尝试去自动生成值,并出现异常

接下来看 EF 1.0:
[OperationContract]
public Models.TMaster GetEntity(string masterKey)
{
    using (Models.TestEntities db = new Models.TestEntities())
    {
        // 在.NET 3.5( Entity Framework 1.0)中,没有 db.ContextOptions.LazyLoadingEnabled 可以设置,
        // 而且相当于 db.ContextOptions.LazyLoadingEnabled = false
        Models.TMaster master = db.TMaster.FirstOrDefault(m => m.MasterKey == masterKey);
        master.TDetail.Load();
        return master;
    }
}
 
[OperationContract]
public int UpdateWithTwoEntities(Models.TMaster current, Models.TMaster original)
{
    using (Models.TestEntities db = new Models.TestEntities())
    {
        // 这里不能写成 db.Attach(current);
        // Attach 会导致新增的子实体的EntityKey != null ,EntityState = Unchanged ,
        // 会导致已删除的子实体的 EntityState = Unchanged,原本为 null
        db.Attach(original);
        this.ApplyCurrentAndOriginalValues(db, current, original);
        return db.SaveChanges();
    }
}
 
[OperationContract]
public int UpdateWithCurrentEntity(Models.TMaster current)
{
    using (Models.TestEntities db = new Models.TestEntities())
    {
        Models.TMaster original = null;
        System.Data.EntityKey key = db.CreateEntityKey(current.EntityKey.EntitySetName, current);
        object oriDataObj;
        if (db.TryGetObjectByKey(key, out oriDataObj))
        {
            original = oriDataObj as Models.TMaster;
            // 只能手工加载(因为相当于 ContextOptions.LazyLoadingEnabled = false)
            original.TDetail.Load();
 
            this.ApplyCurrentAndOriginalValues(db, current, original);
        }
 
        return db.SaveChanges();
    }
}
 
/// <summary>
/// 将主表标识为已更改,同时判断子表的新增、删除、更改
/// </summary>
/// <param name="db"></param>
/// <param name="current"></param>
/// <param name="original"></param>
void ApplyCurrentAndOriginalValues(Models.TestEntities db, Models.TMaster current, Models.TMaster original)
{
    // 这个方法在 .NET 4 中已过时,被 ApplyCurrentValues<T>() 代替
    db.ApplyPropertyChanges(current.EntityKey.EntitySetName, current);
 
    // 已被删除的子表数据
    var deleteds = original.TDetail.Where(ori => !current.TDetail.Any(d => d.DetailKey == ori.DetailKey));
    for (int i = deleteds.Count() - 1; i >= 0; i--)
    {
        Models.TDetail d = deleteds.ElementAt(i);
        // 下面这句要不要执行无所谓,不执行的话则可以使用 foreach
        original.TDetail.Remove(d);
        db.DeleteObject(d);
 
    }
    // 新增加的子表数据
    var inserteds = current.TDetail.Where(d => d.EntityKey == null);
    for (int i = inserteds.Count() - 1; i >= 0; i--)
    {
        Models.TDetail d = inserteds.ElementAt(i);
        current.TDetail.Remove(d);
        // 执行下面的语句,会导致 original.TDetail.Add(d); 也会被执行
        d.TMasterReference.Value = original;
        db.AddToTDetail(d);
 
    }
    // 剩下的子表数据就是被更新的、或没有变化的,
    // 但是要注意 original.TDetail 的条数可能比 current.TDetail 多
    foreach (Models.TDetail d in original.TDetail)
    {
        Models.TDetail curDetail = current.TDetail.FirstOrDefault(cur => cur.DetailKey == d.DetailKey);
        if (curDetail != null)
        {
            db.Attach(d);
            db.ApplyPropertyChanges(d.EntityKey.EntitySetName, curDetail);
        }
    }
}
 
注意:
l   ObjectContext相当于ContextOptions.LazyLoadingEnabled = false;
l   新增的子实体的EntityKey == null;
l   Attach(entity) 应当附加“原始版本”,而不是“当前版本”;
l   tDetail.TMasterReference.Value = tMaster执行时,会导致 tMaster.TDetail.Add(tDetail) 被执行。
 
运行测试,得出的更新子表的SQL为:
exec sp_executesql N'update [dbo].[TDetail]
set [DetailName] = @0
where ([DetailKey] = @1)
',N'@0 varchar(4),@1 varchar(4)',@0='DN E',@1='D001'
完全没有使用并发控制。
 
再看 EF 2.0:
 
[OperationContract]
public Models.TMaster GetEntity(string masterKey)
{
    // 在 .NET 4 中,TestEntities() 的构造函数中写了 ContextOptions.LazyLoadingEnabled = true,
    // 下面这样写会在序列化主表时自动查询子表
    //Models.TestEntities db = new Models.TestEntities();
    //return db.TMasters.FirstOrDefault(m => m.MasterKey == masterKey);
    // 但是奇怪的是加上 using 语句,则会导致主表在序列化时出错,因为此时子表尚未被实例化。
    //using (Models.TestEntities db = new Models.TestEntities())
    //{
    //    db.ContextOptions.LazyLoadingEnabled = true;
    //    return db.TMasters.FirstOrDefault(m => m.MasterKey == masterKey);
    //}
 
    // 要使用 using , 只得设置 db.ContextOptions.LazyLoadingEnabled = false
    using (Models.TestEntities db = new Models.TestEntities())
    {
        //事实上 db.ContextOptions.LazyLoadingEnabled = false 往往是更为灵活的,因为并非每个子表都是我们想要加载的
        db.ContextOptions.LazyLoadingEnabled = false;
        Models.TMaster master = db.TMasters.FirstOrDefault(m => m.MasterKey == masterKey);
        master.TDetails.Load();
        return master;
    }
}
 
[OperationContract]
public int UpdateWithTwoEntities(Models.TMaster current , Models.TMaster original)
{
    using (Models.TestEntities db = new Models.TestEntities())
    {
        // 这里不能写成 db.Attach(current);
        // Attach 会导致新增的子实体的EntityKey != null ,EntityState = Unchanged ,
        // 会导致已删除的子实体的 EntityState = Unchanged,原本为 null
        db.Attach(original);
        this.ApplyCurrentAndOriginalValues(db, current, original);
        return db.SaveChanges();
    }
}
 
[OperationContract]
public int UpdateWithCurrentEntity(Models.TMaster current)
{
    using (Models.TestEntities db = new Models.TestEntities())
    {
        Models.TMaster original = null;
        System.Data.EntityKey key = db.CreateEntityKey(current.EntityKey.EntitySetName, current);
        object oriDataObj;
        if (db.TryGetObjectByKey(key, out oriDataObj))
        {
            original = oriDataObj as Models.TMaster;
            this.ApplyCurrentAndOriginalValues(db, current, original);
        }
        return db.SaveChanges();
    }
}
 
/// <summary>
/// 将主表标识为已更改,同时判断子表的新增、删除、更改
/// </summary>
/// <param name="db"></param>
/// <param name="current"></param>
/// <param name="original"></param>
void ApplyCurrentAndOriginalValues(Models.TestEntities db , Models.TMaster current, Models.TMaster original)
{
    db.TMasters.ApplyOriginalValues(original);
    db.TMasters.ApplyCurrentValues(current);
 
    // 已被删除的子表数据
    var deleteds = original.TDetails.Where(ori => !current.TDetails
        .Any(d => d.DetailKey == ori.DetailKey && d.MasterKey == ori.MasterKey));
    for (int i = deleteds.Count() - 1; i >= 0; i--)
    {
        Models.TDetail d = deleteds.ElementAt(i);
        // 下面这句要不要执行无所谓,不执行的话则可以使用 foreach
        original.TDetails.Remove(d);
        db.DeleteObject(d);
 
    }
    // 新增加的子表数据
    var inserteds = current.TDetails.Where(d => d.EntityKey == null);
    for (int i = inserteds.Count() - 1; i >= 0; i--)
    {
        Models.TDetail d = inserteds.ElementAt(i);
        current.TDetails.Remove(d);
        // 执行下面的语句,会导致 original.TDetails.Add(d); 也会被执行
        d.TMasterReference.Value = original;
        db.AddToTDetails(d);
 
    }
    // 剩下的子表数据就是被更新的、或没有变化的,
    // 但是要注意 original.TDetails 的条数可能比 current.TDetails 多
    foreach (Models.TDetail d in current.TDetails)
    {
        db.TDetails.ApplyOriginalValues(original.TDetails
            .First(ori=>d.DetailKey == ori.DetailKey && d.MasterKey == ori.MasterKey));
        db.TDetails.ApplyCurrentValues(d);
    }
}
 
注意:
l  在GetEntity()中提到的问题,可能是一个Bug,有心的朋友可以通过IL代码查看一下,确定是不是Bug;
l  EF 1.0中ApplyPropertyChanges()已过时。
 
使用 LINQ to SQL还是使用EF,这里不想比较,相信都使用过的朋友自然会进行比较。
 

 

Tags:WCF Silverlight
责任编辑:admin
返回顶部
------分隔线----------------------------
推荐内容
骆驼户外男 真皮磨砂日常休闲鞋 低帮 2011秋冬新款 专柜正品特价 骆驼户外男 真皮磨砂日常休闲鞋 低帮 2011秋冬新款 专柜正品特价