Azure Cosmos DB (三) EF Core 实现 CURD Demo

一,引言

  接着上一篇使用 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

--------------------我是分割线--------------------数据库

1,Azure Cosmos DB (一) 入门介绍

2,Azure Cosmos DB (二) SQL API 操做

3,Azure Cosmos DB (三) EF Core 操做CURD Demo

二,正文

1,Repository 基类仓储实现类

将以前建立好的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     }
Repository.cs
 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     }
IRepository.cs
1     /// <summary>
2     /// IUserRepository接口
3     /// </summary>
4     public interface IUserRepository : IRepository<UserModel>
5     {
6         //一些UserModel独有的接口
7         Task<UserModel> GetByName(string name);
8     }
IUserRepository.cs
 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     }
UserRepository.cs

注意,在更新操做中,有个小坑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,实现业务层 Service 方法

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     }
UserService.cs
 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     }
IApplicationService.cs
1 public interface IUserService:IApplicationService<UserViewModel>
2     {
3         /// <summary>
4         /// 注册
5         /// </summary>
6         /// <param name="userViewModel"></param>
7         Task<int> Register(UserViewModel userViewModel);
8     }
IUserService.cs

3,建立 UserController 的CRUD 方法及页面

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>

4,依赖注入仓储,服务模型以及AutoMap 配置等

// 注入 应用层Application
services.AddScoped<IUserService, UserService>();

// 注入 基础设施层 - 数据层
services.AddScoped<IUserRepository, UserRepository>();

//添加服务
services.AddAutoMapper(typeof(AutoMapperConfig));
//启动配置
AutoMapperConfig.RegisterMappings();

5,配置分区键(分区键属性能够是任何类型,只要将其转换为string)

默认状况下,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 

版权:转载请在文章明显位置注明做者及出处。如发现错误,欢迎批评指正。

相关文章
相关标签/搜索