Шаблон репозитория с использованием linq

Как я могу реализовать так называемый "шаблон репозитория", который Роб Конери показывает в [MVC Storefront][1], когда я использую два разных сгенерированных кода linq? Нужно ли мне реализовывать реальный шаблон репозитория, как обсуждает Фредрик Нормен, для чего предназначен шаблон репозитория?? Дело в том, что я хочу передать некоторые замечательные функции, которые предоставляет LINQ из моего "хранилища", чтобы я мог использовать его позже, поэтому, если это не требуется, я не хочу реализовывать настоящий шаблон хранилища, о котором говорил Фредрик.

Теперь к моей проблеме. Я скачал [dblinq ][3], который является провайдером Linq для MySql, Oracle и PostgreSQL. В моем проекте я сейчас сгенерировал код LINQ для MySQL и SQL(Microsoft). Проблема заключается в том, что им обоим нужно создавать классы с одинаковыми именами, но с разным содержанием. Могу ли я каким-либо образом реализовать это с помощью пространств имен или чего-то такого, чтобы я мог использовать их оба, как это происходит с "шаблоном хранилища"? Пример для языковой таблицы SQl

[Table(Name="dbo.Language")]
public partial class Language : INotifyPropertyChanging, INotifyPropertyChanged
{

    private static PropertyChangingEventArgs emptyChangingEventArgs = new PropertyChangingEventArgs(String.Empty);

    private int _Id;

    private string _Name;

    private EntitySet<Book> _Books;

#region Extensibility Method Definitions
partial void OnLoaded();
partial void OnValidate(System.Data.Linq.ChangeAction action);
partial void OnCreated();
partial void OnIdChanging(int value);
partial void OnIdChanged();
partial void OnNameChanging(string value);
partial void OnNameChanged();
#endregion

    public Language()
    {
        this._Books = new EntitySet<Book>(new Action<Book>(this.attach_Books), new Action<Book>(this.detach_Books));
        OnCreated();
    }

    [Column(Storage="_Id", AutoSync=AutoSync.OnInsert, DbType="Int NOT NULL IDENTITY", IsPrimaryKey=true, IsDbGenerated=true)]
    public int Id
    {
        get
        {
            return this._Id;
        }
        set
        {
            if ((this._Id != value))
            {
                this.OnIdChanging(value);
                this.SendPropertyChanging();
                this._Id = value;
                this.SendPropertyChanged("Id");
                this.OnIdChanged();
            }
        }
    }

    [Column(Storage="_Name", DbType="NVarChar(100) NOT NULL", CanBeNull=false)]
    public string Name
    {
        get
        {
            return this._Name;
        }
        set
        {
            if ((this._Name != value))
            {
                this.OnNameChanging(value);
                this.SendPropertyChanging();
                this._Name = value;
                this.SendPropertyChanged("Name");
                this.OnNameChanged();
            }
        }
    }

    [Association(Name="Language_Book", Storage="_Books", ThisKey="Id", OtherKey="Language")]
    public EntitySet<Book> Books
    {
        get
        {
            return this._Books;
        }
        set
        {
            this._Books.Assign(value);
        }
    }

    public event PropertyChangingEventHandler PropertyChanging;

    public event PropertyChangedEventHandler PropertyChanged;

    protected virtual void SendPropertyChanging()
    {
        if ((this.PropertyChanging != null))
        {
            this.PropertyChanging(this, emptyChangingEventArgs);
        }
    }

    protected virtual void SendPropertyChanged(String propertyName)
    {
        if ((this.PropertyChanged != null))
        {
            this.PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
        }
    }

    private void attach_Books(Book entity)
    {
        this.SendPropertyChanging();
        entity.Language1 = this;
    }

    private void detach_Books(Book entity)
    {
        this.SendPropertyChanging();
        entity.Language1 = null;
    }
}

MySQL

    [Table(Name = "asp.Language")]
public partial class Language : INotifyPropertyChanged
{
    #region INotifyPropertyChanged handling

    public event PropertyChangedEventHandler PropertyChanged;

    protected virtual void OnPropertyChanged(string propertyName)
    {
        if (PropertyChanged != null)
        {
            PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
        }
    }

    #endregion

    #region int ID

    private int _id;
    [DebuggerNonUserCode]
    [Column(Storage = "_id", Name = "Id", DbType = "int", IsPrimaryKey = true, IsDbGenerated = true, CanBeNull = false)]
    public int ID
    {
        get
        {
            return _id;
        }
        set
        {
            if (value != _id)
            {
                _id = value;
                OnPropertyChanged("ID");
            }
        }
    }

    #endregion

    #region string Name

    private string _name;
    [DebuggerNonUserCode]
    [Column(Storage = "_name", Name = "Name", DbType = "varchar(100)", CanBeNull = false)]
    public string Name
    {
        get
        {
            return _name;
        }
        set
        {
            if (value != _name)
            {
                _name = value;
                OnPropertyChanged("Name");
            }
        }
    }

    #endregion

    #region Children

    private EntitySet<Book> _book;
    [Association(Storage = "_book", OtherKey = "Language", ThisKey = "ID", Name = "Book_ibfk_1")]
    [DebuggerNonUserCode]
    public EntitySet<Book> Book
    {
        get
        {
            return _book;
        }
        set
        {
            _book = value;
        }
    }


    #endregion

    #region Attachement handlers

    private void Book_Attach(Book entity)
    {
        entity.LanguageLanguage = this;
    }

    private void Book_Detach(Book entity)
    {
        entity.LanguageLanguage = null;
    }


    #endregion

    #region ctor

    public Language()
    {
        _book = new EntitySet<Book>(Book_Attach, Book_Detach);
    }

    #endregion

}

4 ответа

Решение

В идеале вы должны создавать каждый класс только один раз, а ORM должен учитывать различия в базе данных (именно так работают NHibernate и Subsonic).

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

// C# sample
namespace MyCompany.MyApp.Entities.Oracle
{
    public class MyClass
    {
    // ...
    }
}

namespace MyCompany.MyApp.Entities.SqlServer
{
    public class MyClass
    {
    // ...
    }
}

Если классы генерируются автоматически, вы должны проверить в инструменте генерации кода, как задать пространства имен. Например, в конструкторе LINQ to SQL вы можете указать пространство имен в свойствах таблицы / класса или отредактировать файл DBML самостоятельно.

Это немного оффтоп, но это может помочь.

Если вы используете Linq, репозитории, MySql и MS Sql и советы Роба Коннери, вы также можете использовать SubSonic 3 и сделать свою жизнь немного проще.

Вот 5-минутная демонстрация: http://subsonicproject.com/docs/Simple_Repo_5_Minute_Demo

Я бы предложил пространство имен сущностей для вашего BO, например MyCompany.MyApp.Entities.

Затем вы можете изменить свойство Entity Namespace каждого DBML, чтобы сгенерировать в нем ваши сущности. Если вы используете более одного DBML для одних и тех же типов, вам потребуется отдельное пространство имен для каждого DBML, например, MyCompany.MyApp.Entities.MySql).

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

По своему опыту я недавно создал абстрактную LinqToSqlRepostory, а затем расширил ее для своих основных бизнес-объектов.

Я нахожусь в той же ситуации, что и вы, я использую Linq-to-SQL, но в будущем я хочу попробовать Entity Framework, и я это сделаю.

Как я понял, решением этой проблемы является использование внедрения зависимостей и контейнера ioc (если вы его не используете, посмотрите, может быть, сложно, но очень просто и интересно).

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

Например, у вас есть ICustomersRepositoryэто то, что вы можете использовать в своих контроллерах, и SQLCustomersRepository и EntityCustomersRepository, что вы можете поменять местами только изменяя файл конфигурации.

Как вы сказали, проблема в том, что сущности с одинаковым именем... решение состоит в том, чтобы использовать один и тот же шаблон для сущностей, создать интерфейс для каждой сущности и использовать контейнер ioc. Я знаю, что это утомительно, и я бы создал шаблон T4, который автоматически создает интерфейсы для каждой сущности, а затем заменяет шаблоны T4, использующие LinqToSQL и среду entiy для его реализации... да, это большая работа.

Для ознакомления с T4 посмотрите это видео, http://msdn.microsoft.com/en-us/vstudio/cc308634.aspx

Другие вопросы по тегам