4

我正在使用 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() 中的代码仅作为示例,我理解从数据库返回所有记录是一种不好的做法(缺少分页或过滤器)

4

3 回答 3

7

实际上,您只是创建了一个存储库,只是将其称为“数据访问层”,在我看来,这不是一个好名字,因为实体框架是数据访问层。存储库是数据访问层(在本例中为实体框架)之上的抽象。


在每次调用 DAL 时创建一个新的 DbContext 是错误的吗?任何性能命中?

不,这很好,但是当您在 的一个实例中获取实体DbContext并尝试在另一个实例中更新它时,它可能会导致麻烦。


你会在新项目中使用 Linq2SQL 吗?

不,微软提出实体框架作为 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和服务)。有关详细信息,请参阅此问题。

于 2013-10-17T14:48:49.513 回答
1

我的想法

有什么缺点:

  • 您无法在任何使用您在 DAL 中定义的静态方法的地方进行真正的单元测试。
  • 它们也是强耦合的,如果需要的话,它们在运行时更难更换。
  • 如果您需要在事务中提交多个更新,您可能会开始遇到额外的复杂情况

在每次调用时创建一个新的 DbContext 是错误的吗?

  • 不,这很好。DbContext 是轻量级的,旨在以这种方式使用。

其他图案

  • 您已经提到了非常可靠的存储库模式,尤其是在与工作单元模式一起使用时。

你会使用 Linqtosql

  • 不——Linqtosql 已经完成了,实体框架为这个问题提供了一个更完整和更好的解决方案
于 2013-10-17T14:21:03.503 回答
0

我会重新考虑您如何实施GetUsers(). 您正在调用ToList()这将导致基础表中的所有行返回并存储在内存中。如果表变得足够大,您将遇到性能问题。最好返回一个IQueryable<User>并拥有你的方法return context.Users

当然你会遇到在执行时上下文已经被释放的问题IQueryable<>,所以你需要以不同的方式处理上下文的生命周期。

如果项目足够小,那么您可以只在 Controller 级别存储 Context 的实例,并在处置控制器时处置它。如果你这样做了,请确保你没有在你的视图中做任何会导致额外查询被执行的事情(例如访问一个集合,User如果一个存在的话),否则会出错。

于 2013-10-17T14:31:40.343 回答