第五章 资源操作 使用Entity Framework 之在线图书馆 练习

第四章 展示了从创建项目到为项目准备数据,从创建资源模型到为资源实现各种操作的过程。
第五章介绍EF Core,并通过它来实现对数据的读取与存储。
在这里插入图片描述
EF Core 有两种使用方式:

代码优先(Code-First)→所有对于数据库以及数据表的操作等都应该使用代码来实现,比如要为表添加一个字段,那么应该在实体类中为其添加一个属性。而EF Core会将对实体类的修改同步到数据中→Migration.不会影响数据库中现有的数据方式。

数据库优先(Database First)→在程序包管理器控制台中使用
Scaffold-DbContext命令即可根据指定的数据库生成相应的代码。

EF Core中有一个非常重要的类 DbContext

1.创建实体类
创建文件夹Entities→Author类和Book类
注意 pubic ICollection Books{get;set;} = new List();
导航属性能够为两个实体建立关系。这里是一对多的关系,即一个作者可以包含若干个图书实体。
2.创建DbContext类
3.在Startup类的方法中将LibraryDbContext添加到容器中

services.AddDbContext<LibratyDbContext>(option =>
{
   option.UseSqlServer(Configuration.GetConnectionString("DefaultConnection"));
});

4.添加迁移与创建数据库

Add-Migration InitialCreation
Update-Database

命令执行成功后,此时的数据库已经创建成功了。在SQL Sever Management Studio 或者Vistual Studio 中的“SQL Server对象浏览器”查看

迁移主要用于EFCore应用程序的开发阶段,当应用程序要部署到生产环境中时,为了在生成环境创建相同的数据库或对数据库进行同样的修改,此时应在“程序包管理器控制台”中使用Script-Migration命令。执行该命令会在当前应用程序的程序集中输出目录并创建一个*.sql文件,其文件名为一串随机字符,文件的内容则是根据所有添加的迁移而生成的SQL语句

5.添加测试数据
EF Core 2.1 新增加了用于添加测试数据的API,ModelBuilder类的方法Entity()会返回一个EntityTypeBuilder对象,该对象提供了HasData方法,使用它可以将一个或多个实体对象添加到数据库中。而要得到ModelBuilder对象,则应在DbContext的派生类中重载OnModelCreating方法。

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
   base.OnModelCreating(modelBuilder);
   modelBuilder.Entity<Author>().HasData(
   new Author
   {
      Id = new Guid("72d5b5f5-3008"),
      Name = "Author 1",
      BirthDate = new DateTimeOffset(new DateTime(1990,11,12)),
      Email = "author@xxx.com"
   },...);
}

HasData方法接收一个可变参数数组,即可以添加一个或多个相同的实体类型。如果要添加的测试数据比较多,为了保存LibraryDbcontext类的简洁清晰,可以为ModelBuilder类创建扩展方法,并在扩展方法中添加数据

public static class ModelBuilderExtension
{
   public static void SeedData(this ModelBuilder modelBuilder)
   {
      modelBuilder.Entity<Author>().HasData(...);
      modelBuilder.Entity<Book>().HasData(...);
   }
}

而在LibraryDbContext类的OnModelCreating方法中,则只需要调用这个扩展方法modelBuilder.SeedData()即可。

要让这些数据添加到数据库中,还应创建一个迁移。
①Add-Migration SeedData
命令执行成功后,在Migrations目录下会添加新的迁移类文件,以—SeedData.cs结尾。
将所有的数据更新到数据库中。
Update-Database
命令执行成功后,在数据库中就可以看到新添加的数据了。
②如果要删除测试数据,则可以删除或注释调用HasData方法的代码,并添加一个迁移即可。
Add-Migration RemoveSeededData
Update-Database

如果添加引用数据是最近一次的迁移操作,并且还没有使用Update-Database命令将修改应用到数据库中,则可以使用Remove-Migration命令删除该迁移。

6.重构仓储类
创建通用仓储接口

public interface IRepositoryBase<T>//T表示实体类
{
   Task<IEnumerable<T>> GetAllAsync ();//异步方法
   Task<IEnumerable<T>> GetByConditionAsync(Expression<Func<T,bool>> expression);
   void Create(T entity);
   void Update(T entity);
   void Delete(T entity);
}
public interface IRepository2<T,TId>
{
   Task<T> GetByIdAsync(Tid id);
   Task<bool> IsExistAsync(TId id);//这两个方法的作用分别根据指定的实体Id获取实体
}
public class RepositoryBase<T,Tid>:IRepositoryBase<T>,IRepositoryBase2<T,TId> where T:class
{
   public DbContext DbContext{get;set;}
   public RepositoryBase(DbContext dbContext)
   {
     DbContext = dbContext;
   }

   public void Create(T entity)
   {
      DbContext.Set<T>().Add(entity);
   }
   public void Delete(T entity)
   {
      DbContext.Set<T>().Remove(entity);
   }
   public Task<IEnumerable<T>> GetAllAsync()
   {
     return Task.FormResult(DbContext.Set<T>().AsEnumerable());
   }
   public Task<IEnumerable><T>> GetByConditionAsync(Expression<Func<T,bool>> expression)
   {
      return Task.FormResult(DbContext.Set<T>().Where(expression).AsEnumable());
   }
   public async Task<bool> SaveAsync()
   {
     return await DbContext.SaveChangesAsync() > 0;
   }
   public void Update(T entity)
   {
      DbContext.Set<T>.Update(entity);
   }
   public async Task<T> GetByIdAsync(TId id)
   {
      return await DbContext.Set<T>.FindAsync(id);
   }
   public async Task<bool> IsExistAsync(TId id)
   {
      return await DbContext.Set<T>().FindAsync(id) != null;
   }
}

EF Core对于查询的执行采用延迟执行的方式。

  1. 对结果使用for/foreach循环
  2. 使用了ToList(),ToArray()和ToDictionary()
  3. 使用了Single(),Count(),Average,First()和Max()等方法

7.创建其他仓储接口
对于每一个实体类型,应创建针对它的仓储接口及其实现。

public interface IAuthorRepository:IRepositoyrBase<Author>,IRepositoryBase2<Author,Guid>
{}
public class AuthorRepository:RepositoryBase<Author,Guid>,IAuthorRepository
{
  public AuthorRepository(DbContext dbContext):base(dbContext)
  {}
}

同样方式创建IBookRepository和Repository

上面所有仓储接口与实现类创建完成后,继续创建仓储包装器。

public interface IRepositoryWrapper
{
   IBookRepository Book{get;}
   IAuthorRepository Author{get;}
}

RepositoryWrapper类定义了一个包含LibarayDbContext类型参数的构造函数,它会使用该对象实例化其中所有的仓储类,并最终将该参数传递给RepositoryBase类

public class RepositoryWrapper:IRepositoryWrapper
{
   private IAuthorRepository _authorRepository = null;
   private IBookRepository _bookRepository = null;
   public LibararyDbContext LibararyDbContext{get;}
   public RepositoryWrapper(LibraryDbContext libraryDbContext)
   {
      LibraryDbContext = libararyDbContext;
   }
   public IAuthorRepository Author => _authorRepository?? new AuthorRepository(LibraryDbContext);
   public IBookRepository Book => _bookRepository ?? new BookRepository(LibratyDbContext);
}

至此,对于仓储类的重构基本完成。接下来,要将IRepositoryWrapper及其实现放到容器中,在SetUp类的ConfigureServices方法中。

services.AddScoped<IRepositoryWrapper,RepositoryWrapper>();

8.重构Controller和Action 略

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值