欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

记录:如何使用ASP.NET Core和EnityFramework Core实现 数据库操作 和 数据库实体 的项目分离

程序员文章站 2022-06-10 13:27:59
前情提要: 现有一个网站框架,包括主体项目WebApp一个,包含 IIdentityUser 接口的基架项目 A。用于处理用户身份验证的服务 AuthenticationService 位于命名空间B。用于保存数据的实体 User : IIdentityUser 位置项目C。项目之间的关系是B和C依 ......

前情提要:

  现有一个网站框架,包括主体项目webapp一个,包含 iidentityuser 接口的基架项目 a。用于处理用户身份验证的服务 authenticationservice 位于命名空间b。用于保存数据的实体 user : iidentityuser 位置项目c。项目之间的关系是b和c依赖项目a。

 

需求:

  现在有一个新项目d,在这个项目里有一个duser : iidentityuser 。如何处理才能最优雅的在不添加引用和修改项目b的前提下,将用户保存至duser。

 

实际例子:

  在asp.net core中,有一个东西叫identityserver。里面就有这个东西,他写的是类似identityserverbuilder.addservice<tuser, trole>()这种代码,如何实现?

 

解决方案:

  1、新建一个泛类(这个类可以标记为internal,外部不需要了解,也不需要访问):

public class usercontext<tuser>
        where tuser : class, iidentityuser, new ()
    {
        public yourcontext dbcontext;
        public usercontext(yourcontext ctx) => dbcontext = ctx;

        public dbset<tuser> users
        {
            get
            {
                return dbcontext.set<tuser>();
            }
        }

        public void savechanges()
        {
            dbcontext.savechanges();
        }
    }

  2、新建一个用以操作的服务(注意,所有需要的操作都往这个里面写,未来暴露的也是这个接口)

public class userservice<tuser> : iuserservice
        where tuser: class, iidentityuser, new()
    {
        private usercontext<tuser> dbcontext;
        public userservice(yourcontext ctx, iserviceprovider provider)
        {
            dbcontext = new permissioncontext<tuser>(ctx.dbcontext);
        }
     
   public tuser getuserbyid(guid id)
   {
      return dbcontext.users.firstordefault(e => e.id == id);
   }
    }

  

  3、添加一个注射器

    public static class authenticationinject
    {
        public static iservicecollection addauthenticationcontext<tuser>(this iservicecollection services)
            where tuser: iidentityuser
        {
            var servicetype = typeof(userservice<>).makegenerictype(typeof(tuser));
            services.addsingleton(typeof(iuserservice), servicetype );

            return services;
        }
    }

  技术点:使用makegenerictype方法可以动态加载泛类实例。如果类型是 userservice<tuser, trole>,则写成 typeof(userservice<,>).makegenerictype(typeof(t1), typeof(t2))

  至此,我们就已经将泛类的类型名拆到了变量里面。然后就可以玩出一万种花样了。

  4、在webapp里,注入相关变量

        // this method gets called by the runtime. use this method to add services to the container.
        // for more information on how to configure your application, visit https://go.microsoft.com/fwlink/?linkid=398940
        public void configureservices(iservicecollection services)
        {
            services.addauthenticationcontext<duser>();
        }

  

  分析依赖关系:

  执行项目webapp依赖a,b,d,b和d项目只依赖a。甚至于,这里还能再解耦。把函数addauthenticationcontext从泛型函数改成 addauthenticationcontext(type usertype),还可以再进一步,改成addauthenticationcontext(string type),通过反射和配置来取类型,做到a项目和d项目解耦。

  扩展性:

  在未来,有新项目e,euser。只需要将d和a解除分离,再将e和a进行关联。只需要修改 addauthenticationcontext 函数,即可满足要求。当然,如果要有心情,你甚至可以搞一个自动发现的代码(比如我项目里就是这么搞的,自动分析iidentityuser的对象,然后附加给context,为了保证有多个实现时能正确附加,我做了一个attribute用来标记这个项目要用哪个user)。再有心情还可以做成配置式的,反正你可以把ef core摆出一万种姿势。