LINQ, шаблон репозитория и абстракция базы данных - PullRequest
3 голосов
/ 26 июня 2009

Я потратил некоторое время сейчас, чтобы решить проблему, для которой у меня нет решения до сих пор. У меня есть предопределенная очень большая база данных, структура которой фиксирована. Я использую шаблон репозитория для создания уровня абстракции между служебным кодом и логикой базы данных. Проблема в том, что мне нужно применить некоторую обработку к объекту базы данных, прежде чем передавать их из хранилища. Таким образом, я не могу использовать сущности Linq напрямую.

В основном метод хранилища выглядит следующим образом:

public IList<Bookingcode> FindBookingcode(int id) {
   return (from b in _db.BOOKINGCODE
           where b.ID == id
           select new Bookingcode {
              Id = b.ID,
              Name = b.NAME.Trim()
           }).ToList();
}

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

public IList<Bookingcode> FindBookingcode(int id) {
    return (from b in _db.BOOKINGCODE
            join c1 in _db.CATALOG on b.CATALOGID equals c1.ID
            where b.ID == id
            let refs = (
                from bc1 in _db.BOOKINGCODE
                join p in _db.PACKAGE on bc1.ID equals p.BOOKINGCODE
                join bc2 in _db.BOOKINGCODE on p.PACKAGEREF equals bc2.ID
                join c in _db.CATALOG on bc.CATALOGID on bc2.CATALOGID equals c.ID
                where bc1.ID == b.ID
                select new PackageInfo {
                   ID = p.ID
                   BookingcodeRef = new Bookingcode { 
                       ID = bc2.ID,
                       Catalog = new Catalog { ID = c.ID }
                   }
                })
            select new Bookingcode {
              ID = b.ID,
              PackageInfo = refs.ToList()
            }).ToList();

}

У меня также есть некоторая обработка L2O в репозитории, который собирает возвращаемые объекты. Еще одна вещь, для которой у меня нет классного решения, это способ сообщить хранилищу, что он должен получить, например FindBookingcode (id, includePackageInfo, includeCatalog).

Итак, вот вопросы:

1) Этот подход совершенно глуп?

2) Можете ли вы привести меня к решению, которое упрощает переназначение?

3) Как реализовать механизм критериев DDD

Ответы [ 4 ]

3 голосов
/ 26 июня 2009

В моих репозиториях у меня есть отдельный метод сборки, который принимает сущность LINQ to SQL и возвращает бизнес-объект.

Метод сборки выглядит примерно так (this.Container является контейнером Unity IoC и не важен для примера):

private IGroup BuildGroup(Entities.Group group)
{
    IGroup result = this.Container.Resolve<IGroup>();
    result.ID = group.GroupID;
    result.Name = group.Name;

    return result;
}

Затем каждый метод использует метод сборки для возврата бизнес-объекта:

public override IGroup GetByID(int id)
{
    try
    {
        return (from g in this.Context.Groups
                where g.GroupID == id && g.ActiveFlag
                select this.BuildGroup(g)).Single();
    }
    catch (InvalidOperationException)
    {
        return null;
    }
}

Это работает, возвращая каждую сущность LINQ to SQL из базы данных и выполняя ее через метод сборки, так что в этом случае ваш результат будет перечисляемым из ваших бизнес-объектов вместо сущностей LINQ to SQL.

1 голос
/ 26 июня 2009

Если я вызову метод репозитория, как

var list = x.FindBookingcode(int id)
Я ожидал, что смогу добавлять элементы в список и не беспокоиться о том, как он будет сохранен в базе данных. В этом примере реализации функция хранилища не может обнаружить, что кто-то добавил что-то в список, поэтому он ничего не может с этим поделать.

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

1 голос
/ 26 июня 2009

Используете ли вы Linq to SQL или Entity Framework?

Я предположил, что вы используете Entity Framework, поскольку в своем вопросе вы ссылались на «LINQ Entity».

Вы должны быть в состоянии сделать это отображение, используя Entity Framework навигация Свойства . Вам нужно будет добавить свойства навигации к каждому объекту, который вы хотите отобразить таким образом. Это так же просто, как сообщить визуальному дизайнеру, какой атрибут отображается на соответствующий атрибут в другом объекте.

Взгляните на Как создать серию видеороликов Entity Framework для очень краткого руководства по началу работы. Я бы порекомендовал посмотреть всю серию, так как они очень информативны.

0 голосов
/ 29 декабря 2009

Прежде всего - извините за долгое время за мой ответ ^^ Я был настолько занят, что забыл ответить.

Джон, ваше решение вполне подходит для решения моей проблемы с переназначением.

DoctaJonez, я использую Linq2SQL. Я провел несколько тестов с Entity Framework, но также и с NHibernate. Корень проблем с отображением состоит в том, что у меня есть заданная схема базы данных, которая иногда очень уродлива. Так что у меня есть много обработки, чтобы получить доступ к данным. NHibernate работает довольно хорошо, и я думаю, что добавлю его в проект в ближайшее время.

Ганс, ваша точка зрения совершенно верна. Но поскольку проект является просто API-интерфейсом запроса к базе данных, записи в базу данных отсутствуют. Это скорее dll со службами, которые используются другими приложениями для получения информации из программного обеспечения для бронирования, которое используется в моей компании.

Вы упомянули «Технические характеристики», что также является хорошим моментом. Поскольку у меня есть огромные поисковые запросы с большим количеством фильтров, мне действительно нужен хороший API для репозитория. Проблема в том, что, например, разные комбинации фильтров также запускают разную логику обработки. Это также должно создать другой SQL, чтобы быть эффективным.

Поскольку я сопоставил объекты и использую Linq to SQL, я не могу заставить спецификации работать так, как они должны. Итак, у меня есть много методов в хранилище для выборки объектов с разными критериями, например:

Bookingcode FindBookingcode(int id);
Bookingcode FindBookingcode(string code, string catalog);
List<Bookingcode> FindBookingcode();
List<Bookingcode> FindBookingcode(int[] ids);
List<Bookingcode> FindBookingcode(string code);
List<Bookingcode> FindBookingcode(string[] codes);
List<Bookingcode> FindBookingcodes(Catalog catalog);

Поскольку я не использую IQueryable, я не могу связать такие вещи, как

repo.GetBookingcodes().WithCatalog("WI09").WithCode("XYZ10001");

Для больших интерфейсов, таких как Searches, я использую объекты фильтра, например, так:

srv.SearchTransactions(new TransactionSearchFilter { 
    Customer = 1234, Destination = "DXB", ExtractServices = true });

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

    repo.QueryTransactions()
    .Include<Customer>()
    .Include<TransactionsService>()
    .FilterBy(TransactionSearchFilter.Customer, 1234)
    .FilterBy(TransactionSearchFilter.Destination, "DXB")
    .Execute();

, который внутренне создает критерий / спецификацию. Но это действительно похоже на Entity Framework, и, возможно, если я буду глубже, я смогу использовать его или NHibernate.

Самыми проблемными разделами в моем репозитории являются поиски, потому что при необходимости создается некий динамический SQL с точки зрения производительности. Я поиграл с PredicateBuilder, но не смог решить некоторые запросы, в которых я изменяю / включаю объединения таблиц, если установлены определенные фильтры.

Я думаю, мне нужно «отсканировать» больше документации. Спасибо за ваши ответы.

...