記錄:如何使用ASP.NET Core和EnityFramework Core實現 資料庫操作 和 資料庫實體 的專案分離

bluesky234發表於2020-04-19

前情提要:

  現有一個網站框架,包括主體專案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擺出一萬種姿勢。

相關文章