|
在这里,看看默认情况下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,这里不想比较,相信都使用过的朋友自然会进行比较。
|








骆驼户外男 真皮磨砂日常休闲鞋 低帮 2011秋冬新款 专柜正品特价