GraphQl+ef+mysql打造屬於本身的數據請求網關

   用EF已經有段時間了,一般都是和sql server搭配使用,開發效率確實不錯,閒來無事,就整了套ef+mysql玩玩,ef+mysql與ef+sql server差異仍是挺大的,下面就說說我遇到的坑,node

   我在模型層沒有引入EF,故沒辦法使用EF的特性爲個人字段作約束,這也是我指望的結果,我但願模型層應該和數據庫無關。模型定義完執行mysql

1,啓用數據遷移:Enable-Migrations,會自動生成一個Migrations/Configuration.cs文件,自動遷移默認爲false
2,增長數據遷移項: Add-Migration record,會自動生成數據庫建表腳本,
3,執行數據庫更新: update-database,自動生成數據庫表結構git

外鍵約束
  EF添加外鍵約束比較特殊,在domain層實體結構中,咱們定義一個用戶角色實體,github

        public Guid? user_id { get; set; }web

        public Guid? role_id { get; set; }sql

        public User user { get; set; }數據庫

        public Role role { get; set; }編程

注意事項c#

   user_id 和role_id必須爲可空字段,若是不是可空字段,數據庫會生成               user_id,user_id_id,role_id,role_id_id。實體關係映射api

   public class UserRoleMapper: EntityTypeConfiguration<UserRole>

    {

        public UserRoleMapper()

        {

            HasKey(d => d.id);

            HasOptional(p => p.user).WithMany(p => p.User_role).HasForeignKey(p => p.user_id).WillCascadeOnDelete(false);

            HasOptional(p => p.role).WithMany(p => p.Role_user).HasForeignKey(p => p.role_id).WillCascadeOnDelete(false);

        }

    }

注意事項

    一般咱們開發的會定義一個基類BaseDbContext,實現表的增刪查改,在定義一個MainBCUnitOfWork類去集成BaseDbContext,這樣 Enable-Migrations命令在多個Dbcontext時候會報錯,須要指定爲哪個Dbcontext執行數據遷移,具體解決方法是:Enable-Migrations -ContextTypeName MainBCUnitOfWork

 這裏簡單介紹了ef+mysql遇到的坑,若是您的程序可以正常運行,咱們接着往下說GraphQL,

 關於什麼是GraphQL,小白能夠去百度一下,這裏假設讀者對此有必定的認識,咱們須要引用GraphQL.Net,源碼地址是https://github.com/ckimes89/graphql-net,nuget直接搜索GraphQL.Net,安裝。在Repository層建立用於查詢的   首先定義IGraphQlRepository接口,

 public interface IGraphQlRepository: IRepository<Entity>

    {

        IDictionary<string, object> List(string query);

    }

再建立GraphQlRepository類實現IGraphQlRepository接口

public class GraphQlRepository: Repository<Entity>, IGraphQlRepository

    {

        private  GraphQL<MainBCUnitOfWork> gql ;

        public GraphQlRepository(MainBCUnitOfWork unitOfWork) : base(unitOfWork)

        {

            InitGraphQL();

        }

        private void InitGraphQL()

        {

            LoggerFactory.GetInstance().Info("create graphql start");

            var schema = GraphQL<MainBCUnitOfWork>.CreateDefaultSchema(() => UnitOfWork as MainBCUnitOfWork);

            schema.AddScalar(new { year = 0, month = 0, day = 0 }, ymd => new DateTime(ymd.year, ymd.month, ymd.day));

            var user = schema.AddType<User>();

            user.AddField(u => u.id);

            user.AddField(u => u.name);

            user.AddField(u => u.nick);

            user.AddField(u => u.phone);

            user.AddField(u => u.email);

            user.AddField(u => u.User_role);

            user.AddField("total", (db, u) => db.User.Count());

            user.AddField("user_role_id", (db, u) => u.User_role.FirstOrDefault().role_id);

            var user_role = schema.AddType<UserRole>();

            user_role.AddField(u => u.id);

            user_role.AddField(u => u.role);

            user_role.AddField(u => u.role_id);

            user_role.AddField(u => u.role);

            var role = schema.AddType<Role>();

            role.AddField(u => u.id);

            role.AddField(u => u.name);

            role.AddField(u => u.Role_action);

            var roleAction = schema.AddType<RoleAction>();

            roleAction.AddField(u => u.id);

            roleAction.AddField(u => u.action_type);

            roleAction.AddField(u => u.navigation);

            var navigation = schema.AddType<Navigation>();

            navigation.AddField(u => u.id);

            navigation.AddField(u => u.action_type);

            navigation.AddField(u => u.is_sys);

            navigation.AddField(u => u.name);

            navigation.AddField(u => u.title);

            schema.AddListField("user_roles", db => db.User_role);

            //schema.AddListField("users", db => db.User);

            schema.AddField("user", new { id = Guid.Empty}, (db, args) => db.User.FirstOrDefault(u => u.id == args.id));

            schema.AddListField("users", new { id = Guid.Empty, name = string.Empty }, (db, args) => db.User.AsQueryable().Where(u => u.id == args.id||u.name== args.name));

            schema.AddListField("usersByGuid", new { guid = Guid.Empty },

                  (db, args) => db.User.AsQueryable().Where(a => a.id == args.guid));

            schema.Complete();

            gql = new GraphQL<MainBCUnitOfWork>(schema);

            LoggerFactory.GetInstance().Info("create graphql end "+ gql.GetHashCode());

        }

        public IDictionary<string, object> List(string query)

        {

            // var results = gql.ExecuteQuery("{ user(id:\"3c263730-34aa-4927-b78c-1f9da348980f\") { id, name,nick,user_role {id,role_id} } }");

            var results = gql.ExecuteQuery(query);

            return results;

        }

    }

在構造器中初始化schema,每次查詢都須要經過schema定義的屬性組合sql語句,此段初始化代碼本想放到靜態變量中,這樣不用每次查詢都要初始化schema,可是GraphQL在每次完成查詢以後DbContext會被銷燬,此時執行gql.ExecuteQuery會拋出異常,構造器中初始化schema總給人一種不舒服的感受,等找到更好的辦法再說吧。

定義IGraphQlAppService接口

  public interface IGraphQlAppService

    {

        IDictionary<string, object> List(string query);

    }

定義GraphQlAppService實現該接口

public class GraphQlAppService : IGraphQlAppService

    {

        private readonly IGraphQlRepository graphQlRepository;

        public GraphQlAppService(IGraphQlRepository _graphQlRepository)

        {

            graphQlRepository = _graphQlRepository;

        }

        public IDictionary<string, object> List(string query)

        {

            return graphQlRepository.List(query);

        }

    }

我用的是微軟的Unity容器,

            container.RegisterType<IGraphQlAppService, GraphQlAppService>();

            container.RegisterType<IGraphQlRepository, GraphQlRepository>();

OK,完成註冊。

我用的是webapi實現先後臺數據的交互,webapi來作GraphQL!對,沒看錯,好彆扭的感受,好吧,誰讓這是個人第一個GraphQL呢,

新建一個webapi控制器,

        [Route("Api/V3/List")]

        [HttpGet]

        public JObject List()

        {

            var graphQlAppService = IoCFactory.Instance.CurrentContainer.Resolve<IGraphQlAppService>();

            var query = HttpContext.Current.Request["query"].ToString();

            var results = graphQlAppService.List(query);

            var actual = JObject.FromObject(results, Serializer);

            return actual;

        }

此時一個簡單的GraphQL寫完,下面看怎麼調用

url=http://192.168.5.153:9001/Api/V3/List?query={ user(id:"3c263730-34aa-4927-b78c-1f9da348980f") { id, name,nick,phone,user_role_id,} }

查詢id="3c263730-34aa-4927-b78c-1f9da348980f" 的用戶id, name,nick,phone,user_role_id信息。

此時去掉user_role_id

url=http://192.168.5.153:9001/Api/V3/List?query={user(id:"3c263730-34aa-4927-b78c-1f9da348980f") { id, name,nick,phone} }

這樣就很神奇了,經過統一的查詢入口,獲得不一樣的結果值,是否是有點顛覆rest風格api。接着看

這個查詢結果的url=http://192.168.5.153:9001/Api/V3/List?query={ user(id:"3c263730-34aa-4927-b78c-1f9da348980f"){id,name,nick,phone,user_role_id,user_role {id,role_id,role{id,name,role_action{action_type,navigation{action_type,name,title}}}} } }

      經過自定義query參數實現了按需查找結果,至此一個完整的GraphQL的demo已經成型,這種獲取數據的方式至關前衛,但卻不必定是最好的方式,好比如何管理權限,與EF的緊密耦合會致使領域模型暴露出去。配合node實現輕量級的數據訪問網關確實是一個很不錯的選擇,但用到c#實現起來未免有些怪異。GraphQL爲咱們提供了一種很友好,靈活的數據交互方式,感受這是將來編程的一個趨勢。

總結:

     我的比較欣賞用nodejs去作數據網關(單純的存取數據),GraphQL看上去是一個不錯的選擇,可是沒有作過性能測試,不太清楚高併發會對數據庫有和影響,下一步準備用c#簡單作個性能測試。本篇文章僅僅是對GraohQL的一種探索,不敢斷言其應用場景。文章有不對的地方,歡迎指正。

相關文章
相關標籤/搜索