【问题标题】:How to stop Entity Framework holding on to record from database?如何阻止实体框架继续从数据库中记录?
【发布时间】:2018-09-23 16:40:07
【问题描述】:

我正在研究 ASP.NET 样板。我有一个问题,我尝试从名为Buildings 的表中获取记录并对其进行更新。我通过以下方式从数据库中获取记录:

var buildingApp = _buildingsAppService.getBuildingsById(buildingInput);

然后,我对数据进行了一些更改,如下所示:

buildingApp.streetName = Request["buildingaddress"];
buildingApp.isInHoush = Convert.ToBoolean(Request["buildingOutput.isInHoush"]);
buildingApp.houshName = Request["HoushName"];

然后将buildingApp复制到另一个具有相同属性的对象中,以便将新对象传递给update方法,如下所示:

var updateBuildingInput = new UpdateBuidlingsInput()
{
    Id = buildingApp.Id,
    buildingID = buildingApp.buildingID,
    numOfBuildingUnits = buildingApp.numOfBuildingUnits,
    numOfFloors = buildingApp.numOfFloors,
    streetName = buildingApp.streetName,
    buildingNo = buildingApp.buildingNo,
    neighborhoodID = buildingApp.neighborhoodID,
    buildingTypeID = buildingApp.buildingTypeID,
    GISMAP = buildingApp.GISMAP,
    houshProperty = buildingApp.houshProperty,
    houshName = buildingApp.houshName,
    X = buildingApp.X,
    Y = buildingApp.Y,
    buildingName = buildingApp.buildingName,
    isInHoush = buildingApp.isInHoush,
    buildingUsesID = buildingApp.buildingUsesID
};

update方法如下:

_buildingsAppService.update(updateBuildingInput);

问题是当它执行上一行时,我得到以下错误:

System.InvalidOperationException:“附加类型为“TaawonMVC.Models.Buildings”的实体失败,因为同一类型的另一个实体已经具有相同的主键值。如果图中的任何实体具有冲突的键值,则在使用“附加”方法或将实体的状态设置为“未更改”或“已修改”时,可能会发生这种情况。这可能是因为某些实体是新实体,尚未收到数据库生成的键值。在这种情况下,使用“添加”方法或“已添加”实体状态来跟踪图形,然后将非新实体的状态设置为“未更改”或“已修改”。

我可以看到,当我手动初始化对象updateBuildingInput 时,update 方法运行没有错误。但是当它依赖于使用buildingApp从数据库中获取的对象时,就会发生错误。似乎get 方法从数据库中获取数据并继续保留数据库中的记录,当我尝试更新同一记录时,就会发生冲突。这是所有getupdate 发生的整个动作:

public ActionResult UpdateApplication (UpdateApplicationsInput model)
{
    var updateApplication = new UpdateApplicationsInput();
    updateApplication.buildingId = Convert.ToInt32(Request["buildingnumber"]);
    updateApplication.buildingUnitId = Convert.ToInt32(Request["dropDownBuildingUnitApp"]);
    //==== get building and unit related to application for update ======
    var buildingInput = new GetBuidlingsInput()
    {
        Id = updateApplication.buildingId
    };
    var buildingUnitInput = new GetBuildingUnitsInput()
    {
        Id = updateApplication.buildingUnitId
    };
    var buildingApp = _buildingsAppService.getBuildingsById(buildingInput);

    var buildingUnitApp = _buildingUnitsAppService.GetBuildingUnitsById(buildingUnitInput);
        buildingApp.streetName = Request["buildingaddress"];
        buildingApp.isInHoush = Convert.ToBoolean(Request["buildingOutput.isInHoush"]);
        buildingApp.houshName = Request["HoushName"];
        // buildingUnitApp.BuildingId = updateApplication.buildingId;
        buildingUnitApp.ResidenceStatus = Request["residentstatus"];
        // copy object getBuildingUnitInput to updateBuildingUnitInput

    var updateBuildingUnitInput = new UpdateBuildingUnitsInput()
    {
        BuildingId = buildingUnitApp.BuildingId,
        ResidentName = buildingUnitApp.ResidentName,
        ResidenceStatus = buildingUnitApp.ResidenceStatus,
        NumberOfFamilyMembers = buildingUnitApp.NumberOfFamilyMembers,
        Floor = buildingUnitApp.Floor,
        UnitContentsIds = buildingUnitApp.UnitContentsIds
    };

    //============================================
    // copy object from getBuildingOutput to updateBuildingInput
    var updateBuildingInput = new UpdateBuidlingsInput()
    {
        Id = buildingApp.Id,
        buildingID = buildingApp.buildingID,
        numOfBuildingUnits = buildingApp.numOfBuildingUnits,
        numOfFloors = buildingApp.numOfFloors,
        streetName = buildingApp.streetName,
        buildingNo = buildingApp.buildingNo,
        neighborhoodID = buildingApp.neighborhoodID,
        buildingTypeID = buildingApp.buildingTypeID,
        GISMAP = buildingApp.GISMAP,
        houshProperty = buildingApp.houshProperty,
        houshName = buildingApp.houshName,
        X = buildingApp.X,
        Y = buildingApp.Y,
        buildingName = buildingApp.buildingName,
        isInHoush = buildingApp.isInHoush,
        buildingUsesID = buildingApp.buildingUsesID
    };

    //======================================================
    updateApplication.Id = Convert.ToInt32(Request["applicationId"]);
    updateApplication.fullName = model.fullName;
    updateApplication.phoneNumber1 = model.phoneNumber1;
    updateApplication.phoneNumber2 = model.phoneNumber2;
    updateApplication.isThereFundingOrPreviousRestoration = model.isThereFundingOrPreviousRestoration;
    updateApplication.isThereInterestedRepairingEntity = model.isThereInterestedRepairingEntity;
    updateApplication.housingSince = model.housingSince;
    updateApplication.previousRestorationSource = model.previousRestorationSource;
    updateApplication.interestedRepairingEntityName = model.interestedRepairingEntityName;
    updateApplication.PropertyOwnerShipId = Convert.ToInt32(Request["PropertyOwnerShip"]);
    updateApplication.otherOwnershipType = model.otherOwnershipType;
    updateApplication.interventionTypeId = Convert.ToInt32(Request["interventionTypeName"]);
    updateApplication.otherRestorationType = model.otherRestorationType;
    updateApplication.propertyStatusDescription = model.propertyStatusDescription;
    updateApplication.requiredRestoration = model.requiredRestoration;
    updateApplication.buildingId = Convert.ToInt32(Request["buildingnumber"]);
    updateApplication.buildingUnitId = Convert.ToInt32(Request["dropDownBuildingUnitApp"]);

    // ==== get of restoration types which it is multi select drop down list ======
    var restorationTypes = Request["example-getting-started"];
    string[] restorationTypesSplited = restorationTypes.Split(',');
    byte[] restorationTypesArray = new byte[restorationTypesSplited.Length];
    for (var i = 0; i < restorationTypesArray.Length; i++)
    {
        restorationTypesArray[i] = Convert.ToByte(restorationTypesSplited[i]);
    }

    updateApplication.restorationTypeIds = restorationTypesArray;
    // ====== end of RestorationTypes
    _buildingsAppService.update(updateBuildingInput);
    _applicationsAppService.Update(updateApplication);

    // _buildingUnitsAppService.Update(updateBuildingUnitInput);

    // ==== get list of applications ==============
    var applicationsUpdate = _applicationsAppService.getAllApplications();
    var applicationsViewModel = new ApplicationsViewModel()
    {
        Applications = applicationsUpdate
    };

    return View("Applications", applicationsViewModel);
}

我使用的 ASP.NET Boilerplate 模板如何对数据库进行 CRUD 操作:

public class BuildingsManager : DomainService, IBuildingsManager
{
    private readonly IRepository<Buildings> _repositoryBuildings;

    public BuildingsManager(IRepository<Buildings> repositoryBuildings)
    {
        _repositoryBuildings = repositoryBuildings;
    }
    // create new building in table buildings .
    public async Task<Buildings> create(Buildings entity)
    {
        var building = _repositoryBuildings.FirstOrDefault(x => x.Id == entity.Id);
        if(building!=null)
        {
            throw new UserFriendlyException("Building is already exist");
        }
        else
        {
            return  await _repositoryBuildings.InsertAsync(entity);
        }
    }
    // delete a building from buildings table .
    public void delete(int id)
    {
        try
        {
            var building = _repositoryBuildings.Get(id);
            _repositoryBuildings.Delete(building);
        }
        catch (Exception)
        {
            throw new UserFriendlyException("Building is not exist");
        }
    }

    public IEnumerable<Buildings> getAllList()
    {
        return _repositoryBuildings.GetAllIncluding(b => b.BuildingType, n => n.NeighboorHood,u=>u.BuildingUses);
    }

    public Buildings getBuildingsById(int id)
    {
        return _repositoryBuildings.Get(id);
    }

    public void update(Buildings entity)
    {
        _repositoryBuildings.Update(entity);
    }
}

我该如何解决这个问题?非常感谢您的帮助。

【问题讨论】:

  • 您好!更新前试试这个代码:_buildingsAppService.Entry(updateBuildingInput).State = EntityState.Modified;
  • 核心层,CRUD操作数据库使用IRepository向数据库注入语句:
  • 您不需要将更新的对象复制到另一个对象。一旦您对对象buildingApp 进行了更改,如果您想立即更新它,您可以调用SaveChanges(),或者直接返回,当请求完成时,工作单元会自动为您更新它。

标签: c# asp.net asp.net-mvc entity-framework aspnetboilerplate


【解决方案1】:

通过创建一个新实体 (updateBuildingInput),其主键与您在上下文中已读取的主键相同,当您尝试对新实体(如您所见)进行操作时,Entity 将抛出错误,因为它已经在上下文中使用该主键跟踪实体。

如果_buildingsAppServiceDbContext 并且您只需对实体进行一些更改,您可以:

  1. 读取实体
  2. 直接对该实体对象进行更改
  3. 致电_buildingsAppService.SaveChanges()

SaveChanges() 将:

将在此上下文中所做的所有更改保存到底层数据库。

【讨论】:

  • 我从数据库中读取实体然后对其进行更改然后尝试更新它,而不是创建具有相同主键的新实体,并且我使用它使用 IRepository 将语句注入数据库的样板,如更新,删除,读取和创建。
  • @YassarMansour 这一行 Id = buildingApp.Id 正在将更新实体的 ID(猜测这是主键)设置为您阅读的 ID
  • 是的,它是主键,我需要删除这一行吗?
  • 当我删除它时,它给我的错误是不止一行受到影响。
【解决方案2】:

从 db 获取记录时可以使用.AsNoTracking()

或者如果你真的需要更新一个附加的实体,首先找到附加的副本并分离它,然后修改和更新;

public async Task<bool> UpdateAsync<T>(T entity)
        where T : class, IHasId
    {
        // check if entity is being tracked
        var local = _context.Set<T>().Local.FirstOrDefault(x => x.Id.Equals(entity.Id));

        // if entity is tracked detach it from context
        if (local != null)
            _context.Entry<T>(local).State = EntityState.Detached;

        _context.Attach(entity).State = EntityState.Modified;

        var result = await _context.SaveChangesAsync();


        // detach entity if it was not tracked, otherwise it will be kept tracking
        if(local == null)
            _context.Entry(entity).State = EntityState.Detached;

        return result > 0;
    }

顺便说一句,IHasId 是一个简单的接口,可以让泛型类型可以访问 Id 属性;

public interface IHasId {
    int Id { get; set; }
}

【讨论】:

  • 我从数据库中获取记录如下: var buildingApp = _buildingsAppService.getBuildingsById(buildingInput); ,问题是我没有直接从数据库中获取它,但是我使用asp.net样板,其中有两层,一层称为应用程序,另一层称为核心,在核心中我实现了所谓的IRepository接口,以便在其上注入CRUD操作数据库,所以当我在核心层使用 AsNoTracking() 时,存储库停止检索数据,并给我错误,尝试将它添加到控制器中也不起作用。
【解决方案3】:

使用.AsNoTracking():

public class BuildingsManager : DomainService, IBuildingsManager
{
    public Buildings getBuildingsById(int id)
    {
        return _repositoryBuildings.GetAll().AsNoTracking().First(b => b.Id == id);
    }

    // ...
}

【讨论】:

  • 它给了我这个错误:无法从程序集 'EntityFramework,Version=6.0.0.0,Culture=neutral,PublicKeyToken=b77a5c561934e089' 中加载类型 'System.Data.Entity.DbExtensions'。
  • 顺便说一下,首先不要以整数作为参数,它需要 lambda 表达式,我改变它,但它停止返回数据如上
  • 无法复制。在 GitHub 上创建一个 repro 项目。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-08-03
  • 2014-09-09
相关资源
最近更新 更多