Реализация репозитория C #, нужен совет по универсальному исправлению - PullRequest
4 голосов
/ 08 сентября 2011

[Отредактировано: сущности ниже генерируются Entity-Framework]

Я пытаюсь реализовать универсальный репозиторий.Ниже приведены некоторые интерфейсы, которые определяют специализированные черты.

namespace AnimeVoter.DataLayer.Repositories
{
    internal interface ICanCreate<TEntity>
    {
        void Create(TEntity entity);
    }
    internal interface ICanUpdate<TEntity>
    {
        bool Update(TEntity entity);
    }
    internal interface ICanDelete<TEntity>
    {
        bool Delete(TEntity entity);
    }
    internal interface ICanGetList<TEntity>
    {
        IEnumerable<TEntity> GetList();
    }
    internal interface ICanGetById<TEntity>
    {
        TEntity GetById(int id);
    }
}

Теперь у меня также есть абстрактный класс, который комбинирует черты, как показано ниже.

namespace AnimeVoter.DataLayer.Repositories
{
    public abstract class CrudRepository<TEntity> : 
        ICanCreate<TEntity>,
        ICanUpdate<TEntity>,
        ICanDelete<TEntity>,
        ICanGetList<TEntity>,
        ICanGetById<TEntity>
    {
        public abstract void Create(TEntity entity);
        public abstract bool Update(TEntity entity);
        public abstract bool Delete(TEntity entity);
        public abstract IEnumerable<TEntity> GetList();
        public abstract TEntity GetById(int id);
    }
}

Тогда у меня есть где-то 10-15 конкретныхклассы, которые используют абстракцию выше.Я покажу только два.Я также ограничу обсуждение общим методом Create ().

Ниже приведена таблица пользователей в базе данных:

namespace AnimeVoter.DataLayer.Repositories.Impl
{
    public class UserRepository : CrudRepository<User>, IDisposable
    {
        DbEntities db = new DbEntities();

        public override void Create(User entity)
        {
            db.Users.AddObject(entity);
        }

        ...

Ниже приведена таблица заголовков в базе данных:

namespace AnimeVoter.DataLayer.Repositories.Impl
{
    public class TitleRepository : CrudRepository<Title>, IDisposable
    {
        DbEntities db = new DbEntities();

        public override void Create(Title entity)
        {
            db.Titles.AddObject(entity);
        }

        ...

Так вот в чем проблема!Когда я добавляю новую запись в таблицу User, я делаю db.Users.AddObject (entity) .И при добавлении в таблицу заголовков я делаю db.Titles.AddObject (entity) .

Теперь мне интересно, как я могу реорганизовать это, используя дженерики, чтобы я мог просто сделать что-то вроде db <"TableName">. AddObject (entity) или что-нибудь на этот счет, чтобы я мог иметьтолько одна реализация для всех таблиц вместо множества реализаций для каждой из них?

Ответы [ 4 ]

2 голосов
/ 08 сентября 2011

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

public class TitleRepository : CrudRepository<Title>
{
    public TitleRepository()
        : base(new DbEntities())
    {
    }
}

public abstract class CrudRepository<TEntity> :
    ICanCreate<TEntity>,
    ICanUpdate<TEntity>,
    ICanDelete<TEntity>,
    ICanGetList<TEntity>,
    ICanGetById<TEntity>
    where TEntity : EntityObject
{
    private readonly ObjectSet<TEntity> _objectSet;
    private readonly string _primaryKey;

    protected CrudRepository(ObjectContext context)
    {
        this._objectSet = context.CreateObjectSet<TEntity>();
        this._primaryKey = this.GetPrimaryKeyPropertyName();
    }

    public void Create(TEntity entity)
    {
        this._objectSet.AddObject(entity);
        this._objectSet.Context.SaveChanges();
    }

    public bool Update(TEntity entity)
    {
        if (entity.EntityState == EntityState.Detached)
        {
            this._objectSet.Attach(entity);
        }

        this._objectSet.Context.SaveChanges();
        return true;
    }

    public bool Delete(TEntity entity)
    {
        this._objectSet.DeleteObject(entity);
        this._objectSet.Context.SaveChanges();
        return true;
    }

    public IEnumerable<TEntity> GetList()
    {
        return this._objectSet.ToList();
    }

    public TEntity GetById(int id)
    {
        return this._objectSet.Where(this.CreateGetByIdExpression(id)).FirstOrDefault();
    }

    // Build an Expression that can be used to query an Entity by Id.
    private Expression<Func<TEntity, bool>> CreateGetByIdExpression(object id)
    {
        ParameterExpression e = Expression.Parameter(typeof(TEntity), "e");
        PropertyInfo pi = typeof(TEntity).GetProperty(this._primaryKey);
        MemberExpression m = Expression.MakeMemberAccess(e, pi);
        ConstantExpression c = Expression.Constant(id, id.GetType());
        BinaryExpression b = Expression.Equal(m, c);
        Expression<Func<TEntity, bool>> lambda = Expression.Lambda<Func<TEntity, bool>>(b, e);

        return lambda;
    }

    // Use the EF metadata to get the primary key property name.
    private string GetPrimaryKeyPropertyName()
    {
        return this._objectSet.Context
                              .MetadataWorkspace
                              .GetEntityContainer(this._objectSet.Context.DefaultContainerName, DataSpace.CSpace)
                              .BaseEntitySets
                              .First(meta => meta.ElementType == this._objectSet.EntitySet.ElementType)
                              .ElementType.KeyMembers
                              .Select(k => k.Name)
                              .FirstOrDefault();
    }
}
1 голос
/ 08 сентября 2011

Просто замените db.Title на CreateObjectSet. Я делаю что-то вроде этого:

public class CrudRepository<TEntity> where TEntity : class
{
    DbEntities db = new DbEntities();

    public override void Create(TEntity entity)
    {
        db.CreateObjectSet<TEntity>().AddObject(entity);
    }
}

РЕДАКТИРОВАТЬ: Забыл где ...

1 голос
/ 08 сентября 2011

Для этого вы должны определить такие отображения между типом сущности и таблицей БД.Вы можете рассмотреть что-то вроде DbContractFactory и внедрить его в базовый класс CrudRepository<TEntity>, чтобы он мог получить ссылку / имя таблицы во время выполнения на основе текущего типа сущности TEntity, например

dbContractFactory.GetDbContract<TEntity>()

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

РЕДАКТИРОВАТЬ: пример

interface IDbContract
{
   string TableName { get; }
}

public sealed class DbContractFactory
{
   private readonly IDictionary<Type, IDbContract> dbContractMap;

   public void RegisterContract<TEntity>(IDbContract)
   {
         // store in dbContractMap
   }

   public IDbContract GetDbContract<TEntity>()
   {  
       if (dbContractMap.Contains(typeof(TEntity))
       {
          // retrieve and return
       }    
   }
}
0 голосов
/ 09 сентября 2011

Я нашел ответ здесь, http://geekswithblogs.net/seanfao/archive/2009/12/03/136680.aspx. Это очень хорошо, потому что устраняет необходимость иметь несколько объектов репозитория для каждой таблицы, отображаемой EF, особенно для таких мирских операций, как CRUD, что я и искал.

...