Как определить идентификатор совокупного корня, добавленного в репозиторий?

Скажем, у меня есть общий интерфейс репозитория следующим образом:

public interface IRepository<T>
{
    Add(T item);
    Delete(int itemId);
    Update(T item);
}

Обычно новый идентификатор элемента, добавленного через IRepository.Add (), будет определяться некоторой внутренней базой данных, но только после того, как будет отправлена ​​общая транзакция / единица работы. Поэтому я совершенно уверен, что IRepository.Add () будет неправильно возвращать новый идентификатор добавленного элемента. Репозиторий действительно не должен ничего знать о том, как создаются идентификаторы. Это верно?

Если это так, как еще можно определить новый идентификатор элемента, добавленного в репозиторий, или мне даже следует это делать? Я знаю, что ORM, такая как NHibernate, может автоматически заменять объекты в памяти новыми объектами с правильным идентификатором, но я пытаюсь создать свой репозиторий без какой-либо конкретной реализации ORM.

Например, скажем, у меня есть веб-сайт, на котором клиенты могут делать заказы. Новый клиент выбирает оформить заказ, и ему отправляется форма для заполнения его данных. Эта информация используется для создания объекта Customer, который хранится в CustomerRepository. Теперь необходимо создать информацию об их заказе, но в заказе необходимо ссылаться на клиента по его идентификатору?

Customer newCustomer = new Customer(first, last, address, phone dateOfBirth);
customerRepository.Add(newCustomer);

//How would I determine customerId??
Order newOrder = new Order(customerId, shippingAddress, billingAddress);
newOrder.AddOrderItem("widget");
newOrder.AddOrderItem("doohicky");
newOrder.AddOrderItem("stuff");

person Eric Anastas    schedule 08.06.2011    source источник


Ответы (6)


В приведенном вами примере я бы создал Customer и Order за один шаг и передал бы объекты домена объектам домена вместо передачи идентификаторов:

Customer newCustomer = new Customer(first, last, address, phone dateOfBirth);

// Pass the customer rather than the CustomerId:
Order newOrder = new Order(newCustomer , shippingAddress, billingAddress);
newOrder.AddOrderItem("widget");
newOrder.AddOrderItem("doohicky");
newOrder.AddOrderItem("stuff");

customerRepository.Add(newCustomer);
orderRepository.Add(newOrder);

// SaveChanges()

... когда изменения сохранены, платформа автоматически заполняет идентификаторы как Customer, так и Order и заполняет Customer.Id, Order.customerId и т. д. в силу того, что объект Customer был назначен Order.

person Steve Wilkes    schedule 08.06.2011

Я генерирую идентификатор на клиенте (а-ля CombGuid.NewGuid()), а затем передаю его конструктору. Подход, когда вы используете удостоверение базы данных, имеет серьезные недостатки

person xelibrion    schedule 08.06.2011

Эрик,

В упомянутом вами сценарии я не вижу выполнения CommitChanges (). Я бы обернул все в область транзакций, а затем нажал бы customerRepository.CommitChanges (), прежде чем добавлять строки заказа. после этого вы сможете получить идентификатор из вновь созданного объекта клиента и использовать его следующим образом:

Order newOrder = new Order(newCustomer.Id, shippingAddress, billingAddress);

затем, если порядок (а) не выполняется, вы можете откатить все назад и сохранить атомарность, не нажимая на scope.Complete ().

надеюсь это поможет..

person jim tollan    schedule 08.06.2011
comment
В примерах, которые я видел, репозиторий находится не там, где фиксируются изменения, как вы описываете. Скорее, каждый репозиторий ссылается на объект UnitOfWork, который можно зафиксировать или откатить. Однако у меня сложилось впечатление, что объект UnitOfWork обычно настроен так, что он может откатывать только те изменения, которые еще не были зафиксированы. - person Eric Anastas; 09.06.2011

Независимо от того, используете ли вы NHibernate или нет, я считаю, что выбранный подход правильный. Ваша цель с любыми объектами домена - всегда иметь в памяти только один экземпляр этого объекта, то есть у вас никогда не должно быть двух объектов, представляющих одну и ту же запись базы данных. Отсюда следует, что если база данных обновила запись с новым идентификатором, объект домена в памяти также должен быть обновлен с этим идентификатором, поскольку это «единственное» истинное представление этой записи.

После вызова Add устанавливается идентификатор объекта, и вы можете вносить дальнейшие изменения в этот объект и вызывать Update, не зная слишком много о своей реализации.

person Tim Rogers    schedule 08.06.2011
comment
Итак, следует предположить, что любая конкретная реализация IRepository.Add () будет устанавливать идентификатор добавляемых элементов? - person Eric Anastas; 09.06.2011

Согласно DDD ваш интерфейс больше похож на DAO, чем на репозиторий:

http://codebetter.com/iancooper/2011/04/12/repository-saveupdate-is-a-smell/

Как упоминал Стив Уилкс, вы должны хранить ссылку на клиента в порядке, а не на идентификатор клиента, поэтому при обработке единицы работы будет создана правильная ссылка на эти сущности в постоянном хранилище (база данных SQL, веб-служба и т. Д.)

Дополнительные сведения о DAO см. Здесь: http://java.sun.com/blueprints/corej2eepatterns/Patterns/DataAccessObject.html

person Tomas Dermisek    schedule 09.06.2011

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

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

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

person Mohamed Abed    schedule 05.01.2012