Классы сущностей отделены от поставщика LINQ to SQL для реализации шаблона репозитория. Как?

Я просмотрел шаблон репозитория и узнал некоторые идеи, которые я использовал в прошлом, которые заставили меня чувствовать себя хорошо.

Однако теперь я хотел бы написать приложение, которое бы использовало этот шаблон НО Я ХОТЕЛ БЫ, ЧТО КЛАССЫ СУЩЕСТВ ДЕКОПИЛИРОВАНЫ от поставщика репозитория.

Я бы создал несколько сборок:

  1. сборка «Интерфейсы», в которой будут размещаться общие интерфейсы, включая интерфейс IRepository
  2. сборка «Сущности», в которой будут размещаться классы сущностей, такие как Продукт, Пользователь, Заказ и так далее. На эту сборку будет ссылаться сборка «Интерфейсы», поскольку некоторые методы будут возвращать такие типы или их массивы. Также на него будет ссылаться основная сборка приложения (например, веб-приложение).
  3. одна или несколько сборок / сборок поставщика репозитория. Каждый из них будет включать (по крайней мере) класс, реализующий интерфейс IRepository, и будет работать с определенным хранилищем данных. Хранилища данных могут включать в себя SQL Server, сервер Oracle, MySQL, файлы XML, веб-службы / службы WCF и так далее.

Изучение LINQ to SQL, которое выглядит очень продуктивным с точки зрения времени, затраченного на реализацию, кажется мне удачным, пока я не обнаружу глубокую зависимость между сгенерированными классами и классом CustomDataContext.

Как я могу использовать LINQ to SQL в таком сценарии?


person Andrei Rînea    schedule 23.10.2008    source источник
comment
Я реализовал именно то, о чем вы просите, и опубликовал код в своем блоге: Общий базовый класс репозитория Linq To SQL для отключенных уровней данных Linq to SQL   -  person Adrian Grigore    schedule 17.08.2009
comment
Это сообщение в блоге представляет собой хорошее решение. iridescence.no/ post /   -  person Riko    schedule 21.08.2009


Ответы (9)


Я не знаю, действительно ли это то, что вам нужно, но вы можете взглянуть на код MVC Storefront Роба Конери. Он использует вариант шаблона репозитория с поставщиком linq. Он сопоставляет объекты LINQ to Sql с объектами домена, а затем возвращает объекты домена от поставщика репозитория на уровень службы, который обертывает поставщика, позволяя ему работать с некоторой логикой с возвращаемыми данными до того, как они попадут на бизнес-уровень.

Веб-трансляции витрины MVC
Код

Для меня это звучит так, как будто вы хотите, чтобы поставщики возвращали DTO, а затем вы хотите сопоставить DTO с объектами домена на уровне репозитория / сервиса. Если это так, вы можете сопоставить своего поставщика LINQ to SQL с DTO, попросить его вернуть их, а затем сопоставить DTO с объектами домена на уровне репозитория / сервиса. Это должно работать нормально, но может стать утомительным, поскольку теперь у вас будет 2 слоя сопоставления.

В этом случае у вас будет: ProductService, который принимает IProductRepository. Он вызывает методы в IProductRepository, чтобы вернуть ваши DTO. Затем он сопоставляет DTO с реальными бизнес-объектами и возвращает их вызывающему коду.

person Daniel Auger    schedule 23.10.2008
comment
Я просто зашел и заглянул в MVC Storefront. Они используют объекты домена, отличные от тех, которые автоматически создаются LINQ. Так что я, наверное, тоже это сделаю. Спасибо за указание на это :) - person Andrei Rînea; 26.10.2008
comment
Классы, сгенерированные проектировщиком L2S, не совсем объекты предметной области. martinfowler.com/eaaCatalog/domainModel.html - person Matt Kocaj; 14.12.2009
comment
Коттсак, точно. Вот почему это решение сопоставляет результаты linq2sql с реальными объектами домена перед их возвратом. - person Daniel Auger; 14.12.2009

Вы можете создать внешний XML-файл, сопоставляющий базу данных с любым классом:

 <?xml version="1.0" encoding="utf-8"?>
 <Database Name="DbName" 
           xmlns="http://schemas.microsoft.com/linqtosql/dbml/2007">
    <Table Name="DbTableName">
       <Type Name="EntityClassName" >
           <Column Name="ID" Type="System.Int64" Member="Id"
                   DbType="BigInt NOT NULL IDENTITY" IsPrimaryKey="true"
                   CanBeNull="false" />
           <Column Name="ColumnName" Type="System.String" Member="PropertyA"
                   DbType="VarChar(1024)" CanBeNull="true" />
       </Type>
    </Table>
 </Database>

А затем передайте XML в класс DataContext:

 using (var cn = GetDbConnection())
  { var mappingSrc = XmlMappingSource.FromReader(xmlReader);

    using (var db = new DataContext(cn, mappingSrc))
     { var q = from entity in db.GetTable<EntityClassName>()
               where entity.PropertyA = "..."
               select entity.ID;
     }
  }
person Mark Cidade    schedule 24.10.2008
comment
Я разместил здесь несколько ссылок о внешнем сопоставлении XML: stackoverflow.com/questions/988872/linq-to-sql-external-mapping/ - person alexandrul; 16.07.2009

Я нашел здесь замечательный пост в блоге (с большим количеством хорошего кода) по этому поводу: http://iridescence.no/post/Linq-to-Sql-Programming-Against-an-Interface-and-the-Repository-Pattern.aspx

person Mladen Mihajlovic    schedule 12.04.2009

Я думаю, вам нужна поддержка POCO (Обычные старые объекты CLR). LINQ to SQL имеет адаптер под названием Close2Poco.

Но я бы посоветовал перейти на Entity Framework, на данный момент у них также есть адаптер POCO. , но в v2 ожидается, что он будет поддерживаться из коробки.

person Davy Landman    schedule 24.10.2008

Вам не нужно использовать сгенерированный LINQ to SQL код, вы можете украсить свои собственные классы необходимыми атрибутами ColumnAttributes или использовать внешний файл сопоставления XML.

person DamienG    schedule 24.10.2008

Самый простой способ - отделить ваши сущности от контекста данных: загрузить необходимую сущность, отделить ее от DataContext, использовать как угодно, а позже использовать Attach (), чтобы связать ее с DataContext для сохранения.

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

public static T CloneEntity<T>(T source)
{
  DataContractSerializer dcs = new DataContractSerializer(typeof(T));
  using (Stream stream = new MemoryStream())
  {
    dcs.WriteObject(stream, source);
    stream.Seek(0, SeekOrigin.Begin);
    return (T)dcs.ReadObject(stream);
  }
}
person Sam    schedule 24.10.2008
comment
Вы можете использовать внешнее отображение XML. - person alexandrul; 16.07.2009

Я сделал нечто подобное с WCF

1 В DBML установите для режима сериализации значение "Однонаправленный".

2 Установите для ВСЕХ столбцов в таблицах значение UpdateCheck = false.

3 Напишите свою услугу примерно так:

   public class Service1 : IService1
    {
        public Company GetCompany(int companyId)
        {
            using (DataClasses1DataContext dc = new DataClasses1DataContext())
            {
                return (from c in dc.Companies where c.CompanyId == companyId select c).Single();
            }
        }

    public void SaveCompany(Company company)
    {
        using (DataClasses1DataContext dc = new DataClasses1DataContext())
        {
            dc.Companies.Attach(company, true);
            dc.SubmitChanges();
        }
    }

    public void InsertCompany(Company company)
    {
        using (DataClasses1DataContext dc = new DataClasses1DataContext())
        {
            dc.Companies.InsertOnSubmit(company);
            dc.SubmitChanges();
        }
    }
}

4 Добавьте ссылку на услугу

person user31259    schedule 24.10.2008

Не совсем тот же сценарий, но я работаю над созданием специального инструмента, который на основе файла XML будет генерировать объектно-ориентированную модель. Мой подход заключается в использовании LINQ to SQL за сценой, и, поскольку я генерирую код автоматически, было бы легко использовать другой механизм, скажем, для источника данных MySQL. Поскольку он не поддерживается LINQ to SQL, вам придется писать код доступа к данным вручную, но клиентский код, который будет использовать объектно-ориентированную модель, изменится в любом случае.

person Albert    schedule 23.10.2008
comment
Конечно, если мне нужно реализовать поставщика репозитория MySQL, я сам напишу код доступа к данным, с этим никаких проблем. Однако не будет ли по крайней мере неудобным использовать для этого сущности, сгенерированные LINQ? - person Andrei Rînea; 23.10.2008
comment
Раньше я использовал DataSets, и это было хорошее решение. Возможно, это не самый элегантный способ, но, безусловно, он работает. - person Albert; 23.10.2008

Могут ли ваши классы Entity реализовывать интерфейсы IProduct, IUser, IOrder и т. Д., Которые будут объявлены в вашей сборке «Интерфейсы»? Таким образом, интерфейс IRepository ссылается только на интерфейсы бизнес-объектов (т. Е. Возвращает коллекции IProduct и т. Д.), А сборка «Интерфейсы» отделяется от других сборок, зависящих от реализации.

person Community    schedule 23.10.2008