1. 程式人生 > >使用 EF Core CodeFirst 操作 Mysql 資料庫

使用 EF Core CodeFirst 操作 Mysql 資料庫

EntityFramework(以下簡稱EF) 作為 .NET 廣受歡迎的資料庫操作中介軟體,支援了幾乎所有你用過的關係型資料庫,本文將非常基礎的介紹其在 Mysql 中的使用。EF 常見的使用模式有三種:CodeFirst, ModelFirst, DBFirst;三種方式各有所特點,一般要根據實際的業務情況做選擇。

在完全沒有歷史負擔的情況下,選擇 CodeFirst 更為普遍,在最新的 EntityFramework Core 中 CodeFirst 幾乎是唯一的選擇。本文將介紹如何使用 CodeFirst 建立和操作 Mysql 資料庫,文中將以微信企業號的使用者同步作為案例。IDE 使用 Visual Studio for Mac。

首先,使用 Mac 版 Visual Studio 建立一個 Console Application(.NET Core) 專案:EFCoreSample。使用 Nuget 增加 EFCore 以及 Mysql 所需要的包:

  • Microsoft.EntityFrameworkCore
  • MySql.Data.EntityFrameworkCore

    然後,由於本次例子中會使用到微信企業號的 API,我們直接引用第三方包來實現:

  • Senparc.Weixin

  • Senparc.Weixin.QY

    第三步,在解決方案中新建一個 Models 資料夾,增加部門(Party),使用者(User),標籤(Tag)類,以及它們之間的相互關係部門標籤(PartyTag),使用者部門(UserParty),使用者標籤(UserTag)類,均為多對多關係。類的詳細定義程式碼如下:

    * 部門(Party) *

{% codeblock %} public class Party { public Party() { UserPartys = new List(); PartyTags = new List(); }

/// <summary>
/// 部門Id
/// </summary>
[Key]
public int PartyId { get; set; }
/// <summary>
/// 部門名稱
/// </summary>
[Required]
[StringLength(32)]
public string Name { get; set; }
/// <summary>
/// 在父部門中的次序值。order值小的排序靠前。
/// </summary>
public int Order { get; set; }
/// <summary>
/// 上級部門Id
/// </summary>
public int ParentPartyId { get; set; }
public List<UserParty> UserPartys { set; get; }
public List<PartyTag> PartyTags { set; get; }

} {% endcodeblock %}

* 使用者(User) *

{% codeblock %} public class User { public User() { UserPartys = new List(); UserTags = new List(); }

/// <summary>
/// 員工UserID
/// </summary>
[Key]
[StringLength(32)]
public string UserId { get; set; }
/// <summary>
/// 頭像url。注:小圖將url最後的"/0"改成"/64"
/// </summary>
[StringLength(256)]
public string Avatar { get; set; }
/// <summary>
/// 郵箱
/// </summary>
[StringLength(256)]
public string Email { get; set; }
/// <summary>
/// 性別。gender=0表示男,=1表示女
/// </summary>
public int Gender { get; set; }
/// <summary>
/// 手機號碼
/// </summary>
[StringLength(32)]
public string Mobile { get; set; }
/// <summary>
/// 成員名稱
/// </summary>
[StringLength(128)]
public string Name { get; set; }
/// <summary>
/// 職位資訊
/// </summary>
[StringLength(64)]
public string Position { get; set; }
/// <summary>
/// 關注狀態: 1=已關注,2=已凍結,4=未關注
/// </summary>
public int Status { get; set; }
/// <summary>
/// 微訊號
/// </summary>
[StringLength(64)]
public string Weixinid { get; set; }
/// <summary>
/// 成員所屬部門列表
/// </summary>
public List<UserParty> UserPartys { get; set; }
public List<UserTag> UserTags { set; get; }

} {% endcodeblock %}

* 標籤(Tag) *

{% codeblock %} public class Tag { public Tag() { PartyTags = new List(); UserTags = new List(); }

[Key]
public int TagId { set; get; }
[Required]
[StringLength(64)]
public string TagName { set; get; }
public List<PartyTag> PartyTags { get; set; }
public List<UserTag> UserTags { get; set; }

} {% endcodeblock %}

* 部門標籤(PartyTag) *

{% codeblock %} public class PartyTag { public int PartyId { set; get; } public Party Party { set; get; }

public int TagId { set; get; }
public Tag Tag { set; get; }

} {% endcodeblock %}

* 使用者部門(UserParty) *

{% codeblock %} public class UserParty { [StringLength(32)] public string UserId { set; get; } public User User { set; get; }

public int PartyId { set; get; }
public Party Party { set; get; }

} {% endcodeblock %}

* 使用者標籤(UserTag) *

{% codeblock %} public class UserTag { [StringLength(32)] public string UserId { set; get; } public User User { set; get; }

public int TagId { set; get; }
public Tag Tag { set; get; }

} {% endcodeblock %}

第四步,增加 WechatContext 類來操作資料庫:

{% codeblock %} public class WechatContext : DbContext { public DbSet Users { set; get; } public DbSet Partys { set; get; } public DbSet Tags { set; get; } public DbSet PartyTags { set; get; } public DbSet UserPartys { set; get; } public DbSet UserTags { set; get; }

protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
    => optionsBuilder.UseMySQL(@"server=127.0.0.1;user id=root;password=root;persistsecurityinfo=True;database=wechatdb;Character Set=utf8");

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
    modelBuilder.Entity<PartyTag>()
                .HasKey(c => new { c.PartyId, c.TagId });
    modelBuilder.Entity<UserParty>()
                .HasKey(c => new { c.PartyId, c.UserId });
    modelBuilder.Entity<UserTag>()
                .HasKey(c => new { c.UserId, c.TagId });

}

} {% endcodeblock %}

需要注意以下幾點:1,修改伺服器的IP與使用者名稱和密碼;2,不要忘記連線字串中的“Character Set=utf8”,否則會出現中文亂碼;3,OnModelCreating 中定義了幾個關係表的聯合主鍵,不可缺少。

第五步,建立 UserHelper 類,放置到 Core 資料夾中,程式碼如下:

{% codeblock %} /// /// 使用者幫助類,用於將使用者組,Tag等資訊轉換到具體的使用者 /// 該類可能在通知中用到 /// public class UserHelper { private static string corpID = “”; private static string corpSecret = “”;

/// <summary>
/// Get the wechat users and save to database.
/// </summary>
/// <returns></returns>
public static bool GetWechatUserToDB()
{
    string token = AccessTokenContainer.TryGetToken(corpID, corpSecret);
    WechatContext context = new WechatContext();

    //todo: 清空現有使用者資料

    //獲取微信企業號內的使用者架構資訊
    Dictionary<string, List<Tag>> _userTags = new Dictionary<string, List<Tag>>();
    Dictionary<int, List<Tag>> _partyTags = new Dictionary<int, List<Tag>>();

    Dictionary<string, User> Users = new Dictionary<string, User>();

    //查詢所有Tag並插入資料庫
    GetTagListResult tagList = MailListApi.GetTagList(token);

    if (tagList != null && tagList.taglist != null && tagList.taglist.Count > 0)
    {
        foreach (var tag in tagList.taglist)
        {
            int tagId = -1;
            if (Int32.TryParse(tag.tagid, out tagId))
            {
                Tag tempTag = new Tag() { TagId = tagId, TagName = tag.tagname };

                GetTagMemberResult tagMemberResult = MailListApi.GetTagMember(token, tagId);
                if (tagMemberResult != null && tagMemberResult.partylist != null && tagMemberResult.partylist.Length > 0)
                {
                    foreach (int party in tagMemberResult.partylist)
                    {
                        if (!_partyTags.ContainsKey(party))
                            _partyTags[party] = new List<Tag>();

                        _partyTags[party].Add(tempTag);
                    }
                }

                if (tagMemberResult != null && tagMemberResult.userlist != null && tagMemberResult.userlist.Count > 0)
                {
                    foreach (var tagUser in tagMemberResult.userlist)
                    {
                        if (!_userTags.ContainsKey(tagUser.userid))
                            _userTags[tagUser.userid] = new List<Tag>();

                        _userTags[tagUser.userid].Add(tempTag);
                    }
                }

                context.Tags.Add(tempTag);
            }
        }
        context.SaveChanges();
    }

    //查詢所有部門並插入資料庫
    GetDepartmentListResult departmentList = MailListApi.GetDepartmentList(token);

    if (departmentList != null && departmentList.department != null)
    {
        foreach (var party in departmentList.department)
        {
            var tempParty = new Party() { PartyId = party.id, Name = party.name, Order = party.order, ParentPartyId = party.parentid };

            //此處需要查詢所有的Tag儲存到庫中
            if (_partyTags.ContainsKey(party.id))
            {
                tempParty.PartyTags = _partyTags[party.id].Select(f => new PartyTag() { PartyId = tempParty.PartyId, TagId = f.TagId }).ToList();
            }

            //根據部門查詢所有使用者並存入快取
            GetDepartmentMemberInfoResult memberInfos = MailListApi.GetDepartmentMemberInfo(token, party.id, 1, 0);
            if (memberInfos != null && memberInfos.userlist != null && memberInfos.userlist.Count > 0)
            {
                foreach (var member in memberInfos.userlist)
                {
                    if (!Users.ContainsKey(member.userid))
                    {
                        Users[member.userid] = new User()
                        {
                            Avatar = member.avatar,
                            Email = member.email,
                            Gender = member.gender,
                            Mobile = member.mobile,
                            Name = member.name,
                            Position = member.position,
                            Status = member.status,
                            UserId = member.userid,
                            Weixinid = member.weixinid,
                            UserTags = (_userTags.ContainsKey(member.userid)&& _userTags[member.userid].Count > 0) ? _userTags[member.userid].Select(f => new UserTag() { UserId = member.userid, TagId = f.TagId }).ToList() : null                                   
                        };
                    }
                    Users[member.userid].UserPartys.Add(new UserParty() { PartyId = tempParty.PartyId, UserId = member.userid });
                }
            }

            context.Partys.Add(tempParty);
        }
        context.SaveChanges();
    }

    if (Users != null && Users.Count > 0)
    {
        foreach(var user in  Users.Values)
            context.Users.Add(user);

        context.SaveChanges();
    }

    return true;
}

} {% endcodeblock %}

程式碼中的 corpID 和 corpSecret 需要在微信企業號中獲得,如果你沒有企業號,那麼將僅能建立一個空的資料庫,而無法同步到任何資料。

最後,在 Program 中呼叫資料庫建立以及使用者同步方法:

{% codeblock %} class Program { static void Main(string[] args) { //判斷資料庫是否存在,如果不存在則建立表 WechatContext context = new WechatContext();

    if (!context.Database.EnsureCreated())
    {
        Console.WriteLine("Error: Unable to create the mysql database!");
    }
    else
    {
        Console.WriteLine("Create and Connect to MySQL success!");

        //初始化微信使用者關係資料
        if (UserHelper.GetWechatUserToDB())
        {
            Console.WriteLine("Init wechat users to MySQL db success!");
        }
        else
        {
            Console.WriteLine("Error: Unable to Init wechat users to MySQL db!");
        }
    }
}

} {% endcodeblock %}

至此,一個簡單的 Demo 就完成了,由於篇幅有限,許多地方都還有需要優化的地方,此處就不一一介紹了,完整程式碼可以在 EFCoreSample 檢視。執行後你就可以在資料庫看到結果了!