Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/261.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# EF5在更新选择性字段时无法处理并发性_C#_Asp.net Mvc_Database_Entity Framework_Concurrency - Fatal编程技术网

C# EF5在更新选择性字段时无法处理并发性

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实

我使用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”

这是我的代码供参考

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中为表定义了一个主键。