Эквивалент универсального репозитория LINQ to SQL в Entity Framework - PullRequest
4 голосов
/ 22 ноября 2010

Я преобразую из LINQ в SQL в Entity Framework для своего ORM и обновляю свои репозитории. Все они сделаны за исключением общего. Кажется, я не могу понять, как преобразовать мой метод Select из того, что есть сейчас, в метод, который работает с EF. Вот текущий код:

public T Select(int Id)
{
    MetaDataMember PrimaryKey = this.DataContext.Mapping.GetTable(typeof(T)).RowType.DataMembers.SingleOrDefault(
        d =>
            (d.IsPrimaryKey));
    ParameterExpression Param = Expression.Parameter(typeof(T), "e");

    var Query = Expression.Lambda<Func<T, bool>>(Expression.Equal(Expression.Property(Param, PrimaryKey.Name), Expression.Constant(Id)), new ParameterExpression[] { Param });

    return this.DataContext.GetTable<T>().Single(Query);
}

Я не писал этого, я скопировал его из блога какого-то человека, и до сих пор он работал для меня. Я действительно не знаю, что он делает, смутная идея, но я не способен перевести это на EF.

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

1 Ответ

2 голосов
/ 22 ноября 2010

Если я правильно понимаю этот метод, он возвращает одну запись для любого типа T на основе первичного ключа.

У нас также есть общий репозиторий, но интерфейс выглядит так:

public interface IRepository<T> where T : class
{
   void Add(T entity);
   void Remove(T entity);
   void Attach(T entity);
   IQueryable<T> Find();
}

И наша общая реализация репозитория:

public class GenericRepository<T> : IRepository<T> where T : class
{
   public IQueryable<T> Find()
   {
      return _ctx.GetEntitySet<T>(); // EF plularization/reflection smarts
   }
}

Итак, чтобы получить эквивалентную одиночную запись для «Поста»:

var postId = 1;
IRepository<Post> repository = new GenericRepository<Post>(); // done by DI in reality
repository
   .Find()
   .SingleOrDefault(x => x.PostId == postId); // explicit predicate to grab record

Это сильно отличается от вашего исходного кода - так как вызывающий код определяет первичный ключ (или способ идентификации уникальной записи).

Честно говоря, пытаться динамически получить уникальную запись, основанную на постоянном значении первичного ключа, довольно безумно - что, если это составной ключ? Я не вижу, как этот код будет работать.

Рад видеть другие ответы, но я бы сказал проще.

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

Если вы хотите, чтобы метод захватил одну запись, позвольте вызывающему коду предоставить предикат / ключ:

public T FindSingle(Expression<Func<T,bool>> predicate)
{
   return _ctx.GetEntitySet<T>.SingleOrDefault(predicate);
}

Тогда, если, например, «Post» имеет составной ключ «PostName» и «PostType»:

var singlePost = repository.FindSingle(post => post.PostName == "Foo" && post.PostType == "Question");

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

Ваш репозиторий должен помочь вашей модели, а не определять ее.

EDIT - код для GetEntitySet<T>, по запросу

public IObjectSet<T> GetEntitySet<T>() where T : class
{
   var entityName = _plularizer.Pluralize(typeof(T).Name);
   string entitySetName = string.Format("{0}.{1}", EntityContainerName, entityName);
   return CreateObjectSet<T>(entitySetName );
}

Довольно просто и довольно безопасно, потому что _plularizer имеет тип System.Data.Entity.Design.PluralizationService, который является тем же модулем, который EF использует для создания имен наборов сущностей по умолчанию.

EntityContainerName - имя вашего контейнера для ваших сущностей.

Ваш экземпляр _plularizer должен быть статическим и должен быть защищен от потоков с полностью заблокированным синглтоном.

НТН.

...