这是一篇关于在 ASP.Net Core 中应用 Repository 模式的攻略。我们将使用 Entity Framework 6,以及 UnitOfWork 模式和 Autofac 依赖注入框架来实现它。下面将是具体的步骤:
准备工作
在继续之前,我们需要确保在系统中安装了以下软件:
- Visual Studio 2017 及以上版本
- .NET Core SDK 及以上版本
- Entity Framework 6.2 及以上版本
创建项目
首先我们需要新建一个 ASP.Net Core Web 应用程序。我们可以选择在 Visual Studio 2017 中使用 “ASP.Net Core Web 应用程序” 模板来创建。选择 Web 应用程序模板后,我们需要选择 ASP.Net Core 的版本以及项目的类型(例如 Web API 或 MVC)。
安装 NuGet 包
在创建完项目后,我们需要安装一些 NuGet 包。打开 NuGet 包管理器控制台并运行以下命令:
Install-Package EntityFramework
Install-Package Autofac
Install-Package Autofac.Extensions.DependencyInjection
Install-Package Autofac.Extras.DynamicProxy
Install-Package Microsoft.EntityFrameworkCore.SqlServer
这些命令将安装 Entity Framework、Autofac 及其相关插件以及 SQL Server 数据库提供程序。
创建仓储接口
接下来我们需要创建一个仓储接口,该接口定义了一些标准的 CRUD 操作。在本教程中,我们将创建一个名为 IRepository
的接口。我们可以在项目中创建一个名为 “Repositories” 的文件夹,然后在该文件夹下创建一个接口文件:
public interface IRepository<TEntity> where TEntity : class
{
TEntity GetById(int id);
void Insert(TEntity entity);
void Update(TEntity entity);
void Delete(TEntity entity);
}
在这个例子中,我们定义了四个基本的操作,并使用泛型来确保我们能够处理任何实体类型。
实现仓储接口
接下来我们需要实现 IRepository
接口。在本教程中,我们将使用 Entity Framework 6 数据访问技术实现这个接口。我们可以在项目中创建一个名为 “Repositories” 的文件夹,然后在该文件夹下创建一个名为 EfRepository
的类,该类将实现 IRepository
接口:
public class EfRepository<TEntity> : IRepository<TEntity> where TEntity : class
{
private readonly DbContext _dbContext;
public EfRepository(DbContext dbContext)
{
_dbContext = dbContext;
}
public TEntity GetById(int id)
{
return _dbContext.Set<TEntity>().Find(id);
}
public void Insert(TEntity entity)
{
_dbContext.Set<TEntity>().Add(entity);
}
public void Update(TEntity entity)
{
_dbContext.Set<TEntity>().Attach(entity);
_dbContext.Entry(entity).State = EntityState.Modified;
}
public void Delete(TEntity entity)
{
_dbContext.Set<TEntity>().Remove(entity);
}
}
在这个例子中,我们创建了一个名为 EfRepository
的类来实现 IRepository
接口。我们使用 Entity Framework 中的 DbContext
类来管理数据库连接和操作实体。在构造函数中,我们将 DbContext
通过构造函数注入到仓储实现中。
实现 UnitOfWork
现在我们需要创建一个名为 IUnitOfWork
的接口,该接口定义了一个 SaveChanges
方法,并在其内部使用事务来确保数据的完整性。我们可以在项目中创建一个名为 “UnitOfWorks” 的文件夹,然后在该文件夹下创建一个接口文件:
public interface IUnitOfWork : IDisposable
{
void SaveChanges();
}
在这个例子中,我们定义了 IUnitOfWork
接口,并指定了一个 SaveChanges
方法,该方法将确保在一个事务内提交所有待提交的更改。
接下来,我们需要一个具体的 UnitOfWork
类来实现 IUnitOfWork
接口。我们可以在项目中的名为 “UnitOfWorks” 的文件夹下创建一个名为 EfUnitOfWork
的类,代码如下:
public class EfUnitOfWork : IUnitOfWork
{
private readonly DbContext _dbContext;
private readonly ILifetimeScope _lifetimeScope;
private readonly IDictionary<Type, object> _repositories;
public EfUnitOfWork(DbContext dbContext, ILifetimeScope lifetimeScope)
{
_dbContext = dbContext;
_lifetimeScope = lifetimeScope;
_repositories = new Dictionary<Type, object>();
}
public void SaveChanges()
{
using (var transaction = _dbContext.Database.BeginTransaction())
{
try
{
_dbContext.SaveChanges();
foreach (var repository in _repositories.Values)
{
((dynamic) repository).Commit();
}
transaction.Commit();
}
catch (Exception)
{
transaction.Rollback();
throw;
}
finally
{
foreach (var repository in _repositories.Values)
{
((IDisposable) repository).Dispose();
}
_repositories.Clear();
_dbContext.Dispose();
}
}
}
public IRepository<TEntity> GetRepository<TEntity>() where TEntity : class
{
if (!_repositories.ContainsKey(typeof(TEntity)))
{
var repository = _lifetimeScope.Resolve<IRepository<TEntity>>();
_repositories.Add(typeof(TEntity), repository);
}
return (IRepository<TEntity>) _repositories[typeof(TEntity)];
}
private bool _disposed;
public virtual void Dispose(bool disposing)
{
if (!_disposed)
{
if (disposing)
{
_dbContext.Dispose();
}
_disposed = true;
}
}
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
}
在这个例子中,我们创建了一个基于 Entity Framework 的 EfUnitOfWork
类来实现我们定义的 IUnitOfWork
接口。在构造函数中,我们将 DbContext
和 Autofac 的 ILifetimeScope
通过构造函数注入到了 EfUnitOfWork
类中。此外,我们还为每个类型创建了一个仓储对象。在 SaveChanges
方法中,我们开始一个新的事务,并处理所有等待提交的更改。在成功提交更改后,我们将调用每个仓储的 Commit
方法来确保所有数据都已保存。
实现依赖注入
现在我们需要设置依赖注入来确保我们的 EfRepository
和 EfUnitOfWork
类能够被正确地注入到其他类中。为此,我们需要在 ConfigureServices
方法中做以下两件事情:
- 注册依赖项
- 设置 MVC 控制器解析
请注意,我们需要为 DbContext
和 IUnitOfWork
接口设置生命周期为“请求”,以确保在 HTTP 请求期间使用的所有组件都具有相同的依赖项。这将确保所有组件都使用相同的数据库上下文对象,并在 HTTP 请求结束时确保它被销毁。我们可以通过以下方式来配置依赖注入:
public void ConfigureServices(IServiceCollection services)
{
// ...
services.AddDbContext<MyDbContext>(options =>
{
options.UseSqlServer(Configuration.GetConnectionString("MyDbContext"));
}, ServiceLifetime.Scoped);
services.AddScoped(typeof(IRepository<>), typeof(EfRepository<>));
services.AddScoped<IUnitOfWork, EfUnitOfWork>();
services.AddControllers().AddControllersAsServices().AddJsonOptions(options =>
{
options.JsonSerializerOptions.IgnoreNullValues = true;
});
// ...
}
在这个例子中,我们首先为 DbContext
注册一个 SQL Server 数据库提供程序。我们在这里使用了 AddDbContext
方法,该方法将被发送到 IServiceCollection
,从而确保在请求期间只有一个 SQL Server 数据库实例存在。此外,我们还将 IRepository
和 IUnitOfWork
接口注册为 Scoped 生存期,以确保它们在 HTTP 请求内部具有相同的生存期,只有在请求结束时才会被销毁。
注入仓储和工作单元
现在我们已经设置好了依赖注入,所以我们可以注入我们的仓储接口和工作单元接口到我们的代码中。例如,假设我们已经创建了一个名为 MyService
的类,在这个类中我们需要使用一个仓储:
public class MyService
{
private readonly IRepository<MyEntity> _repository;
private readonly IUnitOfWork _unitOfWork;
public MyService(IRepository<MyEntity> repository, IUnitOfWork unitOfWork)
{
_repository = repository;
_unitOfWork = unitOfWork;
}
public void DoSomething()
{
var entity = new MyEntity { Name = "John Doe" };
_repository.Insert(entity);
_unitOfWork.SaveChanges();
}
}
在这个例子中,我们已经注入了一个 IRepository<MyEntity>
对象和一个 IUnitOfWork
对象到 MyService
类中。
示例说明
下面将给出两个示例说明,以便更好地理解本教程中所描述的概念。
示例 1:使用仓储
首先,我们将使用 MyService
类中注入的 IRepository<MyEntity>
对象实现一些数据访问示例:
public class MyController : Controller
{
private readonly IRepository<MyEntity> _repository;
public MyController(IRepository<MyEntity> repository)
{
_repository = repository;
}
public IActionResult GetById(int id)
{
var entity = _repository.GetById(id);
if (entity != null)
{
return Ok(entity);
}
return NotFound();
}
public IActionResult Create()
{
var entity = new MyEntity { Name = "John Doe" };
_repository.Insert(entity);
return Ok();
}
}
在这个示例中,我们创建了一个名为 MyController
的 ASP.Net Core 控制器来处理两个请求。 GetById
请求接受一个参数 id
,并使用 IRepository<MyEntity>
中的 GetById
方法查找具有这个 ID 的实体。 如果找到了,就返回一个包含字符串表示的 MyEntity
对象。否则返回 404 未找到错误。
另一个动作是 Create
,它创建一个新的 MyEntity
对象并插入数据库。在这个例子中,我们使用 IRepository<MyEntity>
的 Insert
方法来操作数据。
示例 2:使用工作单元
现在我们将实现一个需要使用工作单元来确保数据完整性的功能。假设我们需要在数据库中创建一个需要提交多个更改才能保持完整性的实体。在这种情况下,我们将使用 IUnitOfWork
接口和 EfUnitOfWork
类中的 SaveChanges
方法。
下面是一个使用 IUnitOfWork
接口的示例:
public class MyController : Controller
{
private readonly IRepository<MyEntity> _repository;
private readonly IUnitOfWork _unitOfWork;
public MyController(IRepository<MyEntity> repository, IUnitOfWork unitOfWork)
{
_repository = repository;
_unitOfWork = unitOfWork;
}
public IActionResult CreateComplexEntity()
{
var entity1 = new MyEntity { Name = "John Doe" };
var entity2 = new MyEntity { Name = "Jane Doe" };
var entity3 = new MyComplexEntity
{
Entity1 = entity1,
Entity2 = entity2
};
_repository.Insert(entity1);
_repository.Insert(entity2);
_repository.Insert(entity3);
_unitOfWork.SaveChanges();
return Ok();
}
}
在这个示例中,我们创建了一个名为 CreateComplexEntity
的 ASP.Net Core 控制器动作,该动作将创建一个包含两个实体的复合实体并将它们全部插入数据库。在这个例子中,我们需要在 IUnitOfWork
接口上调用 SaveChanges
方法,这将确保在事务内提交所有更改。
本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:ASP.Net Core基于EF6、Unitwork、Autofac实现Repository模式 - Python技术站