Запутался насчет Linq to SQL и репозиториев - PullRequest
4 голосов
/ 16 марта 2010

Последние 4 года я работаю разработчиком ASP.NET в небольшой компании, где все приложения создаются с использованием анти-паттерна Smart UI в .NET 2.0. Это означает, что у меня нет опыта работы с .NET 3.5 и такими вещами, как LINQ, и общими понятиями, такими как хранилище и уровни обслуживания. Я понимаю, что для того, чтобы найти новую работу, мне нужно обновить свои знания, поэтому я начал читать книги, блоги и много вопросов типа SO, таких как этот , и пришло время попробовать простое приложение с тем, что я должен был выучить.

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

Это самая базовая схема базы данных, которую я придумал:

The database
(источник: kristofclaes.be )

Я перевел эту диаграмму в следующие классы (я опустил атрибуты Linq to SQL):

class Project
{
    public int ID { get; internal set; }
    public string Name { get; set; }
    public string Description { get; set; }

    private EntitySet<Bug> bugs;
    public EntitySet<Bug> Bugs
    {
        get { return this.bugs; }
        set { this.bugs.Assign(value); }
    }
}

class Bug
{
    public int ID { get; internal set; }
    public string Summary { get; set; }
    public string Description { get; set; }

    private EntityRef<Project> belongsTo;
    public Project BelongsTo
    {
        get { return this.belongsTo.Entity; }
        set { this.belongsTo.Entity = value; }
    }

    private EntityRef<Person> currentStatusSetBy;
    public Person CurrentStatusSetBy
    {
        get { return this.currentStatusSetBy.Entity; }
        set { this.currentStatusSetBy.Entity = value; }
    }

    public Datetime CurrentStatusSetOn { get; set; }
    public BugStatus CurrentStatus { get; set; }

    private EntitySet<BugStatusHistory> previousStatuses
    public EntitySet<BugStatusHistory> PreviousStatuses
    {
        get { return this.previousStatuses; }
        set { this.previousStatuses.Assign(value); }
    }
}

class BugStatusHistory
{
    public int ID { get; internal set; }
    public DateTime StatusSetAt { get; set; }   
    public BugStatus Status { get; set; }

    private EntityRef<Person> statusSetBy;
    public Person StatusSetBy
    {
        get { return this.statusSetBy.Entity; }
        set { this.statusSetBy.Entity = value; }
    }
}

class Person
{
    public ID { get; internal set; }
    public string Name {get; set; }
    public string Email { get; set; }
    public string Login { get; set; }
    public string Password { get; set; }
}

enum BugStatus { New, Confirmed, Solved }

Я поместил эти классы в библиотеку классов с именем DomainModel и хочу сослаться на этот DomainModel из приложения ASP.NET MVC 2. Из того, что я прочитал, я должен также добавить репозитории и, возможно, даже сервисный уровень в мою DomainModel. Это то, что меня смущает.

Я прочитал, что вы не должны создавать репозиторий для каждого класса / таблицы, но вы должны создавать репозиторий для агрегатов (групп классов). Если сущность не может существовать вне контекста другой сущности, у нее не должно быть своего собственного хранилища. В моем примере ошибка всегда связана с проектом. Означает ли это, что я должен создать хранилище для совокупного Project-Bug? Что делать, если я хочу отобразить список всех ошибок, независимо от того, в каком проекте они находятся. Должен ли я добавить метод GetAllBugs () в мой IProjectsRepository? Или я должен создать отдельный IBugsRepository для этого использования?

Я думаю, что создание отдельных репозиториев может иметь свои преимущества. Из того, что я читал о Linq to SQL, вы можете установить свойство в DataContext, чтобы указать, как обрабатывать ленивую и энергичную загрузку. Теперь, когда я получаю список проектов или одного проекта, я хочу с нетерпением загрузить список ошибок. Но я не хочу загружать проект каждой ошибки в этом списке. Но если я хочу загрузить список всех ошибок (независимо от проекта) или одну ошибку, я do хочу загрузить проект с нетерпением, но в этом случае я не хотят загружать список ошибок в этом проекте. Мои знания Linq to SQL очень ограничены, но разве это не может быть достигнуто только путем установки свойств DataContext? Разве это не потребовало бы от меня наличия DataContext для проектов и DataContext для ошибок и, следовательно, двух хранилищ? Разве невозможно как-то сказать DataContext с нетерпением загружать до 2-х уровней и выполнять ленивую загрузку для чего-то более глубокого? Или все это не имеет значения из-за отложенного выполнения?

Пожалуйста, извините меня за мой длинный, длинный вопрос (который, возможно, даже не настолько ясен), но вся эта новая информация действительно смущает меня.

(если вы хотите прокомментировать мою схему базы данных / структуру классов, пожалуйста, не жалейте меня: -))

1 Ответ

2 голосов
/ 16 марта 2010

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

Шаблон репозитория - болячка с L2S, потому что он усложняет Updates - вы раздаете объект из репозитория (который открыл и закрыл DataContext, чтобы получить его - они должны быть недолговечными, как вы видите), вы модифицируете его свойства и отправить его обратно для обновления - но вы не можете, потому что вам нужен оригинальный DataContext. Вам нужно снова получить объект из нового контекста данных, скопировать измененные значения и обновить второе.

Чтобы избежать этого, вы должны начать передавать контекст данных в свой репозиторий для большинства операций (позволяя вызывающей стороне определять время жизни контекста данных); который раздувает все ваши сигнатуры метода и вызывает всестороннюю боль в a * s.

Красота L2S, для меня, в том, что это скорость - поэтому я содрогаюсь, когда вижу, как люди без необходимости оборачивают DC ради него.

Не поймите меня неправильно - у меня есть система, которую я сейчас пишу, где каждый объект и «служба данных» абстрагированы от интерфейсов; и некоторые из этих служб данных реализованы с использованием Linq To Sql; но это потому, что вся система предназначена для горячей замены для всей платформы. Использовать L2S было сложно, но все же выполнимо.

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

Если вы обнаружите, что вам нужен детальный контроль над тем, какие вещи выполнять лениво и активно, то вам не нужны отдельные контроллеры домена - вы просто переносите создание текстового контекста данных на разные методы, приспособленные для различных нужд:

public MyDataContext GetDCForProjects()
{
  var DC = new MyDataContext();
  DataLoadOptions dlo = new DataLoadOptions();
  dlo.LoadWith<Project>(p => p.Bugs);
  DC.LoadOptions = dlo;
  return DC;
}

//add more methods for the different needs
...