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


    前情提要:

      现有一个网站框架,包括主体项目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摆出一万种姿势。

  • 相关阅读:
    74.Android之四种启动模式
    Android面试总结 (转)
    1.Android常见异常:android.view.WindowLeaked 分析以及解决办法
    73.Android之SparseArray替代HashMap
    C#(类)
    C#(一维数组)
    C#(二维数组/集合)
    c#(特殊集合)
    C#字符串去除html格式
    获取文件的后缀名 和 Process进程
  • 原文地址:https://www.cnblogs.com/Pray4U/p/12732296.html
Copyright © 2020-2023  润新知