热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

.NetCoreMVC网站开发(Ninesky)2.3、项目架构调整(续)使用配置文件动态注入

上次实现了依赖注入,但是web项目必须要引用业务逻辑层和数据存储层的实现,项目解耦并不完全;另一方面,要同时注入业务逻辑层和数据访问层,注入的服务直接写在Startup中显得非常臃

上次实现了依赖注入,但是web项目必须要引用业务逻辑层和数据存储层的实现,项目解耦并不完全;另一方面,要同时注入业务逻辑层和数据访问层,注入的服务直接写在Startup中显得非常臃肿。理想的方式是,web项目近引用接口而不引用实现,在配置文件中进行配置实现程序集合类,注入业务逻辑层而不必注入数据访问层。

一、数据访问层

在项目中摒弃数据访问层或者使用EntityFramework作为数据访问层。

在项目中数据访问层主要实现数据的存储,仔细看一下EntityFramework发现DbContext的功能完全实现了查、增、删、改等各种操作,并且有缓存等功能,本身就实现了仓储模式,并且比自己封装的数据存储层的功能还强大,干脆在项目中用EntityFramework作为数据存储层。删除掉Ninesky.InterfaceDataLibrary项目和Ninesky.DataLibrary项目。

注:项目结构调整的确实太频繁了,以后一段时间内绝不再调整了。

二、实现业务逻辑层。

添加业务逻辑层接口项目Ninesky.InterfaceBase

1、添加接口基类接口InterfaceBaseService,添加基本的查、增、删、改方法

技术分享技术分享
using Ninesky.Models;
using System;
using System.Linq;
using System.Linq.Expressions;

namespace Ninesky.InterfaceBase
{
    /// 
    ///  服务基础接口
    /// 
    public interface InterfaceBaseService where T:class
    {

        /// 
        /// 添加
        /// 
        /// 实体
        /// 是否立即保存
        /// 添加的记录数
        int Add(T entity, bool isSave = true);

        /// 
        /// 添加[批量]
        /// 
        /// 实体
        /// 是否立即保存
        /// 添加的记录数
        int AddRange(T[] entities, bool isSave = true);

        /// 
        /// 查询记录数
        /// 
        /// 查询条件
        /// 记录数
        int Count(Expressionbool>> predicate);

        /// 
        /// 查询是否存在
        /// 
        /// 查询条件
        /// 是否存在
        bool Exists(Expressionbool>> predicate);

        /// 
        /// 查找
        /// 
        /// 主键
        /// 
        T Find(int Id);

        /// 
        /// 查找
        /// 
        /// 主键
        /// 
        T Find(object[] keyValues);


        /// 
        /// 查找
        /// 
        /// 查询条件
        /// 
        T Find(Expressionbool>> predicate);


        IQueryable FindList(int number, Expressionbool>> predicate);

        /// 
        /// 查询
        /// 
        /// 排序属性
        /// 显示数量[小于等于0-不启用]
        /// 查询条件
        /// 排序
        /// 正序
        /// 
        IQueryable FindList(int number, Expressionbool>> predicate, Expression> keySelector, bool isAsc);

        /// 
        /// 查询[分页]
        /// 
        /// 排序属性
        /// 查询条件
        /// 排序
        /// 是否正序
        /// 分页数据
        /// 
        Paging FindList(Expressionbool>> predicate, Expression> keySelector, bool isAsc, Paging paging);

        /// 
        /// 查询[分页]
        /// 
        /// 排序属性
        /// 查询条件
        /// 排序
        /// 是否正序
        /// 当前页
        /// 每页记录数
        /// 
        Paging FindList(Expressionbool>> predicate, Expression> keySelector, bool isAsc, int pageIndex, int pageSize);

        /// 
        /// 删除
        /// 
        /// 实体
        /// 是否立即保存
        /// 是否删除成功
        bool Remove(T entity, bool isSave = true);

        /// 
        /// 删除[批量]
        /// 
        /// 实体数组
        /// 是否立即保存
        /// 成功删除的记录数
        int RemoveRange(T[] entities, bool isSave = true);


        /// 
        ///  保存到数据库
        /// 
        /// 更改的记录数
        int SaveChanges();

        /// 
        /// 更新
        /// 
        /// 实体
        /// 是否立即保存
        /// 是否保存成功
        bool Update(T entity, bool isSave = true);

        /// 
        /// 更新[批量]
        /// 
        /// 实体数组
        /// 是否立即保存
        /// 更新成功的记录数
        int UpdateRange(T[] entities, bool isSave = true);

    }
}
View Code

2、在Ninesky.Base中添加,接口InterfaceBaseService的实现类BaseService.cs

技术分享技术分享
using Microsoft.EntityFrameworkCore;
using Ninesky.InterfaceBase;
using Ninesky.Models;
using System;
using System.Linq;
using System.Linq.Expressions;

namespace Ninesky.Base
{
    /// 
    /// 服务基类
    /// 
    public class BaseService:InterfaceBaseService where T:class
    {
        protected DbContext _dbContext;
        public BaseService(DbContext dbContext)
        {
            _dbCOntext= dbContext;
        }

        public virtual int Add(T entity, bool isSave = true)
        {
            _dbContext.Set().Add(entity);
            if (isSave) return _dbContext.SaveChanges();
            else return 0;
        }

        public virtual int AddRange(T[] entities, bool isSave = true)
        {
            _dbContext.Set().AddRange(entities);
            if (isSave) return _dbContext.SaveChanges();
            else return 0;
        }

        /// 
        /// 查询记录数
        /// 
        /// 查询条件
        /// 记录数
        public virtual int Count(Expressionbool>> predicate)
        {
            return _dbContext.Set().Count(predicate);
        }

        /// 
        /// 查询是否存在
        /// 
        /// 查询条件
        /// 是否存在
        public virtual bool Exists(Expressionbool>> predicate)
        {
            return Count(predicate) > 0;
        }

        /// 
        /// 查找
        /// 
        /// 主键
        /// 
        public virtual T Find(int Id)
        {
            return _dbContext.Set().Find(Id);
        }

        public virtual T Find(object[] keyValues)
        {
            return _dbContext.Set().Find(keyValues);
        }

        public virtual T Find(Expressionbool>> predicate)
        {
            return _dbContext.Set().SingleOrDefault(predicate);
        }

        public virtual IQueryable FindList(int number, Expressionbool>> predicate)
        {
            var entityList = _dbContext.Set().Where(predicate);
            if (number > 0) return entityList.Take(number);
            else return entityList;
        }

        public virtual IQueryable FindList(int number, Expressionbool>> predicate, Expression> keySelector, bool isAsc)
        {
            var entityList = _dbContext.Set().Where(predicate);
            if (isAsc) entityList = entityList.OrderBy(keySelector);
            else entityList.OrderByDescending(keySelector);
            if (number > 0) return entityList.Take(number);
            else return entityList;
        }


        public virtual Paging FindList(Expressionbool>> predicate, Expression> keySelector, bool isAsc, Paging paging)
        {
            var entityList = _dbContext.Set().Where(predicate);
            paging.Total = entityList.Count();
            if (isAsc) entityList = entityList.OrderBy(keySelector);
            else entityList.OrderByDescending(keySelector);
            paging.Entities = entityList.Skip((paging.PageIndex - 1) * paging.PageSize).Take(paging.PageSize).ToList();
            return paging;
        }

        public virtual Paging FindList(Expressionbool>> predicate, Expression> keySelector, bool isAsc, int pageIndex, int pageSize)
        {
            Paging paging = new Paging { PageIndex = pageIndex, PageSize = pageSize };
            return FindList(predicate, keySelector, isAsc, paging);
        }

        /// 
        /// 删除
        /// 
        /// 实体
        /// 是否立即保存
        /// 是否删除成功
        public virtual bool Remove(T entity, bool isSave = true)
        {
            _dbContext.Set().Remove(entity);
            if (isSave) return _dbContext.SaveChanges() > 0;
            else return false;
        }

        /// 
        /// 删除[批量]
        /// 
        /// 实体数组
        /// 是否立即保存
        /// 成功删除的记录数
        public virtual int RemoveRange(T[] entities, bool isSave = true)
        {
            _dbContext.Set().RemoveRange(entities);
            if (isSave) return _dbContext.SaveChanges();
            else return 0;
        }


        public virtual int SaveChanges()
        {
            return _dbContext.SaveChanges();
        }

        /// 
        /// 更新
        /// 
        /// 实体
        /// 是否立即保存
        /// 是否保存成功
        public virtual bool Update(T entity, bool isSave = true)
        {
            _dbContext.Set().Update(entity);
            if (isSave) return _dbContext.SaveChanges() > 0;
            else return false;
        }

        /// 
        /// 更新[批量]
        /// 
        /// 实体数组
        /// 是否立即保存
        /// 更新成功的记录数
        public virtual int UpdateRange(T[] entities, bool isSave = true)
        {
            _dbContext.Set().UpdateRange(entities);
            if (isSave) return _dbContext.SaveChanges();
            else return 0;
        }
    }
}
View Code

3、在Ninesky.InterfaceBase项目中添加栏目接口InterfaceCategoryService.cs,新增了一个Findtree的方法

技术分享技术分享
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Ninesky.Models;

namespace Ninesky.InterfaceBase
{
    /// 
    /// 栏目服务接口
    /// 
    public interface InterfaceCategoryService:InterfaceBaseService
    {
        /// 
        /// 查找树形菜单
        /// 
        /// 栏目类型,可以为空
        /// 
        List FindTree(CategoryType? categoryType);
    }
}
View Code

4、在Ninesky.Base中添加栏目接口的实现类CategoryService.cs

技术分享技术分享
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.EntityFrameworkCore;
using Ninesky.Models;
using Ninesky.InterfaceBase;

namespace Ninesky.Base
{
    /// 
    /// 栏目服务类
    /// 
    public class CategoryService:BaseService,InterfaceCategoryService
    {
        public CategoryService(DbContext dbContext):base(dbContext)
        {
        }
        /// 
        /// 查找
        /// 
        /// 栏目ID
        /// 
        public override Category Find(int Id)
        {
            return _dbContext.Set().Include("General").Include("Page").Include("Link").SingleOrDefault(c => c.CategoryId == Id);
        }

        /// 
        /// 查找树形菜单
        /// 
        /// 栏目类型,可以为空
        /// 
        public List FindTree(CategoryType? categoryType)
        {
            var categories = _dbContext.Set().AsQueryable();
            //根据栏目类型分类处理
            switch (categoryType)
            {
                case null:
                    break;
                case CategoryType.General:
                    categories = categories.Where(c => c.Type == categoryType);
                    break;
                    //默认-Page或Link类型
                default:
                    //Id数组-含本栏目及父栏目
                    List<int> idArray = new List<int>();
                    //查找栏目id及父栏目路径
                    var categoryArray = categories.Where(c => c.Type == categoryType).Select(c => new { CategoryId = c.CategoryId, ParentPath = c.ParentPath });
                    if(categoryArray != null)
                    {
                        //添加栏目ID到
                        idArray.AddRange(categoryArray.Select(c => c.CategoryId));
                        foreach (var parentPath in categoryArray.Select(c=>c.ParentPath))
                        {
                            var parentIdArray = parentPath.Split(new char[] { ‘,‘ }, StringSplitOptions.RemoveEmptyEntries);
                            if (parentIdArray != null)
                            {
                                int parseId = 0;
                                foreach(var parentId in parentIdArray)
                                {
                                    if (int.TryParse(parentId, out parseId)) idArray.Add(parseId);
                                }
                            }
                        }
                    }
                    categories = categories.Where(c => idArray.Contains(c.CategoryId));
                    break;
            }
            return categories.OrderBy(c => c.ParentPath).ThenBy(C => C.Order).ToList();
        }
    }
}
View Code

三、实现dll动态加载和注入

要在web项目中对实现类进行解耦和注入,那么项目只能对接口进行依赖,解除对实现的依赖,然后在配置文件中配置实现的程序集和注入的服务,在Startup类中读取配置文件并加载程序集,然后实现接口的注入。

1、解除实现类依赖

在Web项目中添加对Ninesky.InterfaceBase项目的引用,解除对Ninesky.Base项目的引用。

2、实现注入的配置文件

首先在Models项目中实现注入服务类型配置项ServiceItem

技术分享技术分享
using Microsoft.Extensions.DependencyInjection;
using Newtonsoft.Json;
using Newtonsoft.Json.Converters;

namespace Ninesky.Models
{
    /// 
    /// 注入服务配置
    /// 
    public class ServiceItem
    {
        /// 
        /// 服务类型[含命名空间]
        /// 
        public string ServiceType { get; set; }

        /// 
        /// 实现类类型[含命名空间]
        /// 
        public string ImplementationType { get; set; }

        /// 
        /// 生命周期
        /// 
        [JsonConverter(typeof(StringEnumConverter))]
        public ServiceLifetime LifeTime { get; set; }
    }
}
View Code

然后在Models项目中实现注入需要加载的程序集配置项 AssemblyItem

技术分享技术分享
using System.Collections.Generic;

namespace Ninesky.Models
{
    /// 
    /// 程序集注入项目
    /// 
    public class AssemblyItem
    {
        /// 
        ///  服务的程序集名称[不含后缀]
        /// 
        public string ServiceAssembly { get; set; }
        /// 
        /// 实现程序集名称[含后缀.dll]
        /// 
        public string ImplementationAssembly { get; set; }

        /// 
        /// 注入服务集合
        /// 
        public List DICollections { get; set; }
    }
}
View Code

添加配置文件

在Web项目中添加配置文件service.json

技术分享技术分享
{
  "AssemblyCollections": [
    {
      "ServiceAssembly": "Ninesky.InterfaceBase",
      "ImplementationAssembly": "Ninesky.Base.dll",
      "DICollections": [
        {
          "ServiceType": "Ninesky.InterfaceBase.InterfaceCategoryService",
          "ImplementationType": "Ninesky.Base.CategoryService",
          "LifeTime": "Scoped"
        }
      ]
    }
  ]
}
View Code

可以看到配置文件的键值对于AssemblyItem类和ServiceItem类对应。集合的服务程序集为Ninesky.InterfaceBase,实现程序集为Ninesky.Base.dll,注入的服务为Ninesky.InterfaceBase.InterfaceCategoryService,实现类是Ninesky.Base.CategoryService。

读取配置文件并绑定到类型

在Startup只需要一行到即可绑定配置到类型。读取配置文件并绑定的详细操作见《Asp.Net Core自定义配置并绑定》

技术分享技术分享
var assemblyCollectiOns=  new ConfigurationBuilder().SetBasePath(Directory.GetCurrentDirectory()).AddJsonFile("service.json").Build().GetSection("AssemblyCollections").Get();
View Code

3、进行注入

在assemblyCollections变量加载了配置文件后使用如下代码即可实现注入

技术分享技术分享
            foreach(var assembly in assemblyCollections)
            {
                var serviceAssembly = Assembly.Load(new AssemblyName(assembly.ServiceAssembly));
                var implementatiOnAssembly= AssemblyLoadContext.Default.LoadFromAssemblyPath(AppContext.BaseDirectory + "//" + assembly.ImplementationAssembly);
                foreach(var service in assembly.DICollections)
                {
                    services.Add(new ServiceDescriptor(serviceAssembly.GetType(service.ServiceType), implementationAssembly.GetType(service.ImplementationType), service.LifeTime));
                }
            }
View Code

代码中可以看到加载接口程序集使用的方法是Assembly.Load(new AssemblyName(assembly.ServiceAssembly)),这是因为项目引用了接口程序集的项目,加载程序集的时候只需要提供程序集的名称就可以。

加载实现类所在程序集的时候使用的是AssemblyLoadContext.Default.LoadFromAssemblyPath(AppContext.BaseDirectory + "//" + assembly.ImplementationAssembly)。在.Net Core中Assembly没有了LoadFrom方法,仅有一个Load方法加载已引用的程序集。多方搜索资料才找到AssemblyLoadContext中有一个方法可以不需要引用项目可以动态加载Dll,但必须包含Dll的完整路径。

到这里就完整实现了解耦,现在项目结构看起来是这样子

技术分享

解耦后有些麻烦的是修改Base项目的代码后运行项目会出错,必须生成项目后将Base项目生成的Ninesky.Base.dll和Ninesky.Base.pdb复制到Web项目的bin\Debug\netcoreapp1.1目录下才能正常运行。

F5运行一下可以看到正常读出了数据。

技术分享

四、其他

 

代码托管地址:https://git.oschina.net/ninesky/Ninesky

文章发布地址:http://www.ninesky.cn

                 http://mzwhj.cnblogs.com/

代码包下载:Ninesky2.3、项目架构调整(续)-使用配置文件动态注入.rar

返回目录

.Net Core MVC 网站开发(Ninesky) 2.3、项目架构调整(续)-使用配置文件动态注入


推荐阅读
  • 基于layUI的图片上传前预览功能的2种实现方式
    本文介绍了基于layUI的图片上传前预览功能的两种实现方式:一种是使用blob+FileReader,另一种是使用layUI自带的参数。通过选择文件后点击文件名,在页面中间弹窗内预览图片。其中,layUI自带的参数实现了图片预览功能。该功能依赖于layUI的上传模块,并使用了blob和FileReader来读取本地文件并获取图像的base64编码。点击文件名时会执行See()函数。摘要长度为169字。 ... [详细]
  • Mac OS 升级到11.2.2 Eclipse打不开了,报错Failed to create the Java Virtual Machine
    本文介绍了在Mac OS升级到11.2.2版本后,使用Eclipse打开时出现报错Failed to create the Java Virtual Machine的问题,并提供了解决方法。 ... [详细]
  • 在说Hibernate映射前,我们先来了解下对象关系映射ORM。ORM的实现思想就是将关系数据库中表的数据映射成对象,以对象的形式展现。这样开发人员就可以把对数据库的操作转化为对 ... [详细]
  • 本文介绍了在SpringBoot中集成thymeleaf前端模版的配置步骤,包括在application.properties配置文件中添加thymeleaf的配置信息,引入thymeleaf的jar包,以及创建PageController并添加index方法。 ... [详细]
  • 知识图谱——机器大脑中的知识库
    本文介绍了知识图谱在机器大脑中的应用,以及搜索引擎在知识图谱方面的发展。以谷歌知识图谱为例,说明了知识图谱的智能化特点。通过搜索引擎用户可以获取更加智能化的答案,如搜索关键词"Marie Curie",会得到居里夫人的详细信息以及与之相关的历史人物。知识图谱的出现引起了搜索引擎行业的变革,不仅美国的微软必应,中国的百度、搜狗等搜索引擎公司也纷纷推出了自己的知识图谱。 ... [详细]
  • 本文详细介绍了Linux中进程控制块PCBtask_struct结构体的结构和作用,包括进程状态、进程号、待处理信号、进程地址空间、调度标志、锁深度、基本时间片、调度策略以及内存管理信息等方面的内容。阅读本文可以更加深入地了解Linux进程管理的原理和机制。 ... [详细]
  • 1,关于死锁的理解死锁,我们可以简单的理解为是两个线程同时使用同一资源,两个线程又得不到相应的资源而造成永无相互等待的情况。 2,模拟死锁背景介绍:我们创建一个朋友 ... [详细]
  • 后台获取视图对应的字符串
    1.帮助类后台获取视图对应的字符串publicclassViewHelper{将View输出为字符串(注:不会执行对应的ac ... [详细]
  • 《数据结构》学习笔记3——串匹配算法性能评估
    本文主要讨论串匹配算法的性能评估,包括模式匹配、字符种类数量、算法复杂度等内容。通过借助C++中的头文件和库,可以实现对串的匹配操作。其中蛮力算法的复杂度为O(m*n),通过随机取出长度为m的子串作为模式P,在文本T中进行匹配,统计平均复杂度。对于成功和失败的匹配分别进行测试,分析其平均复杂度。详情请参考相关学习资源。 ... [详细]
  • 本文介绍了通过ABAP开发往外网发邮件的需求,并提供了配置和代码整理的资料。其中包括了配置SAP邮件服务器的步骤和ABAP写发送邮件代码的过程。通过RZ10配置参数和icm/server_port_1的设定,可以实现向Sap User和外部邮件发送邮件的功能。希望对需要的开发人员有帮助。摘要长度:184字。 ... [详细]
  • Java验证码——kaptcha的使用配置及样式
    本文介绍了如何使用kaptcha库来实现Java验证码的配置和样式设置,包括pom.xml的依赖配置和web.xml中servlet的配置。 ... [详细]
  • 本文介绍了使用Java实现大数乘法的分治算法,包括输入数据的处理、普通大数乘法的结果和Karatsuba大数乘法的结果。通过改变long类型可以适应不同范围的大数乘法计算。 ... [详细]
  • HDU 2372 El Dorado(DP)的最长上升子序列长度求解方法
    本文介绍了解决HDU 2372 El Dorado问题的一种动态规划方法,通过循环k的方式求解最长上升子序列的长度。具体实现过程包括初始化dp数组、读取数列、计算最长上升子序列长度等步骤。 ... [详细]
  • 本文讨论了Alink回归预测的不完善问题,指出目前主要针对Python做案例,对其他语言支持不足。同时介绍了pom.xml文件的基本结构和使用方法,以及Maven的相关知识。最后,对Alink回归预测的未来发展提出了期待。 ... [详细]
  • 本文讨论了如何优化解决hdu 1003 java题目的动态规划方法,通过分析加法规则和最大和的性质,提出了一种优化的思路。具体方法是,当从1加到n为负时,即sum(1,n)sum(n,s),可以继续加法计算。同时,还考虑了两种特殊情况:都是负数的情况和有0的情况。最后,通过使用Scanner类来获取输入数据。 ... [详细]
author-avatar
手机用户2602879975
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有