目录

  • 分享基于ef6、unitwork、autofac的repository模式设计
    • 二、repository设计具体的实现代码
    • 三、repository设计的具体的使用

分享基于ef6、unitwork、autofac的repository模式设计

一、实现的思路和结构图

  • repository的共同性

有一些公共的方法(增删改查), 这些方法无关于repository操作的是哪个实体类,可以把这些方法定义成接口irepository ,然后有个基类baserepository 实现该接口的方法。常见的方法,比如find, filter, delete, create等

  • repository的差异性

每个repository类又会有一些差异性,应当允许它们能够继承baserepository 之外,还能够再扩展自己的一些方法。所以每个类都可以再定义一个自己特有的接口,定义一些属于自己repository的方法。

  • repository的协同性

    不同的repository可能需要协同,repository对数据的修改,需要在统一的保存.
    最终实现的类结构图如下:

二、repository设计具体的实现代码

irepository 接口定义了repository共有的方法, baserepository 实现了这些接口的方法。其它的repository类再集成baserepository 方法,就天然的得到了对数据操作的基本方法。

  • irepository 代码
    public interface irepository<tentity> where tentity : class
    {
        /// <summary>
        /// gets all objects from database
        /// </summary>
        /// <returns></returns>
        iqueryable<tentity> all();

        /// <summary>
        /// gets objects from database by filter.
        /// </summary>
        /// <param name="predicate">specified a filter</param>
        /// <returns></returns>
        iqueryable<tentity> filter(expression<func<tentity, bool>> predicate);

        /// <summary>
        /// gets objects from database with filtering and paging.
        /// </summary>
        /// <param name="filter">specified a filter</param>
        /// <param name="total">returns the total records count of the filter.</param>
        /// <param name="index">specified the page index.</param>
        /// <param name="size">specified the page size</param>
        /// <returns></returns>
        iqueryable<tentity> filter(expression<func<tentity, bool>> filter, out int total, int index = 0, int size = 50);

        /// <summary>
        /// gets the object(s) is exists in database by specified filter.
        /// </summary>
        /// <param name="predicate">specified the filter expression</param>
        /// <returns></returns>
        bool contains(expression<func<tentity, bool>> predicate);

        /// <summary>
        /// find object by keys.
        /// </summary>
        /// <param name="keys">specified the search keys.</param>
        /// <returns></returns>
        tentity find(params object[] keys);

        /// <summary>
        /// find object by specified expression.
        /// </summary>
        /// <param name="predicate"></param>
        /// <returns></returns>
        tentity find(expression<func<tentity, bool>> predicate);

        /// <summary>
        /// create a new object to database.
        /// </summary>
        /// <param name="t">specified a new object to create.</param>
        /// <returns></returns>
        void create(tentity t);

        /// <summary>
        /// delete the object from database.
        /// </summary>
        /// <param name="t">specified a existing object to delete.</param>
        void delete(tentity t);

        /// <summary>
        /// delete objects from database by specified filter expression.
        /// </summary>
        /// <param name="predicate"></param>
        /// <returns></returns>
        int delete(expression<func<tentity, bool>> predicate);

        /// <summary>
        /// update object changes and save to database.
        /// </summary>
        /// <param name="t">specified the object to save.</param>
        /// <returns></returns>
        void update(tentity t);

        /// <summary>
        /// select single item by specified expression.
        /// </summary>
        /// <param name="expression"></param>
        /// <returns></returns>
        tentity firstordefault(expression<func<tentity, bool>> expression);
    }
  • baserepository 代码
    public class baserepository<tentity> : irepository<tentity> where tentity : class
    {
        protected readonly dbcontext context;

        public baserepository(dbcontext context)
        {
            context = context;
        }

        /// <summary>
        /// gets all objects from database
        /// </summary>
        /// <returns></returns>
        public iqueryable<tentity> all()
        {
            return context.set<tentity>().asqueryable();
        }

        /// <summary>
        /// gets objects from database by filter.
        /// </summary>
        /// <param name="predicate">specified a filter</param>
        /// <returns></returns>
        public virtual iqueryable<tentity> filter(expression<func<tentity, bool>> predicate)
        {
            return context.set<tentity>().where<tentity>(predicate).asqueryable<tentity>();
        }

        /// <summary>
        /// gets objects from database with filtering and paging.
        /// </summary>
        /// <param name="filter">specified a filter</param>
        /// <param name="total">returns the total records count of the filter.</param>
        /// <param name="index">specified the page index.</param>
        /// <param name="size">specified the page size</param>
        /// <returns></returns>
        public virtual iqueryable<tentity> filter(expression<func<tentity, bool>> filter, out int total, int index = 0,
            int size = 50)
        {
            var skipcount = index * size;
            var resetset = filter != null
                ? context.set<tentity>().where<tentity>(filter).asqueryable()
                : context.set<tentity>().asqueryable();
            resetset = skipcount == 0 ? resetset.take(size) : resetset.skip(skipcount).take(size);
            total = resetset.count();
            return resetset.asqueryable();
        }

        /// <summary>
        /// gets the object(s) is exists in database by specified filter.
        /// </summary>
        /// <param name="predicate">specified the filter expression</param>
        /// <returns></returns>
        public bool contains(expression<func<tentity, bool>> predicate)
        {
            return context.set<tentity>().any(predicate);
        }

        /// <summary>
        /// find object by keys.
        /// </summary>
        /// <param name="keys">specified the search keys.</param>
        /// <returns></returns>
        public virtual tentity find(params object[] keys)
        {
            return context.set<tentity>().find(keys);
        }

        /// <summary>
        /// find object by specified expression.
        /// </summary>
        /// <param name="predicate"></param>
        /// <returns></returns>
        public virtual tentity find(expression<func<tentity, bool>> predicate)
        {
            return context.set<tentity>().firstordefault<tentity>(predicate);
        }

        /// <summary>
        /// create a new object to database.
        /// </summary>
        /// <param name="t">specified a new object to create.</param>
        /// <returns></returns>
        public virtual void create(tentity t)
        {
            context.set<tentity>().add(t);
        }

        /// <summary>
        /// delete the object from database.
        /// </summary>
        /// <param name="t">specified a existing object to delete.</param>
        public virtual void delete(tentity t)
        {
            context.set<tentity>().remove(t);
        }

        /// <summary>
        /// delete objects from database by specified filter expression.
        /// </summary>
        /// <param name="predicate"></param>
        /// <returns></returns>
        public virtual int delete(expression<func<tentity, bool>> predicate)
        {
            var objects = filter(predicate);
            foreach (var obj in objects)
                context.set<tentity>().remove(obj);
            return context.savechanges();
        }

        /// <summary>
        /// update object changes and save to database.
        /// </summary>
        /// <param name="t">specified the object to save.</param>
        /// <returns></returns>
        public virtual void update(tentity t)
        {
            try
            {
                var entry = context.entry(t);
                context.set<tentity>().attach(t);
                entry.state = entitystate.modified;
            }
            catch (optimisticconcurrencyexception ex)
            {
                throw ex;
            }
        }

        /// <summary>
        /// select single item by specified expression.
        /// </summary>
        /// <param name="expression"></param>
        /// <returns></returns>
        public tentity firstordefault(expression<func<tentity, bool>> expression)
        {
            return all().firstordefault(expression);
        }
    }

iunitofwork接口定义了方法获取特定的repository, 执行存储过程, savechange方法提交修改,统一更新数据。

  • iunitofwork接口代码:
    public interface iunitofwork : idisposable
    {
        dbcontext dbcontext { get; }
        trepository getrepository<trepository>() where trepository : class;
        void executeprocedure(string procedurecommand, params object[] sqlparams);
        void executesql(string sql);
        list<t> sqlquery<t>(string sql);
        void savechanges();
    }

unitofwork代码, 代码中使用到了autofac中的icomponentcontext来获取repository实例

    public class unitofwork : iunitofwork
    {
        private readonly icomponentcontext _componentcontext;
        protected readonly dbcontext context;

        public unitofwork(dbcontext context, icomponentcontext componentcontext)
        {
            context = context;
            _componentcontext = componentcontext;
        }

        public dbcontext dbcontext => context;

        public trepository getrepository<trepository>() where trepository : class
        {
            return _componentcontext.resolve<trepository>();
        }

        public void executeprocedure(string procedurecommand, params object[] sqlparams)
        {
            context.database.executesqlcommand(procedurecommand, sqlparams);
        }

        public void executesql(string sql)
        {
            context.database.executesqlcommand(sql);
        }

        public list<t> sqlquery<t>(string sql)
        {
            return context.database.sqlquery<t>(sql).tolist();
        }

        public void savechanges()
        {
            try
            {
                context.savechanges();
            }
            catch (invalidoperationexception ex)
            {
                if (!ex.message.contains("the changes to the database were committed successfully"))
                {
                    throw;
                }
            }
        }

        public void dispose()
        {
            context?.dispose();
        }
    }

三、repository设计的具体的使用

这里我们定义一个istudentrepository接口, 包含了方法getallstudents(), 同时继承于irepository<student>接口

public interface istudentrepository : irepository<student>
{
    ienumerable<dynamic> getallstudents();
}

接着定义studentrepository类来实现这个接口

public class studentrepository : baserepository<student>, istudentrepository
{
    private readonly schoolcontext _context;

    public studentrepository(schoolcontext context)
        : base(context)
    {
        _context = context;
    }

    public ienumerable<dynamic> getallstudents()
    {
        return _context.students;
    }
}
  • application_start方法中使用autofac注册repository的代码如下:
    var builder = new containerbuilder();

    //register controllers
    builder.registercontrollers(typeof(mvcapplication).assembly);

    //register repository
    builder.registerassemblytypes(assembly.getexecutingassembly()).asimplementedinterfaces();

    //add the entity framework context to make sure only one context per request
    builder.registertype<schoolcontext>().instanceperrequest();
    builder.register(c => c.resolve<schoolcontext>()).as<dbcontext>().instanceperrequest();

    var container = builder.build();
    dependencyresolver.setresolver(new autofacdependencyresolver(container));
  • 在控制器中注入使用repository的代码如下:
private readonly iunitofwork _repositorycenter;

private readonly istudentrepository _studentrepository;

public homecontroller(iunitofwork repositorycenter)
{
    _repositorycenter = repositorycenter;
    _studentrepository = _repositorycenter.getrepository<istudentrepository>();
}

public actionresult index(student sessionstudent)
{
    var students = _studentrepository.getallstudents();

    // 同时你也可以使用定义于irepository<student>中的方法, 比如:

    _studentrepository.delete(students.first());
    _repositorycenter.savechanges();

    ...

    return view(students);
}

四、思路总结

上面的设计,把repository的通用代码剥离到父类中,同时又允许每个repository扩展自己的方法,达到了比较理想的状态。

只是现在的设计和autofac耦合了,但是如果想继续剥离autofac直接使用 _repositorycenter.getrepository<istudentrepository>(); 的方式获取istudentrepository的实例就很困难了。

五、案例源码

源代码仓库 autofacmvc