Невозможно получить связь для обновления для свойств навигации в каркасе сущностей - PullRequest
6 голосов
/ 07 марта 2012

В настоящее время я использую EF4.3 и Code First. Создание моих объектов работает (через мои представления - просто с помощью автоматически сгенерированного Create), но когда я пытаюсь редактировать объект, он не сохраняет никаких изменений, которые полностью связаны с моими свойствами навигации. Я читал об отношениях , но я не понимаю, как сказать моему контексту, что отношения изменились.

Вот пример кода моей реализации.

@* Snippet from my view where I link into my ViewModel. *@
<div class="row">
    <div class="editor-label">
        @Html.LabelFor(model => model.ManagerID)
    </div>
    <div class="editor-field">
        @Html.DropDownListFor(model => model.ManagerID, ViewBag.Manager as SelectList, String.Empty)
        @Html.ValidationMessageFor(model => model.ManagerID)
    </div>
</div>

Вот моя реализация контроллера (POST моего редактирования):

    [HttpPost]
    public ActionResult Edit(ProjectViewModel projectViewModel)
    {
        if (ModelState.IsValid)
        {
            Project project = new Project();
            project.ProjectID = projectViewModel.ProjectID;
            project.Name = projectViewModel.Name;
            project.ProjectManager = repository.GetUser(projectViewModel.ManagerID);
            repository.InsertOrUpdateProject(project);
            repository.Save();
            return RedirectToAction("Index");
        }
        ViewBag.Manager = new SelectList(repository.GetUsers(), "UserID", "FullName", projectViewModel.ManagerID);
        return View(projectViewModel);
    }

В моем объекте Project:

public class Project
{
    public int ProjectID { get; set; }
    [Required]
    public string Name { get; set; }

    // Navigation Properties
    public virtual User Manager { get; set; }
}

Вот соответствующий метод из хранилища (где находится мой контекст):

public void InsertOrUpdateProject(Project project)
    {
        if (program.ProjectID == default(int))
        {
            context.Projects.Add(project);
        }
        else
        {
            context.Entry(project).State = EntityState.Modified;
        }
    }

Просто чтобы прояснить, это действительно помогает обновить мои свойства, но не обновляет мои навигационные свойства (в данном случае, Manager). Цени любую помощь.

Ответы [ 3 ]

11 голосов
/ 07 марта 2012

Установка состояния на Modified помечает скалярные свойства как измененные, а не навигационные свойства.У вас есть несколько вариантов:

  • Взлом (вам не понравится)

    //...
    else
    {
        var manager = project.Manager;
        project.Manager = null;
        context.Entry(project).State = EntityState.Modified;
        // the line before did attach the object to the context
        // with project.Manager == null
        project.Manager = manager;
        // this "fakes" a change of the relationship, EF will detect this
        // and update the relatonship
    }
    
  • Перезагрузка проекта из базы данных включая (нетерпеливая загрузка) текущего менеджераЗатем установите свойства.Отслеживание изменений снова обнаружит изменение менеджера и напишет ОБНОВЛЕНИЕ.

  • Предоставьте свойство внешнего ключа для свойства навигации Manager в вашей модели:

    public class Project
    {
        public int ProjectID { get; set; }
        [Required]
        public string Name { get; set; }
    
        public int ManagerID { get; set; }
        public virtual User Manager { get; set; }
    }
    

    Теперь ManagerID является скалярным свойством, и установка состояния в Modified будет включать это свойство.Более того, вам не нужно загружать пользователя Manager из базы данных, вы можете просто назначить идентификатор, который вы получите из своего представления:

    Project project = new Project();
    project.ProjectID = projectViewModel.ProjectID;
    project.Name = projectViewModel.Name;
    project.ManagerID = projectViewModel.ManagerID;
    repository.InsertOrUpdateProject(project);
    repository.Save();
    
4 голосов
/ 28 мая 2016

Здесь есть несколько опций, я перечислю 3 из них:

Опция 1: Использование GraphDiff

* Для этого Configuration.AutoDetectChangesEnabled вашего контекста должно быть установлено в значение true.

Просто установите GraphDiff с NuGet

Install-Package RefactorThis.GraphDiff

Затем

using (var context = new Context())
{
    var customer = new Customer()
    {
        Id = 12503,
        Name = "Jhon Doe",
        City = new City() { Id = 8, Name = "abc" }
    };

    context.UpdateGraph(customer, map => map.AssociatedEntity(p => p.City));
    context.Configuration.AutoDetectChangesEnabled = true;

    context.SaveChanges();
}

Для получения более подробной информации о GraphDiff смотрите здесь .

Вариант 2. Поиск и редактирование

Поиск вашей сущности с помощью EF для отслеживания ее в контексте.Затем отредактируйте свойства.

* Для этого Configuration.AutoDetectChangesEnabled вашего контекста должно быть установлено значение true.

var customer = new Customer()
{
    Id = 12503,
    Name = "Jhon Doe",
    City = new City() { Id = 8, Name = "abc" }
};

using (var context = new Contexto())
{
    var customerFromDatabase = context.Customers
                                      .Include(x => x.City)
                                      .FirstOrDefault(x => x.Id == customer.Id);

    var cityFromDataBase = context.Cities.FirstOrDefault(x => x.Id == customer.City.Id);

    customerFromDatabase.Name = customer.Name;
    customerFromDatabase.City = cityFromDataBase;                

    context.Configuration.AutoDetectChangesEnabled = true;
    context.SaveChanges();
}

Опция 3: использование скалярного свойства

ВС точки зрения производительности это лучший способ, но он мешает вашему классу заботиться о базе данных.Поскольку вам потребуется создать скалярное свойство ( примитив тип) для сопоставления идентификатора.

* Таким образом, нет необходимости устанавливать Configuration.AutoDetectChangesEnabled в значение true.А также вам не нужно будет делать запрос к базе данных для извлечения сущностей (как и первые два варианта - да, GraphDiff делает это за кулисами!).

var customer = new Customer()
{
    Id = 12503,
    Name = "Jhon Doe",
    City_Id = 8,
    City = null
};

using (var contexto = new Contexto())
{
    contexto.Entry(customer).State = EntityState.Modified;
    contexto.SaveChanges();
}
0 голосов
/ 07 марта 2012

Я не уверен, что именно вы подразумеваете под навигационными свойствами? Вы имеете в виду отношения с иностранным ключом? Если это так, попробуйте следующую аннотацию данных:

public class Project
{
    public int ProjectID { get; set; }

    [Required]
    public string Name { get; set; }

    [ForeignKey("YourNavigationProperty")]
    public virtual UserManager { get; set; }
}

Обновите свой EF Context и посмотрите, что произойдет?

UPDATE

public class Project
{
    public int ProjectID { get; set; }

    [Required]
    public string Name { get; set; }

    [ForeignKey("ManagerId")]
    public ManagerModel UserManager { get; set; }
}

public class ManagerModel
{
    [Key]
    public int ManagerId { get; set; }

    public String ManagerName { get; set; }
}

Посмотри, работает ли это?

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...