Восстановление объектов домена из базы данных: проблема идентификации - PullRequest
1 голос
/ 11 мая 2009

Мы используем Linq to SQL для чтения и записи наших доменных объектов в базу данных SQL Server.

Мы предоставляем ряд услуг (через WCF) для выполнения различных операций. Конкретно реализация этих операций состоит из трех этапов: воссоздание необходимых доменных объектов из базы данных; выполнить операцию над объектами домена; сохранить (теперь измененные) объекты домена обратно в базу данных.

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

public void Move(string sourceLocationid, destinationLocationId, itemId);

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

Эта проблема теперь "решена", проверяя ее вручную, то есть мы восстанавливаем первое местоположение, проверяем, отличается ли его идентификатор от второго, и если да, то повторно устанавливаем второе, и так далее. Это, конечно, сложно и подвержено ошибкам.

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

Моя идея заключается в создании объекта DomainContext для каждой операции, которая отслеживает и кэширует объекты домена, используемые в этом конкретном методе. Вместо того, чтобы восстанавливать и сохранять отдельные доменные объекты, такой объект будет восстановлен и сохранен как единое целое (возможно, с использованием репозиториев), и он может действовать как кэш для доменных объектов, используемых в этой конкретной операции.

В любом случае, кажется, что это обычная проблема, так как с этим обычно справляются? Что вы думаете об идее выше?

Ответы [ 2 ]

1 голос
/ 27 июля 2009

DataContext в Linq-To-Sql поддерживает концепцию Identity Map из коробки и должна кэшировать извлекаемые вами объекты. Объекты будут разными, только если вы не используете один и тот же DataContext для каждой операции GetById ().

Объекты Linq to Sql действительно недопустимы вне времени жизни DataContext. * * * * * * * * * * * * * * * * * * * * 1009

Кроме того, ORM не несет ответственности за логику в домене. Это не запретит вашу примерную Move операцию. Это зависит от домена, чтобы решить, что это значит. Это игнорирует это? или это ошибка? Это логика вашего домена, и она должна быть реализована на границе сервиса, которую вы создаете.

Однако Linq-To-Sql знает, когда объект изменяется, и из того, что я видел, он не будет записывать изменение, если вы переназначаете то же значение. например если Item.LocationID = 12, установка locationID на 12 снова не будет запускать обновление при вызове SubmitChanges().

Исходя из приведенного примера, у меня возникнет соблазн вернуться рано, даже не загружая объект, если источник и пункт назначения совпадают.

public void Move(string sourceLocationId, destinationLocationId, itemId)
{
    if( sourceLocationId == destinationLocationId )
        return;

    using( DataContext ctx = new DataContext() )
    {
       Item item = ctx.Items.First( o => o.ItemID == itemId );
       Location destination = 
          ctx.Locations.First( o => o.LocationID == destinationLocationID );
       item.Location = destination;

       ctx.SubmitChanges();
    }
}

Еще один маленький момент, который может или не может быть применим, заключается в том, что вы должны сделать свои интерфейсы как можно короче. например Если вы, как правило, собираетесь выполнять 10 операций перемещения одновременно, лучше вызвать 1 сервисный метод, чтобы выполнить все 10 операций одновременно, а не 1 операцию за раз. ref: коренастый и болтливый

1 голос
/ 25 июля 2009

Многие ORM используют две концепции, которые, если я вас понимаю, решают вашу проблему. Первый и самый важный - это Context, который отвечает за то, чтобы только один объект представлял сущность (в простом случае строку таблицы базы данных), независимо от того, сколько раз или каким образом он запрашивается из базы данных. Второе - Единица работы; это гарантирует, что обновления базы данных для группы объектов либо все будут выполнены успешно, либо все завершатся неудачно.

Оба они реализованы в ORM, с которым я больше всего знаком (LLBLGen Pro), однако я считаю, что NHibernate и другие также реализуют эти концепции.

...