使用 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 檢視。執行後你就可以在資料庫看到結果了!