如何停止Entity Framework保留从数据库进行记录?

时间:2018-09-20 11:35:12

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

我正在研究ASP.NET Boilerplate。我有一个问题,我尝试从名为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);
    }
}

如何解决此问题?非常感谢您的帮助。

3 个答案:

答案 0 :(得分:0)

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

如果updateBuildingInput_buildingsAppService,而您要做的就是对实体进行一些更改,则可以:

  1. 阅读实体
  2. 直接更改该实体对象
  3. 致电DbContext

_buildingsAppService.SaveChanges()将:

  

将在此上下文中进行的所有更改保存到基础数据库中。

答案 1 :(得分:0)

使用.AsNoTracking()

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

    // ...
}

答案 2 :(得分:0)

从数据库获取记录时,可以使用.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;
    }

btw,IHasId是一个简单的界面,使泛型类型可以访问ID属性;

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