Как остановить Entity Framework на запись в базу данных? - PullRequest
0 голосов
/ 20 сентября 2018

Я работаю над 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 получает данные из базы данных и продолжает удерживать запись из базы данных, и когда я пытаюсь обновить ту же запись, возникает конфликт.Это целое действие, при котором происходят все get и update:

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 голосов
/ 22 сентября 2018

Использование .AsNoTracking():

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

    // ...
}
0 голосов
/ 22 сентября 2018

При получении записи из базы данных вы можете использовать .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; }
}
0 голосов
/ 20 сентября 2018

Создавая новую сущность (updateBuildingInput) с тем же первичным ключом, что и тот, который вы уже прочитали в своем контексте, Entity выдаст ошибку, когда вы попытаетесь выполнить операцию с новой сущностью (как вы видели), так какуже отслеживает сущность с этим первичным ключом в контексте.

Если _buildingsAppService - это DbContext, и все, что вам нужно сделать, это внести некоторые изменения в сущность, вы можете:

  1. Чтение сущности
  2. Внесение изменений непосредственно в этот объект сущности
  3. Вызов _buildingsAppService.SaveChanges()

SaveChanges() будет:

Сохраняет все изменения, сделанные в этом контексте, в базовой базе данных.

...