ASP.Net MVC 和实体框架项目架构,没有存储库模式
本文关键字:存储 模式 MVC Net 实体 项目 框架 ASP | 更新日期: 2023-09-27 18:35:42
我正在用 ASP.NET MVC和实体框架开始一个新的小项目。(SQL Server - 大约 20 个数据库表)在过去的项目中,我使用过Linq2SQL,但它似乎已经过时了。
我已经阅读了很多关于将存储库模式用于EF(优点和缺点)的文章,对我来说,没有存储库模式的代码似乎更好/更简单。
我创建了以下项目架构:
namespace MySite.Models
{
public class User
{
public Int32 ID { get; set; }
public String Email { get; set; }
public String Password { get; set; }
public String Name { get; set; }
public Int32 Gender { get; set; }
}
}
namespace MySite.DAL
{
public class Users
{
public static IEnumerable<User> GetUsers()
{
using (var context = new DatingSiteContext())
{
return context.Users.ToList();
}
}
public static User GetUserByID(int id)
{
using (var context = new DatingSiteContext())
{
return context.Users.Find(id);
}
}
}
namespace MySite.Controllers
{
public class HomeController : Controller
{
public ActionResult Index()
{
ViewBag.Message = "Modify this template to jump-start your ASP.NET MVC application.";
var users = DAL.Users.GetUsers();
return View(users);
}
}
}
- 像这样使用 EF 有什么缺点?(缺少单元除外测试支持)
- 在每次调用 DAL 时创建新的 DbContext 是错误的吗?性能有影响吗?
- 使用 EF 的任何其他推荐结构?例子?:)
- 你会在新项目中使用 Linq2SQL 吗?
谢谢。
编辑:
GetUsers() 和 GetUserByID() 中的代码只是例如,我知道从 db 返回所有记录是一种不好的做法(缺少分页或过滤器)
您实际上刚刚创建了一个存储库,只是您称它为"数据访问层",在我看来,这不是一个好名字,因为实体框架是数据访问层。存储库是数据访问层(在本例中为实体框架)之上的抽象。
<小时 />在每次调用 DAL 时创建新的 DbContext 是错误的吗?任何 性能命中?
不,这很好,但是当您在一个DbContext
实例中获取实体并尝试在另一个实例中更新它时,可能会导致麻烦。
你会在新项目中使用 Linq2SQL 吗?
不,Microsoft提出的实体框架作为L2SQL的继承者,并且它的积极开发已经停止。
<小时 />使用 EF 的任何其他推荐结构?例子?:)
您使用的方法,特定的存储库,将导致大量冗余代码。您可以创建一个实现接口的通用存储库:
public interface IRepository<TEntity>
where TEntity : class, new()
{
IEnumerable<TEntity> GetAll();
TEntity GetById(int id);
IQueryable<TEntity> Table { get; }
}
并实现这一点:
public EfRepository<TEntity> : IRepository<TEntity>
where TEntity : class, new()
{
private readonly DatingSiteContext _context;
public EfRepository()
{
_context = new DatingSiteContext();
}
private IDbSet<TEntity> Entities
{
get
{
return _context.Set<TEntity>();
}
}
public IEnumerable<TEntity> GetAll()
{
return Entities.ToList();
}
public TEntity GetById(int id)
{
return Entities.Find(id);
}
public IQueryable<TEntity> Table
{
get { return Entities; }
}
}
您可以在控制器中使用此存储库,如下所示:
public class HomeController : Controller
{
private readonly IRepository<User> _userRepository;
public HomeController()
{
_userRepository = new EfRepository<User>();
}
public ActionResult Index()
{
var users = _userRepository.GetAll();
var inactiveUsers = _userRepository.Table.Where(u => !u.Active).ToList();
}
}
此通用存储库允许您创建模拟存储库:
public class FakeUserRepository : IRepository<User>
{
// ...
}
<小时 />这种方法可能看起来像很多代码,但随着实体类型数量的增加,它将为您节省大量工作,因为您所要做的就是在控制器中创建IRepository<>
字段。但是,IQueryable<>
属性具有很大的灵活性,它允许延迟执行。
我并不是说这是最好的方法,只是我在项目中经常使用的方法。不得不说,我通常在控制器和存储库之间编写一个业务(服务)层。我把我的业务逻辑和复杂的 Linq 查询(及其执行)留在那里。我还使用一个 IoC 容器来处理我的对象(例如DbContext
和服务实例)的生存期。有关此内容的更多信息,请参阅此问题。
我的想法
缺点是什么:
- 你不能在使用你在 DAL 中定义的静态方法的任何地方真正单元测试。 它们
- 也是强耦合的,这使得它们在运行时更难换出,如果这成为一项要求的话。
- 如果您需要在事务中提交多个更新,您可能会开始遇到额外的复杂性
在每次调用时创建新的 DbContext 是错误的吗?
- 不,这很好。DbContext 是轻量级的,旨在以这种方式使用。
其他模式
- 您已经提到了存储库模式,它非常可靠,尤其是在与工作单元模式一起使用时。
你会使用 Linqtosql 吗
- 不 - Linqtosql 已经完成了,实体框架为这个问题提供了一个更完整、通常更好的解决方案
我会重新考虑你是如何实现GetUsers()
的。您正在调用ToList()
这将导致返回基础表中的所有行并将其存储在内存中。如果表增长到足够大,则会遇到性能问题。最好改为返回IQueryable<User>
并return context.Users
您的方法。
当然,您会遇到一个问题,即在执行IQueryable<>
时上下文已经处理完毕,因此您需要以不同的方式处理上下文的生命周期。
如果项目足够小,则可以在控制器级别存储上下文的实例,并在释放控制器时释放它。如果这样做,请确保不要在视图中执行任何会导致执行其他查询的操作(例如,如果存在,则访问User
集合),否则会出错。