zoukankan      html  css  js  c++  java
  • DM层与Service层

    首先分享一点自己最近的感悟:讨厌你的人总可以找到理由去讨厌你

    正文开始

    如果您是初次阅读这个系列,请先去《Index & Writing Plan》查找并阅读“架构设计系列”的前两篇文章,顺序阅读会使您有更好的阅读体验

    强烈推荐配合源代码阅读本文:点击此处下载(可以直接运行,会在本地自动生成数据库)

    已经写完了Factory的实现。在Factory中,我们使用了预编译指令来实现了Model的切换:

    复制代码
    #define A
    #if B
    using Model.B;
    using DBaccess.B;
    #endif
    #if A
    using Model.A;
    using DBaccess.A;
    #endif
    复制代码

    切换Model,只需将#define A 修改为 #define B,非常方便(尽管要重新编译,还是让我有点不爽)

    但是出现预编译指令的地方不宜过多——事实上,已经有一处要改就已经让我不爽了

    所以,在DM层(数据操作层)与Service层(业务逻辑层)中,我们不能出现任何具体的Model的类名。

    在本例中,就是DM和Service中不能出现类名:Teacher、Contact

    因为每个出现了类Teacher和类Contact的地方,我们都要加上前文提到的预编译指令。而在实际项目中,这样做会导致切换Model要修改的地方非常多,可能会导致不可预期的错误

     

    那么,DM层主要是用泛型来解决问题,代码如下:

    复制代码
    public class DMbase
        {
            protected DbContext db;
            public DMbase(DbContext db)
            {
                this.db = db;
            }
    
            /// <summary>
            /// select one
            /// </summary>
            /// <typeparam name="T"></typeparam>
            /// <param name="entity">这里的entity并无实际作用,只是用于编译器推敲类型</param>
            /// <param name="predicate">λ表达式</param>
            /// <returns>返回第一条匹配的记录,若无记录返回null</returns>
            public virtual T FindOne<T>(T entity, Func<T, bool> expression) 
                where T : class, new()
            {
                return this.db.Set<T>().FirstOrDefault(expression);
            }
            
            /// <summary>
            /// select all
            /// </summary>
            /// <typeparam name="T"></typeparam>
            /// <param name="entity">这里的entity并无实际作用,只是用于编译器推敲类型</param>
            public virtual IQueryable<T> FindAll<T>(T entity) 
                where T : class, new()
            {
                return this.db.Set<T>();
            }
    
            public virtual void Insert<T>(params T[] entities)
                where T : class, new()
            {
                if (entities != null && entities.Length > 0)
                {
                    var set = this.db.Set<T>();
                    foreach (var item in entities)
                    {
                        set.Add(item);
                    }
                }
            }
    
            public virtual void Delete<T>(params T[] entities)
                where T : class, new()
            {
                if (entities != null && entities.Length > 0)
                {
                    var set = this.db.Set<T>();
                    foreach (var item in entities)
                    {
                        set.Remove(item);
                    }
                }
            }
    
            /// <summary>
            /// 提交事务
            /// </summary>
            public void Commit()
            {
                this.db.SaveChanges();
            }
        }
    复制代码

    我的注释也说明了,其实FindOne方法和FindAll方法其实是不需要参数的,但是为了编译器推敲类型,传入了一个entity参数

    在此要感谢下xanthodont同学,这个DM是在你的版本上改的,封装得很不错

     

    写完DM层,接下来就是重头戏,Service层

    下面的代码示范了如何取得所有的Teacher,并将Teacher与Contact对应起来

    复制代码
            public object FindAllTeacher()
            {
                //取得一个Context
                var context = ContextFactory.GetContext();
    
                //从Factory中取得Teacher和Contact
                //这里必须要用var
                var a = ModelFactory.GetTeacher();
                var b = ModelFactory.GetContact();
                var aList = ModelListFactory.GetTeacherList();
    
                DMbase dm = new DMbase(context);
    
                //用之前取得的Teacher与Contact传入DM层,方便编译器推敲类型
                var contact = dm.FindAll(b);
                var teacher = dm.FindAll(a);
    
                //业务逻辑,将Teacher与Contact关联起来
                var result = teacher.Join(contact, o => o.ID, r => r.TeacherID, (o, r) => new { tt = o, aa = r });
                result = result.OrderByDescending(o => o.aa.Email);
    
                //处理数据
                foreach (var item in result)
                {
                    a = item.tt;
                    a.Contact = item.aa;
                    aList.Add(a);
                }
                return aList;
            }
    复制代码

    注意,这里我使用了Object作为返回类型,因为我其实不知道返回值是IList<Model.A.Teacher> 还是IList<Model.B.Teacher>,好在这里只需要一次拆装箱,无伤大雅

    再来演示下如何插入:

    复制代码
            public bool AddTeacher<T, T1>(T teacher, T1 contact)
                where T : class,new()
                where T1 : class,new()
            {
                try
                {
                    var context = ContextFactory.GetContext();
                    DMbase dm = new DMbase(context);
                    dm.Insert(teacher);
                    dm.Insert(contact);
                    dm.Commit();
    
                    return true;
                }
                catch
                {
                    return false;
                }
            }
    复制代码

    只有执行了dm.Commit(),两个Insert才会提交到数据库,保证了事务的一致性

    就此搁笔

    PS:昨天是博主生日,又老了一岁;有缘读到这里的园友,就别吝啬自己的祝福了吧 :)



    /*=============================================================*/
    作者:CrazyJinn
    本文版权归作者和博客园共有,欢迎转载.但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利. 
    如果看完这篇文章让您有所收获,请点击右下角"推荐". 
    如果这篇文章让您觉得不知所云,或者通篇谬误,请点击右下角"反对".并且欢迎您留言给我提出宝贵的意见. 
    如果您想获知我最新的动态,可以在绿色通道中点击"关注我".
    /*=============================================================*/
    分类: .NET
    标签: 设计架构
  • 相关阅读:
    jquery 实现可编辑div
    【ubuntu firefox】 Firefox is already running, but is not responding
    PO标准form的一点疑问
    hdu3488Tour KM算法
    经典算法学习——归并排序
    Linux 安装Redis全过程日志
    算法竞赛入门经典 习题 2-10 排列(permutation)
    每日一支TED——弗兰斯&#183;兰庭:为动物发声的摄影作品——2015年6月3日
    Linux用户密码策略
    chage命令
  • 原文地址:https://www.cnblogs.com/Leo_wl/p/2747551.html
Copyright © 2011-2022 走看看