1

作为 MVC 3 和 EF 的新手,我试图了解为我的公司开发应用程序的最佳架构方法。该应用程序将是一个可能同时处理数百个用户的大型应用程序,因此我想确保我理解并遵循正确的程序。到目前为止,我已经确定简单的存储库模式(例如 Controller -> Repository -> EF)方法是最好且最容易实现的方法,但我不确定这是否绝对是最好的方法。该应用程序基本上将返回在 devexpress 网格中显示给用户的数据,他们可以修改这些数据/添加到它等等。

我发现了这篇文章,此时我感到很困惑,所以我想知道是否有任何理由尝试使用断开连接的 EF,以及为什么你甚至想要这样做:http://www.codeproject .com/Articles/81543/Finally-Entity-Framework-working-in-fully-disconne?msg=3717432#xx3717432xx

所以总结一下我的问题:

  • 下面的代码可以接受吗?
  • 它应该适用于大型 MVC 应用程序吗?
  • 有没有更好的办法?
  • 是否会从 EF 保持打开与 SQL 的不必要连接?(SQL Profiler 让它看起来像在 using 语句退出后保持打开一段时间)
  • 断开连接的框架想法是更好的想法吗?您为什么还要这样做?我不相信我们需要跨层跟踪数据......

注意:存储库实现 IDisposable 并具有下面列出的 dispose 方法。它在存储库构造函数中创建实体上下文的新实例。

示例用法:

控制器(使用自定义成员资格提供程序登录):

if (MembershipService.ValidateUser(model.UserName, model.Password))
{
    User newUser = new User();                    

    using (AccountRepository repo = new AccountRepository())
    {
         newUser = repo.GetUser(model.UserName);
         ...
    }
}

会员提供者验证用户:

public override bool ValidateUser(string username, string password)
{
    using (AccountRepository repo = new AccountRepository())
    {
        try
        {
            if (string.IsNullOrEmpty(password.Trim()) || string.IsNullOrEmpty(username.Trim()))
                return false;

            string hash = FormsAuthentication.HashPasswordForStoringInConfigFile(password.Trim(), "md5");

            bool exists = false;

            exists = repo.UserExists(username, hash);

            return exists;
        }catch{
            return false;
        }
    }
}

GetUser 和 UserExists 的帐户存储库方法:

获取用户:

public User GetUser(string userName)
    {
        try
        {
            return entities.Users.SingleOrDefault(user => user.UserName == userName);
        }
        catch (Exception Ex)
        {
            throw new Exception("An error occurred: " + Ex.Message);
        }           
    }

用户存在:

 public bool UserExists(string userName, string userPassword)
 {
        if (userName == "" || userPassword == "")
            throw new ArgumentException(InvalidUsernamePassword);

        try
        {
            bool exists = (entities.Users.SingleOrDefault(u => u.UserName == userName && u.Password == userPassword) != null);
            return exists;
        }
        catch (Exception Ex)
        {
            throw new Exception("An error occurred: " + Ex.Message);
        } 
    }

存储库片段(构造函数、Dispose 等):

    public class AccountRepository : IDisposable
    {
         private DbContext entities;

         public AccountRepository()
         {
            entities = new DbContext();
         }

         ...

         public void Dispose()
         {
            entities.Dispose();
         }
    }
4

2 回答 2

5

可以接受的是相当主观的,但是如果您想进行适当的数据访问,我建议您不要使用存储库模式,因为它会随着您的应用程序变得更加复杂而崩溃。

最大的原因是最小化数据库访问。因此,例如查看您的存储库并注意该GetUser()方法。现在从代码中退后一步,想想你的应用程序将如何被使用。现在考虑一下在没有任何附加数据的情况下,您将多久从用户表请求数据。除非您正在创建一个基本的数据输入应用程序,否则答案几乎总是“很少”。

你说你的应用程序会显示很多网格。该网格中有哪些数据?我假设(不知道您的应用程序域)网格会将用户数据与与该用户相关的其他信息结合起来。如果是这种情况,您如何处理您的存储库?

一种方法是单独调用每个存储库的方法,如下所示:

var user = userRepository.GetUser("KallDrexx");
var companies = companyRepository.GetCompaniesForUser(user.Id);

现在这意味着您有 2 个数据库调用,而实际上应该只是一个。随着您的屏幕变得越来越复杂,这将导致数据库点击次数越来越多,如果您的应用程序获得大量流量,这将导致性能问题。在存储库模式中执行此操作的唯一真正方法是向存储库添加特殊方法来执行特定查询,例如:

public class UserRepository
{
    public User GetUser(string userName)
    {
        // GetUser code          
    }

    public User GetUserWithCompanies(string userName)
    {
        // query code here
    }
}

因此,如果您需要用户并在一次查询中说出他们的联系数据,那么现在会发生什么。现在您必须向您的用户存储库添加另一个方法。现在假设您需要执行另一个查询,该查询还返回每个公司拥有的客户数量,因此您需要添加另一个方法(或添加一个可选参数)。现在假设您要添加一个查询,该查询返回所有公司及其包含的用户。现在您需要一种新的查询方法,但随之而来的问题是您将其放入User存储库还是Company存储库?您如何跟踪它在哪一个中,GetUserWithCompanyGetCompanyWithUsers在以后需要它时轻松进行选择?

从那时起,一切都变得非常复杂,正是这些情况让我放弃了存储库模式。我现在对数据访问所做的是创建单独的查询和命令类,每个类代表对数据库的 1 个(并且只有 1 个)查询或数据更新命令。每个查询类都返回一个视图模型,该模型只包含一个特定用户使用场景所需的数据。还有其他数据访问模式也可以工作(规范模式,一些优秀的开发人员甚至说您应该只在控制器中进行数据访问,因为 EF您的数据访问层)。

成功进行数据访问的关键是良好的规划。你知道你的屏幕会是什么样子吗?您知道用户将如何使用您的系统吗?您知道每个屏幕上实际显示的所有数据吗?如果其中任何一个的答案是否定的,那么您需要退后一步,忘记数据层,因为数据层(或应该用于一个好的应用程序)是根据应用程序的实际情况确定的使用时,UI 和屏幕不应依赖于数据层的设计方式。如果您在开发数据访问时没有考虑您的 UI 需求和用户使用场景,那么您的应用程序将无法很好地扩展并且不会具有高性能。有时,如果您不打算让您的网站变大,这不是问题,

于 2012-02-16T13:52:54.747 回答
1

无论您做什么,您都可以考虑将实例化并将上下文处理到您的控制器,如下所示:

public class MyController : Controller
{
    private Entities context = new Entities();

    ...

    public override void Dispose()
    {
        context.Dispose();
    }
}

然后,您可以将该上下文传递给任何需要它的方法,而无需重复创建它的开销。

由于同样的原因,我不同意存储库模式必然是不好的。您创建多个类来分解代码以使其易于管理并仍然重用相同的上下文。这可能看起来像这样:

repository.Users.GetUser(userName);

在这种情况下,“用户”是用户存储库类的延迟加载实例,它重用存储库中的上下文。因此,您的存储库中该 Users 属性的代码将如下所示:

private UserRepository users;

public UserRepository Users
{
    get
    {
        If (users == null)
        {
            users = new UserRepository(this);
        }
        return users;
    }
}

然后,您可以通过属性将您的上下文公开给这些其他延迟加载的类。

我认为这不一定与 KallDrexx 的模式相冲突。他的方法只是简单地翻转了这个,而不是

repository.Users.GetUser(userName);

你会有类似的东西

UserQuery query = new UserQuery(repository.Users);

这将成为语法问题。你想要这个吗:

repository.Area.Query(value1, value2, ...);

或这个:

AreaQuery query = new AreaQuery { Property1 = value1, ... };

后者实际上更适合模型绑定,但当您实际必须对其进行编码时显然更冗长。

KallDrexx 给出的最佳建议是将您的代码放在您的操作中,然后弄清楚。如果您正在执行简单的 CRUD,然后让 MVC 实例化并填充您的模型,那么您所要做的就是附加和保存。如果您发现可以重用代码,请将其移至可以重用的地方。如果您的应用程序开始变得过于复杂,请尝试其中的一些建议,直到找到适合您的方法。

于 2012-02-18T05:08:42.380 回答