Невозможно удалить дочерние объекты из POCO, используя шаблон единиц работы - PullRequest
3 голосов
/ 17 марта 2011

Я использую классы POCO в проекте EF4 CTP5, и у меня возникают проблемы при удалении дочерних свойств. Вот мой пример (надеюсь, не слишком длинный).

Соответствующие части класса тура

public partial class Tour
{
  public Guid TourId { get; private set; }
  protected virtual List<Agent> _agents { get; set; }

  public void AddAgent(Agent agent)
  {
    _agents.Add(agent);
  }

  public void RemoveAgent(Guid agentId)
  {
    var a = Agents.Single(x => x.AgentId == agentId);
    _agents.Remove(Agents.Single(x => x.AgentId == agentId));
  }
}

Командный обработчик

public class DeleteAgentCommandHandler : ICommandHandler<DeleteAgentCommand>
{
  private readonly IRepository<Core.Domain.Tour> _repository;
  private readonly IUnitOfWork _unitOfWork;

  public DeleteAgentCommandHandler(
      IRepository<Core.Domain.Tour> repository, 
      IUnitOfWork unitOfWork
    )
  {
    _repository = repository;
    _unitOfWork = unitOfWork;
  }

  public void Receive(DeleteAgentCommand command)
  {
    var tour = _repository.GetById(command.TourId);
    tour.RemoveAgent(command.AgentId);

    // The following line just ends up calling
    // DbContext.SaveChanges(); on the current context.

    _unitOfWork.Commit();
  }
}

Вот ошибка, которую я получаю, когда мой UnitOfWork вызывает DbContext.SaveChanges()

Операция завершилась неудачно: отношение не может быть изменено, поскольку одно или несколько свойств внешнего ключа не могут быть равны нулю. Когда в отношение вносится изменение, для соответствующего свойства внешнего ключа устанавливается нулевое значение. Если внешний ключ не поддерживает нулевые значения, необходимо определить новое отношение, свойству внешнего ключа должно быть назначено другое ненулевое значение или несвязанный объект должен быть удален.

Это происходит из-за того, что EF просто не удаляет сущность Agent автоматически из базы данных только потому, что он был удален из коллекции Agents в моем классе Tour.

Мне нужно явно позвонить dbContext.Agents.DeleteObject(a);, но моя проблема в том, что у меня нет доступа к dbContext из моего POCO.

Есть ли способ справиться с этим сценарием?

Ответы [ 2 ]

1 голос
/ 17 марта 2011

С вашей текущей архитектурой, боюсь, вам нужно скормить DeleteAgentCommandHandler вторым хранилищем (я думаю, IRepository<Core.Domain.Agent>), а затем вызвать что-то вроде Delete(command.AgentId) в этом втором хранилище.

Иливы можете расширить свой IUnitOfWork до фабрики репозиториев, чтобы интерфейс получал дополнительный метод, такой как T CreateRepository<T>(), который позволяет вам извлекать любой экземпляр вашего общего репозитория из единицы работы.(Тогда вам нужно только добавить IUnitOfWork в DeleteAgentCommandHandler, а не в репозитории.)

Или держаться подальше от универсальных репозиториев на уровне вашего бизнеса / пользовательского интерфейса.Если Agent полностью зависит от Tour, ему вообще не нужно иметь хранилище.Не универсальный ITourRepository может иметь методы для обработки случая удаления агента из обхода на уровне базы данных соответствующим образом.

0 голосов
/ 01 июня 2011

Это похоже на то, что должно работать.Я нашел этот пост, который предполагает, что эта функция исследуется для будущих версий:

http://social.msdn.microsoft.com/Forums/en-US/adonetefx/thread/58a31f34-9d2c-498d-aff3-fc96988a3ddc/

Я также нашел другой пост (где-то - к сожалению, я его потерял), в котором предлагалось добавитьключ родительской сущности к дочерней сущности в вашем методе DbContext OnModelCreating, подобный следующему:

modelBuilder.Entity<Agent>()
.HasKey(AgentId)
.HasKey(TourId);

В настоящее время это вызывает исключение во время выполнения с использованием кода сначала, хотя у меня это работает при использовании файла EDMX путем взломаXAML для включения родительского ключа в модель данных хранилища, а также концептуальную модель данных.Я думаю, что это различие в поведении связано с тем, что в случае файла EDMX EF полагается, что хранимые метаданные хранилища являются точными, тогда как код сначала проверяет базу данных на предмет соответствия ее модели.

Еще один способ, который может работать, хотя я еще не пробовал, - это включить родительский ключ в качестве составного ключа в дочернюю таблицу, чтобы первый код был доволен.Очевидно, что изменение базы данных или взлом XAML - это далеко не идеал, и в лучшем случае обходные пути.

...