Entity Framework 6 - DataServiceContext 检测有变化
Entity Framework 6 - DataServiceContext Detect Has Changes
我有一个 WCF 服务器应用程序 运行 Entity Framework 6.
我的客户端应用程序通过 DataServiceContext 使用来自服务器的 OData,在我的客户端代码中,我希望能够在上下文中调用 HasChanges() 方法以查看其中的任何数据是否已更改。
我尝试使用以下扩展方法:
public static bool HasChanges(this DataServiceContext ctx)
{
// Return true if any Entities or links have changes
return ctx.Entities.Any(ed => ed.State != EntityStates.Unchanged) || ctx.Links.Any(ld => ld.State != EntityStates.Unchanged);
}
但它总是 return 错误,即使它正在跟踪的实体确实发生了变化。
例如,假设我有一个名为 Customer 的跟踪实体,以下代码在调用 SaveChanges() 之前总是 returns。
Customer.Address1 = "Fred"
if not ctx.HasChanges() then return
ctx.UpdateObject(Customer)
ctx.SaveChanges()
如果我注释掉 if not ctx.HasChanges() then return 这行代码,更改已成功保存,很高兴该实体已收到更改并能够保存它。
似乎更改 是 被上下文跟踪,只是我无法从我的代码中确定这一事实。
谁能告诉我如何确定 DataServiceContext 上的 HasChanges?
你有没有可能adding/editing你的实体不正确? MSDN 声明您必须使用 AddObject
、UpdateObject
或 DeleteObject
来获取更改跟踪以在客户端上触发(https://msdn.microsoft.com/en-us/library/gg602811(v=vs.110).aspx - 请参阅 管理并发 ).否则你的扩展方法看起来不错。
为了使其工作,必须启用自动更改跟踪。您可以在
中找到此设置
ctx.Configuration.AutoDetectChangesEnabled
所有实体对象也必须由上下文跟踪ctx
。
这意味着它们必须由 ctx
的方法之一返回或显式添加到上下文中。
这也意味着它们必须被DataServiceContext
的同一个实例跟踪。您是否以某种方式创建了多个上下文?
模型也必须正确配置。也许 Customer.Address1
没有映射到数据库列。在这种情况下,EF 将不会检测到列的更改。
我怀疑客户端中的数据上下文不一样one.so变化总是false
。
对于 Datacontext
的每一次更改,您必须确保 Datacontext
是同一个(实例)。然后检测变化是有意义的。
另一种方式,您必须通过 yourself.simply 使用 Trackable Entities 来跟踪更改,以帮助您跟踪数据上下文中实体的更改。
顺便说一句。我使用代码“ctx.ChangeTracker.HasChanges()”来检测 DataContext 的变化。
public bool IsContextDirty(DataServiceContext ctx)
{
#if DEBUG
var changed = ctx.ChangeTracker.Entries().Where(t => t.State != EntityState.Unchanged).ToList();
changed.ForEach(
(t) => Debug.WriteLine("entity Type:{0}", t.Entity.GetType()));
#endif
return ctx != null && ctx.ChangeTracker.HasChanges();
}
很远。我刚刚通读了 DataServiceContext.UpdateObjectInternal(entity, failIfNotUnchanged)
,它是通过 false
参数直接从 UpdateObject(entity)
调用的。
逻辑如下:
- 如果已经修改,return; (短路)
- 如果没有改变,则抛出 if
failIfNotUnchanged
; (仅来自 ChangeState()
)
- 否则将状态设置为已修改。 (没有进行数据检查)
所以从表面上看,UpdateObject
不关心 about/check 实体的内部状态,只关心 State
枚举。这使得在没有更改时更新感觉有点不准确。
但是,我认为你的问题是在 OP 2nd 代码块中,你检查你的扩展 HasChanges
before 呼叫 UpdateObject
。这些实体只是经过美化的 POCO(您可以在 Reference.cs
(显示隐藏文件,然后在服务参考下)中阅读)。它们具有明显的属性和一些 On-
操作来通知更改。他们而不是在内部做的是跟踪状态。事实上,有一个 EntityDescriptor
关联到实体,它负责 EntityTracker.TryGetEntityDescriptor(entity)
中的状态跟踪。
最重要的是操作实际上非常简单,我认为你只需要让你的代码像
Customer.Address1 = "Fred";
ctx.UpdateObject(Customer);
if (!ctx.HasChanges()) return;
ctx.SaveChanges();
虽然我们现在知道,这将总是报告 HasChanges == true,因此您最好跳过检查。
但不要绝望!您的服务参考提供的部分 类 可能会被扩展以完全满足您的需求。它完全是样板代码,因此您可能想要编写 .tt 或其他一些代码生成器。无论如何,只需将其调整到您的实体即可:
namespace ODataClient.ServiceReference1 // Match the namespace of the Reference.cs partial class
{
public partial class Books // Your entity
{
public bool HasChanges { get; set; } = false; // Your new property!
partial void OnIdChanging(int value) // Boilerplate
{
if (Id.Equals(value)) return;
HasChanges = true;
}
partial void OnBookNameChanging(string value) // Boilerplate
{
if (BookName == null || BookName.Equals(value)) return;
HasChanges = true;
}
// etc, ad nauseam
}
// etc, ad nauseam
}
但现在效果很好,并且与 OP 的表达方式类似:
var book = context.Books.Where(x => x.Id == 2).SingleOrDefault();
book.BookName = "W00t!";
Console.WriteLine(book.HasChanges);
HTH!
我有一个 WCF 服务器应用程序 运行 Entity Framework 6.
我的客户端应用程序通过 DataServiceContext 使用来自服务器的 OData,在我的客户端代码中,我希望能够在上下文中调用 HasChanges() 方法以查看其中的任何数据是否已更改。
我尝试使用以下扩展方法:
public static bool HasChanges(this DataServiceContext ctx)
{
// Return true if any Entities or links have changes
return ctx.Entities.Any(ed => ed.State != EntityStates.Unchanged) || ctx.Links.Any(ld => ld.State != EntityStates.Unchanged);
}
但它总是 return 错误,即使它正在跟踪的实体确实发生了变化。
例如,假设我有一个名为 Customer 的跟踪实体,以下代码在调用 SaveChanges() 之前总是 returns。
Customer.Address1 = "Fred"
if not ctx.HasChanges() then return
ctx.UpdateObject(Customer)
ctx.SaveChanges()
如果我注释掉 if not ctx.HasChanges() then return 这行代码,更改已成功保存,很高兴该实体已收到更改并能够保存它。
似乎更改 是 被上下文跟踪,只是我无法从我的代码中确定这一事实。
谁能告诉我如何确定 DataServiceContext 上的 HasChanges?
你有没有可能adding/editing你的实体不正确? MSDN 声明您必须使用 AddObject
、UpdateObject
或 DeleteObject
来获取更改跟踪以在客户端上触发(https://msdn.microsoft.com/en-us/library/gg602811(v=vs.110).aspx - 请参阅 管理并发 ).否则你的扩展方法看起来不错。
为了使其工作,必须启用自动更改跟踪。您可以在
中找到此设置ctx.Configuration.AutoDetectChangesEnabled
所有实体对象也必须由上下文跟踪ctx
。
这意味着它们必须由 ctx
的方法之一返回或显式添加到上下文中。
这也意味着它们必须被DataServiceContext
的同一个实例跟踪。您是否以某种方式创建了多个上下文?
模型也必须正确配置。也许 Customer.Address1
没有映射到数据库列。在这种情况下,EF 将不会检测到列的更改。
我怀疑客户端中的数据上下文不一样one.so变化总是false
。
对于 Datacontext
的每一次更改,您必须确保 Datacontext
是同一个(实例)。然后检测变化是有意义的。
另一种方式,您必须通过 yourself.simply 使用 Trackable Entities 来跟踪更改,以帮助您跟踪数据上下文中实体的更改。
顺便说一句。我使用代码“ctx.ChangeTracker.HasChanges()”来检测 DataContext 的变化。
public bool IsContextDirty(DataServiceContext ctx)
{
#if DEBUG
var changed = ctx.ChangeTracker.Entries().Where(t => t.State != EntityState.Unchanged).ToList();
changed.ForEach(
(t) => Debug.WriteLine("entity Type:{0}", t.Entity.GetType()));
#endif
return ctx != null && ctx.ChangeTracker.HasChanges();
}
很远。我刚刚通读了 DataServiceContext.UpdateObjectInternal(entity, failIfNotUnchanged)
,它是通过 false
参数直接从 UpdateObject(entity)
调用的。
逻辑如下:
- 如果已经修改,return; (短路)
- 如果没有改变,则抛出 if
failIfNotUnchanged
; (仅来自ChangeState()
) - 否则将状态设置为已修改。 (没有进行数据检查)
所以从表面上看,UpdateObject
不关心 about/check 实体的内部状态,只关心 State
枚举。这使得在没有更改时更新感觉有点不准确。
但是,我认为你的问题是在 OP 2nd 代码块中,你检查你的扩展 HasChanges
before 呼叫 UpdateObject
。这些实体只是经过美化的 POCO(您可以在 Reference.cs
(显示隐藏文件,然后在服务参考下)中阅读)。它们具有明显的属性和一些 On-
操作来通知更改。他们而不是在内部做的是跟踪状态。事实上,有一个 EntityDescriptor
关联到实体,它负责 EntityTracker.TryGetEntityDescriptor(entity)
中的状态跟踪。
最重要的是操作实际上非常简单,我认为你只需要让你的代码像
Customer.Address1 = "Fred";
ctx.UpdateObject(Customer);
if (!ctx.HasChanges()) return;
ctx.SaveChanges();
虽然我们现在知道,这将总是报告 HasChanges == true,因此您最好跳过检查。
但不要绝望!您的服务参考提供的部分 类 可能会被扩展以完全满足您的需求。它完全是样板代码,因此您可能想要编写 .tt 或其他一些代码生成器。无论如何,只需将其调整到您的实体即可:
namespace ODataClient.ServiceReference1 // Match the namespace of the Reference.cs partial class
{
public partial class Books // Your entity
{
public bool HasChanges { get; set; } = false; // Your new property!
partial void OnIdChanging(int value) // Boilerplate
{
if (Id.Equals(value)) return;
HasChanges = true;
}
partial void OnBookNameChanging(string value) // Boilerplate
{
if (BookName == null || BookName.Equals(value)) return;
HasChanges = true;
}
// etc, ad nauseam
}
// etc, ad nauseam
}
但现在效果很好,并且与 OP 的表达方式类似:
var book = context.Books.Where(x => x.Id == 2).SingleOrDefault();
book.BookName = "W00t!";
Console.WriteLine(book.HasChanges);
HTH!