Программирование на интерфейсах и Entity Framework 4.0 - PullRequest
7 голосов
/ 14 октября 2010

Я пытаюсь выяснить, можно ли придерживаться заклинания «Программа против интерфейса, а не реализации».при использовании Entity Framework 4.0.

Хотя я нашел страницу, объясняющую, как придерживаться вышеупомянутого правила при использовании Linq-to-SQL ( смотрите здесь ), я очень хотел бы знать, еслиэто можно было бы сделать с помощью Entity Framework (также с использованием Linq).

Вот как это обычно используется:

var query = from pages in dataContext.Pages where pages.IsPublished select pages;

foreach (Page page in query)
{
    // do something with page...
    var routeQuery = from routes in page.Route where route.IsValid select routes;

    foreach (Route route in routeQuery)
    {
        // do something with route
    }
}

Но я бы хотел использовать это так:

var query = from pages in dataContext.Pages where pages.IsPublished select pages;

foreach (IPage page in query)
{
    // do something with page...
    var routeQuery = from routes in page.Route where route.IsValid select routes;

    foreach (IRoute route in routeQuery)
    {
        // do something with route
    }
}

По сути, я хотел бы иметь возможность передавать DataContext Entity Framework из сборки / подсистемы, где он создается с использованием интерфейса.Вся информация, предоставляемая контекстом данных, должна быть в форме интерфейса, а не фактического класса.

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

Возможно ли это с Entity Framework?Если нет, есть ли какой-либо другой O / R-преобразователь, который можно использовать таким образом?

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

Ответы [ 4 ]

6 голосов
/ 14 октября 2010

Что ж, лучшее решение (на мой взгляд) - это сделать следующее:

Создать Репозитории для вашей модели данных сущности, выставив либо ICollection<T>, либо IQueryable<T>* 1007.*

Используйте интерфейсы в вашем репозитории:

public interface IRepository
{
   public ICollection<Person> Find(string name); // tighter, more maintanability    
   public IQueryable<Person> Find(); // full power! but be careful when lazy loading

Из-за интерфейсов вы можете менять местами макеты, другие ORM:

public class MockRepo : IRepository
{ 
   public List<Person> persons; // mimics entity set
}

Существует так много, что вы можете абстрагироваться.

Если вы беспокоитесь об использовании ObjectSet (который связан с EF), используйте POCO.

Посмотрите некоторые другие мои вопросы для получения дополнительной информации (поскольку мы сейчас строим эту архитектуру).

Кроме того, подумайте об использовании Dependency Injection.Здесь вы можете получить хранилище из бизнеса управления ObjectContext - вы можете внедрить хранилище в единицу работы (которая является оберткой для ObjectContext - чтобы несколько хранилищ или совокупные корни могли обрабатывать один и тот же контекст).

В нашем решении ничто не касается Entity Framework (или любой персистентной логики), кроме хранилищ, которые находятся в отдельной сборке.

HTH.

1 голос
/ 14 октября 2010

Вы можете следовать большей части логики в примере L2S, который вы упомянули для реализации класса EF Repository на основе интерфейса. Одним из основных отличий является метод репозитория. Это немного отличается, потому что EF не работает с Cast <> при приведении из одного IQueryable к другому. Я использовал рефлексию, чтобы обойти эту проблему.

Вот пример метода Repository, переписанного для класса EF Repository:

public IQueryable<T> Repository<T>()
    where T : class
{
    if (typeof(T).IsInterface)
    {
        // if T is an interface then get the actual EntityObject Type by calling GetEntityType
        // so that entityType can be used to call back to this method 
        Type entityType = this.GetEntityType<T>();
        MethodInfo mi = this.GetType().GetMethod("Repository");
        // set the T in Repository<T> to be the entity type
        Type[] genericTypes = new Type[] { entityType };
        mi = mi.MakeGenericMethod(genericTypes);
        // call Repository<T>
        object result = mi.Invoke(this, new object[0]);
        return result as IQueryable<T>;
    }
    else
    {
        return this._context.CreateQuery<T>(this.GetEntitySetName<T>());
    }
}

private Type GetEntityType<T>()
{
    if (this.TableMaps.ContainsKey(typeof(T)))
    {
        return this.TableMaps[typeof(T)];
    }
    else
    {
        return typeof(T);
    }
}
0 голосов
/ 05 сентября 2013

Я достиг этого с EF 5.0. Решение слишком сложное, чтобы оставлять сообщения. Я могу дать описание очень высокого уровня.

Базовый класс для хранилища выглядит так ...

public class GenericRepository<TEntityInterface, TEntity> 
    where TDataInterface : TEntityInterface
    where TEntity : class, IBaseEntityInterface, new()

Подкласс этого хранилища будет выглядеть как ...

public class EmployeeRepository<TEntity> : GenericRepository<IEmployeeEntity, TEntity>, IEmployeeRepository
where TEntity : class, IEmployeeEntity, new()

Я использую Шаблоны T4 , чтобы настроить генерацию сущностей для наследования интерфейсов сущностей, или же вы можете создать частичные классы для каждой сущности EF для наследования интерфейсов. Я использовал сценарии code gen для создания интерфейсов для сущностей, которые соотносятся со свойствами сущностей. Все объекты наследуют IBaseEntityInterface.

В какой-то момент в вашем коде (в моем случае, с использованием инфраструктуры инъекций INject) вы объединяете сущность EF с репозиторием, как это ....

Bind<IEmployeeRepository>().To<EmployeeRepository<EmployeeEntity>();

Где EmployeeEntity создается структурой сущности.

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

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

Однако иногда вам захочется выполнять объединения без навигационных свойств, а обходной путь заключается в предоставлении методов в репозиториях, которые возвращают примитивные объекты IQueryable для использования в других репозиториях, например, запрос идентификаторов, таких как IQueryable или коды IQueryable, создавая в хранилище для другого хранилища, чтобы включить их в запрос.

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

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

    public IList<TEntityInterface> Where(Expression<Func<TEntityInterface, bool>> predicate, params string[] includedProperties)
    {
        DbQuery<TEntity> query = Context.Set<TEntity>();

        foreach (string prop in includedProperties)
            query = query.Include(prop);

        return query.Where(predicate).ToList<TEntityInterface>();
    }

Что касается меня, то ссылка на сущности Entity Framework в вашем коде в основном тесно связывает код с EDMX и, следовательно, с конкретной схемой базы данных. Если вам нужно поддерживать несколько схем баз данных с одной и той же кодовой базой, Entity Framework из коробки оставляет вас довольно высокими и сухими.

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

0 голосов
/ 14 октября 2010

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

...