接着上一篇使用 EF Core 操做 Azure CosmosDB 生成種子數據,今天咱們完成經過 EF Core 實現CRUD一系列功能。EF Core 3.0 提供了CosmosDB 數據庫提供程序的第一個可用的版本,今天咱們使用 EF Core 3.1在嘗試使用Cosmos DB 來存儲和構建 Asp.NET Core 應用程序時,可能還有一些差別。html
1,CosmosDB 不會生成惟一的主鍵,Cosmos DB不會像SQL數據庫那樣建立主鍵。若是須要添加到 Cosmos DB中,則可能會使用相似GUID 字符串的名稱。git
2,Cosmos DB 不支持EF Core 遷移github
--------------------我是分割線--------------------數據庫
將以前建立好的UserContext 注入基類倉儲中緩存
1 /// <summary> 2 /// 基類倉儲 3 /// </summary> 4 public abstract class Repository<TEntity> : IRepository<TEntity> where TEntity : class,new() 5 { 6 protected DbContext Db; 7 8 public async virtual Task<TEntity> GetById(string partitionKey) 9 { 10 return await Db.Set<TEntity>().FindAsync( partitionKey); 11 } 12 13 public async virtual Task<TEntity> Add(TEntity entity) 14 { 15 await Db.AddAsync<TEntity>(entity); 16 return entity; 17 } 18 19 public virtual bool Update(TEntity entity) 20 { 21 Db.Add(entity); 22 Db.Entry(entity).State = EntityState.Modified; 23 return true; 24 } 25 26 public virtual bool Remove(TEntity entity) 27 { 28 Db.Set<TEntity>().Remove(entity); 29 return true; 30 } 31 32 public virtual IEnumerable<TEntity> GetAll() 33 { 34 return Db.Set<TEntity>(); 35 } 36 37 public virtual IEnumerable<TEntity> Find(Expression<Func<TEntity, bool>> conditions) 38 { 39 return Db.Set<TEntity>().Where(conditions); 40 } 41 42 public async Task<int> SaveChangesAsync() 43 { 44 return await Db.SaveChangesAsync(); 45 } 46 47 public int SaveChanges() 48 { 49 return Db.SaveChanges(); 50 } 51 52 public void Dispose() 53 { 54 Db.Dispose(); 55 GC.SuppressFinalize(this); 56 } 57 }
1 public interface IRepository<TEntity> : IDisposable where TEntity : class 2 { 3 /// <summary> 4 /// 根據Id獲取對象 5 /// </summary> 6 /// <param name="partitionKey"></param> 7 /// <returns></returns> 8 Task<TEntity> GetById(string partitionKey); 9 10 /// <summary> 11 /// 添加 12 /// </summary> 13 /// <param name="entity"></param> 14 Task<TEntity> Add(TEntity entity); 15 16 /// <summary> 17 /// 更新 18 /// </summary> 19 /// <param name="entity"></param> 20 bool Update(TEntity entity); 21 22 /// <summary> 23 /// 刪除 24 /// </summary> 25 /// <param name="entity"></param> 26 bool Remove(TEntity entity); 27 28 /// <summary> 29 /// 查詢所有 30 /// </summary> 31 /// <returns></returns> 32 IEnumerable<TEntity> GetAll(); 33 34 /// <summary> 35 /// 查詢根據條件 36 /// </summary> 37 /// <param name="conditions"></param> 38 /// <returns></returns> 39 IEnumerable<TEntity> Find(Expression<Func<TEntity, bool>> conditions); 40 41 42 /// <summary> 43 /// 保存 44 /// </summary> 45 /// <returns></returns> 46 Task<int> SaveChangesAsync(); 47 48 49 /// <summary> 50 /// 保存 51 /// </summary> 52 /// <returns></returns> 53 int SaveChanges(); 54 }
1 /// <summary> 2 /// IUserRepository接口 3 /// </summary> 4 public interface IUserRepository : IRepository<UserModel> 5 { 6 //一些UserModel獨有的接口 7 Task<UserModel> GetByName(string name); 8 }
1 public class UserRepository : Repository<UserModel>, IUserRepository 2 { 3 public UserRepository(UserContext context) 4 { 5 Db = context; 6 } 7 8 #region 01,獲取用戶根據姓名+async Task<UserModel> GetByName(string name) 9 /// <summary> 10 /// 獲取用戶根據姓名 11 /// </summary> 12 /// <param name="name">姓名</param> 13 /// <returns></returns> 14 public async Task<UserModel> GetByName(string name) 15 { 16 //返回一個新查詢,其中返回的實體將不會在 System.Data.Entity.DbContext 中進行緩存 17 return await Db.Set<UserModel>().AsNoTracking().FirstOrDefaultAsync(c => c.Name == name); 18 } 19 #endregion 20 }
注意,在更新操做中,有個小坑app
每一個項目都必須具備 id 給定分區鍵惟一的值。默認狀況下,EF Core經過使用「 |」將區分符和主鍵值鏈接起來來生成該值。做爲分隔符。僅當實體進入 Added 狀態時才生成鍵值。若是實體 id
在.NET類型上沒有屬性來存儲值,則在附加實體時可能會出現問題。async
將實體標記爲首先添加,而後將其更改成所需狀態,我這裏將狀態改成 「Modified」ide
public virtual bool Update(TEntity entity) { Db.Add(entity); Db.Entry(entity).State = EntityState.Modified; return true; }
如下是整個倉儲層的結構函數
2.1,這裏咱們的業務層的模型視圖爲 「UserViewModel」,可是咱們的倉儲使用的是實體數據模型 "UserModel",這裏我引用 Automapper,進行對象轉化 。性能
使用程序包管理控制檯進行安裝
Install-Package AutoMapper -Version 10.0.0
Install-Package AutoMapper.Extensions.Microsoft.DependencyInjection -Version 8.0.1
2.2,配置構建函數,用來建立關係映射
public DomainToViewModelMappingProfile() { CreateMap<UserModel, UserViewModel>(); }
public ViewModelToDomainMappingProfile() { //手動進行配置 CreateMap<UserViewModel, UserModel>(); }
全局 AutoMapper 配置文件
/// <summary> /// 靜態全局 AutoMapper 配置文件 /// </summary> public class AutoMapperConfig { public static MapperConfiguration RegisterMappings() { //建立AutoMapperConfiguration, 提供靜態方法Configure,一次加載全部層中Profile定義 //MapperConfiguration實例能夠靜態存儲在一個靜態字段中,也能夠存儲在一個依賴注入容器中。 一旦建立,不能更改/修改。 return new MapperConfiguration(cfg => { //這個是領域模型 -> 視圖模型的映射,是 讀命令 cfg.AddProfile(new DomainToViewModelMappingProfile()); //這裏是視圖模型 -> 領域模式的映射,是 寫 命令 cfg.AddProfile(new ViewModelToDomainMappingProfile()); }); } }
2.3,建立UserService 實現類,IApplicationService、IUserService 接口
1 /// <summary> 2 /// UserService 服務接口實現類,繼承 服務接口 3 /// </summary> 4 public class UserService : IUserService 5 { 6 private readonly IUserRepository _UserRepository; 7 // 用來進行DTO 8 private readonly IMapper _mapper; 9 10 public void Dispose() 11 { 12 GC.SuppressFinalize(this); 13 } 14 15 public UserService( 16 IUserRepository userRepository, 17 IMapper mapper) 18 { 19 _UserRepository = userRepository; 20 _mapper = mapper; 21 } 22 23 public IEnumerable<UserViewModel> GetAll() 24 { 25 //第一種寫法 Map 26 return _mapper.Map<IEnumerable<UserViewModel>>(_UserRepository.GetAll()); 27 } 28 29 public UserViewModel GetById(string partitionKey) 30 { 31 return _mapper.Map<UserViewModel>(_UserRepository.GetById(partitionKey).Result); 32 } 33 34 public async Task<int> Register(UserViewModel userViewModel) 35 { 36 var partitionKey = _UserRepository.GetAll().Max(x => int.Parse(x.PartitionKey)); 37 userViewModel.PartitionKey = (++partitionKey).ToString(); 38 await _UserRepository.Add(_mapper.Map<UserModel>(userViewModel)); 39 return await _UserRepository.SaveChangesAsync(); 40 } 41 42 public void Remove(string partitionKey) 43 { 44 45 _UserRepository.Remove(_mapper.Map<UserModel>(_UserRepository.GetById(partitionKey).Result)); 46 _UserRepository.SaveChangesAsync(); 47 } 48 49 public int Update(UserViewModel userViewModel) 50 { 51 _UserRepository.Update(_mapper.Map<UserModel>(userViewModel)); 52 return _UserRepository.SaveChanges(); 53 } 54 }
1 public interface IApplicationService<T> where T : class, new() 2 { 3 /// <summary> 4 /// 獲取所有數據 5 /// </summary> 6 /// <returns></returns> 7 IEnumerable<T> GetAll(); 8 9 /// <summary> 10 /// 獲取單個數據 11 /// </summary> 12 /// <param name="partitionKey"></param> 13 /// <returns></returns> 14 T GetById(string partitionKey); 15 16 /// <summary> 17 /// 更新數據 18 /// </summary> 19 /// <param name="viewmodel"></param> 20 int Update(T viewmodel); 21 22 /// <summary> 23 /// 刪除數據 24 /// </summary> 25 /// <param name="partitionKey"></param> 26 void Remove(string partitionKey); 27 }
1 public interface IUserService:IApplicationService<UserViewModel> 2 { 3 /// <summary> 4 /// 註冊 5 /// </summary> 6 /// <param name="userViewModel"></param> 7 Task<int> Register(UserViewModel userViewModel); 8 }
3.1,用戶列表,用戶詳情控制器方法
// GET: User public ActionResult Index() { return View(_userService.GetAll()); } // GET: User/Details/5 public ActionResult Details(string partitionKey) { try { // TODO: Add insert logic here // 執行查詢方法 var userViewModel= _userService.GetById(partitionKey); return View(userViewModel); } catch { return View(); } }
3.2 用戶列表,用戶信息詳細頁面
Index.cshtml(用戶列表)
@model IEnumerable<Azure.CosmosDB.Models.UserViewModel> @{ ViewData["Title"] = "Index"; } <h1>Index</h1> <p> <a asp-action="Create">Create New</a> </p> <table class="table"> <thead> <tr> <th> @Html.DisplayNameFor(model => model.Id) </th> <th> @Html.DisplayNameFor(model => model.Name) </th> <th> @Html.DisplayNameFor(model => model.Age) </th> <th> @Html.DisplayNameFor(model => model.Address) </th> <th></th> </tr> </thead> <tbody> @foreach (var item in Model) { <tr> <td> @Html.DisplayFor(modelItem => item.Id) </td> <td> @Html.DisplayFor(modelItem => item.Name) </td> <td> @Html.DisplayFor(modelItem => item.Age) </td> <td> @Html.DisplayFor(modelItem => item.Address) </td> <td> @Html.ActionLink("Edit", "Edit", new { partitionKey = item.PartitionKey }) | @Html.ActionLink("Details", "Details", new { partitionKey = item.PartitionKey }) | @Html.ActionLink("Delete", "Delete", new { partitionKey = item.PartitionKey }) </td> </tr> } </tbody> </table>
Details.cshtml(用戶詳情)
@model Azure.CosmosDB.Models.UserViewModel @{ ViewData["Title"] = "Details"; Layout = "~/Views/Shared/_Layout.cshtml"; } <h1>Details</h1> <div> <h4>UserViewModel</h4> <hr /> <dl class="row"> <dt class = "col-sm-2"> @Html.DisplayNameFor(model => model.PartitionKey) </dt> <dd class = "col-sm-10"> @Html.DisplayFor(model => model.PartitionKey) </dd> <dt class = "col-sm-2"> @Html.DisplayNameFor(model => model.Id) </dt> <dd class = "col-sm-10"> @Html.DisplayFor(model => model.Id) </dd> <dt class = "col-sm-2"> @Html.DisplayNameFor(model => model.Name) </dt> <dd class = "col-sm-10"> @Html.DisplayFor(model => model.Name) </dd> <dt class = "col-sm-2"> @Html.DisplayNameFor(model => model.Age) </dt> <dd class = "col-sm-10"> @Html.DisplayFor(model => model.Age) </dd> <dt class = "col-sm-2"> @Html.DisplayNameFor(model => model.Address) </dt> <dd class = "col-sm-10"> @Html.DisplayFor(model => model.Address) </dd> </dl> </div> <div> @Html.ActionLink("Edit", "Edit", new { partitionKey = Model.PartitionKey }) | <a asp-action="Index">Back to List</a> </div>
// 注入 應用層Application services.AddScoped<IUserService, UserService>(); // 注入 基礎設施層 - 數據層 services.AddScoped<IUserRepository, UserRepository>(); //添加服務 services.AddAutoMapper(typeof(AutoMapperConfig)); //啓動配置 AutoMapperConfig.RegisterMappings();
默認狀況下,EF Core將在建立分區時將分區鍵設置爲的容器 "_partitionkey" 而不爲其提供任何值。可是要充分利用 Azure Cosmos 的性能,應使用通過精心選擇的分區鍵。能夠經過調用HasPartitionKey進行配置:
//配置分區鍵 modelBuilder.Entity<UserModel>() .HasPartitionKey(o => o.PartitionKey);
運行項目,能夠看到用戶列表信息,同時點擊數據的 「Details」 能夠看到用戶數據詳情信息。
ok,這裏就再也不演示運行後,測試各個增刪改查的方法,你們能夠下載代碼,配置本地環境,運行代碼進行測試
撒花🎉🎉🎉🎉🎉,今天的分析到此結束。
寫這篇文章,花了我比預期更長的時間,主要耗時用在寫Demo 上,查文檔,寫測試等。但願對你們有用。固然也能夠加深咱們對Cosmos DB能夠作什麼以及EF Core 3.1 如何操做 Cosmos DB 數據庫所提供程序的有進一步的瞭解。
若是沒有你們沒有條件在Azure 上建立Cosmos DB 進行測試,學習,可使用 「Azure Cosmos DB 仿真器」,仿真器在本地運行,並使用localdb存儲結果。它還帶有一個不錯的界面,用於查看/編輯數據。
github:https://github.com/yunqian44/Azure.CosmosDB.git
做者:Allen
版權:轉載請在文章明顯位置註明做者及出處。如發現錯誤,歡迎批評指正。