C# EF5在更新选择性字段时无法处理并发性
我使用EF5和数据优先的方法来更新实体。 我正在使用其他问题建议的方法有条件地只更新实体中修改的属性 Oki所以这里的场景是我的控制器用POCO对象调用服务并从服务中获取POCO对象,服务层与数据层对话,数据层内部使用EF5从数据库中检索实体并在数据库中更新它们 视图数据由控制器从DTO加载到从服务层检索的对象。 用户对视图进行更改并将JSON数据发回控制器,该控制器被映射到控制器中的DTO对象(由MVC提供)。 控制器使用DTO对象(POCO)对象调用服务层。 该服务将POCO对象映射到EF实体对象,并调用传入EF实体的数据层(即存储库)更新方法。 在存储库中,我从DB获取现有实体并调用ApplyCurrentvaluesValues方法,然后检查是否修改了任何属性。 如果修改了属性,则我将自定义逻辑应用于与当前实体无关的其他实体,并更新当前实体的“UpdateAdminId”和“UpdateDate”。 在Centext上发布我称之为“SaveChanges”的方法 我上面提到的每一件事都可以正常工作,除非我在“SaveChanges”调用中插入一个断点,并将用户修改的某些字段更新为不同的值,然后EF5不会抛出“DbUpdateConcurrencyException”。 i、 当我感兴趣的属性被修改为完美工作时,我可以得到条件更新并触发我的自定义逻辑。 但在并发的情况下,我并没有得到错误,也就是说,在我从数据库获取记录、更新记录并保存记录的过程中,如果记录被更新,EF并没有引发“DbUpdateConcurrencyException” 在实际场景中,有一个脱机cron正在运行,它检查新创建的活动并为其创建公文包,并将下面的IsPortfolioCreated属性标记为true,同时用户可以编辑活动,并且即使cron已创建公文包,也可以将标志设置为false 为了复制并发场景,我在SaveChanges上设置了一个断点,然后从MS Sql enterprise manager为同一实体更新IsPortfolioCreated feild,但即使存储区中的数据已更新,也不会引发“DbUpdateConcurrencyException” 这是我的代码供参考C# EF5在更新选择性字段时无法处理并发性,c#,asp.net-mvc,database,entity-framework,concurrency,C#,Asp.net Mvc,Database,Entity Framework,Concurrency,我使用EF5和数据优先的方法来更新实体。 我正在使用其他问题建议的方法有条件地只更新实体中修改的属性 Oki所以这里的场景是我的控制器用POCO对象调用服务并从服务中获取POCO对象,服务层与数据层对话,数据层内部使用EF5从数据库中检索实体并在数据库中更新它们 视图数据由控制器从DTO加载到从服务层检索的对象。 用户对视图进行更改并将JSON数据发回控制器,该控制器被映射到控制器中的DTO对象(由MVC提供)。 控制器使用DTO对象(POCO)对象调用服务层。 该服务将POCO对象映射到EF实
Public bool EditGeneralSettings(CampaignDefinition campaignDefinition)
{
var success = false;
//campaignDefinition.UpdatedAdminId is updated in controller by retreiving it from RquestContext, so no its not comgin from client
var updatedAdminId = campaignDefinition.UpdatedAdminId;
var updationDate = DateTime.UtcNow;
CmsContext context = null;
GlobalMasterContext globalMasterContext = null;
try
{
context = new CmsContext(SaveTimeout);
var contextCampaign = context.CampaignDefinitions.Where(x => x.CampaignId == campaignDefinition.CampaignId).First();
//Always use this fields from Server, no matter what comes from client
campaignDefinition.CreationDate = contextCampaign.CreationDate;
campaignDefinition.UpdatedAdminId = contextCampaign.UpdatedAdminId;
campaignDefinition.UpdationDate = contextCampaign.UpdationDate;
campaignDefinition.AdminId = contextCampaign.AdminId;
campaignDefinition.AutoDecision = contextCampaign.AutoDecision;
campaignDefinition.CampaignCode = contextCampaign.CampaignCode;
campaignDefinition.IsPortfolioCreated = contextCampaign.IsPortfolioCreated;
var campaignNameChanged = contextCampaign.CampaignName != campaignDefinition.CampaignName;
// Will be used in the below if condition....
var originalSkeForwardingDomain = contextCampaign.skeForwardingDomain.ToLower();
var originalMgForwardingDomain = contextCampaign.mgForwardingDomain.ToLower();
//This also not firing concurreny exception....
var key = ((IObjectContextAdapter) context).ObjectContext.CreateEntityKey("CampaignDefinitions", campaignDefinition);
((IObjectContextAdapter)context).ObjectContext.AttachTo("CampaignDefinitions", contextCampaign);
var updated = ((IObjectContextAdapter)context).ObjectContext.ApplyCurrentValues(key.EntitySetName, campaignDefinition);
ObjectStateEntry entry = ((IObjectContextAdapter)context).ObjectContext.ObjectStateManager.GetObjectStateEntry(updated);
var modifiedProperties = entry.GetModifiedProperties();
//Even tried this , works fine but no Concurrency exception
//var entry = context.Entry(contextCampaign);
//entry.CurrentValues.SetValues(campaignDefinition);
//var modifiedProperties = entry.CurrentValues.PropertyNames.Where(propertyName => entry.Property(propertyName).IsModified).ToList();
// If any fields modified then only set Updation fields
if (modifiedProperties.Count() > 0)
{
campaignDefinition.UpdatedAdminId = updatedAdminId;
campaignDefinition.UpdationDate = updationDate;
//entry.CurrentValues.SetValues(campaignDefinition);
updated = ((IObjectContextAdapter)context).ObjectContext.ApplyCurrentValues(key.EntitySetName, campaignDefinition);
//Also perform some custom logic in other entities... Then call save changes
context.SaveChanges();
//If campaign name changed call a SP in different DB..
if (campaignNameChanged)
{
globalMasterContext = new GlobalMasterContext(SaveTimeout);
globalMasterContext.Rename_CMS_Campaign(campaignDefinition.CampaignId, updatedAdminId);
globalMasterContext.SaveChanges();
}
}
success = true;
}
catch (DbUpdateConcurrencyException ex)
{
//Code never enters here, if it does then I am planning to show the user the values from DB and ask him to retry
//In short Store Wins Strategy
//Code in this block is not complete so dont Stackies don't start commenting about this section and plague the question...
// Get the current entity values and the values in the database
var entry = ex.Entries.Single();
var currentValues = entry.CurrentValues;
var databaseValues = entry.GetDatabaseValues();
// Choose an initial set of resolved values. In this case we
// make the default be the values currently in the database.
var resolvedValues = databaseValues.Clone();
// Update the original values with the database values and
// the current values with whatever the user choose.
entry.OriginalValues.SetValues(databaseValues);
entry.CurrentValues.SetValues(resolvedValues);
}
catch (Exception ex)
{
if (ex.InnerException != null)
throw ex.InnerException;
throw;
}
finally
{
if (context != null) context.Dispose();
if (globalMasterContext != null) globalMasterContext.Dispose();
}
return success;
}
实体框架在您(作为开发人员)将其配置为检查并发性问题之前,它不会对并发性做任何特殊的处理 您正在尝试捕获DbUpdateConcurrencyException,该异常的文档说明:“当预期实体的SaveChanges将导致数据库更新时,DbContext引发异常,,但实际上数据库中没有任何行受到影响,您可以阅读该异常。” 在数据库优先的方法中,您必须为“Fixed”列设置属性“Concurrency Mode”(默认值为None)。请看此屏幕截图: 列版本是SQL SERVER时间戳类型,是一种特殊类型,每次行更改时自动更新,请阅读 使用此配置,如果一切正常,您可以尝试此简单测试:
try
{
using (var outerContext = new testEntities())
{
var outerCust1 = outerContext.Customer.FirstOrDefault(x => x.Id == 1);
outerCust1.Description += "modified by outer context";
using (var innerContext = new testEntities())
{
var innerCust1 = innerContext.Customer.FirstOrDefault(x => x.Id == 1);
innerCust1.Description += "modified by inner context";
innerContext.SaveChanges();
}
outerContext.SaveChanges();
}
}
catch (DbUpdateConcurrencyException ext)
{
Console.WriteLine(ext.Message);
}
在上面的示例中,将提交来自内部上下文的更新,来自外部上下文的更新将引发DbUpdateConcurrencyException,因为EF将尝试使用两列作为筛选器更新实体:Id列和版本列
希望这有帮助 该实体是否具有某些属性的[concurrency check]属性?@omar.ballerani否该实体的concurrency check属性不存在。但是我认为它仍然应该像下面的MSDN文章中描述的那样工作。根据我的经验,实体框架在找不到要更新的行时会抛出DbUpdateConcurrencyException。将[ConcurrencyCheck]属性添加到一个或多个属性时,ef使用相关列作为更新的筛选器query@omar.ballerani我使用的是数据库优先的方法,所以我认为我不能将属性添加到实体中。但是我在DB中为表定义了一个主键。