MVC,Repository Pattern和DataLoadOptions

时间:2012-04-09 11:11:27

标签: asp.net-mvc asp.net-mvc-3 repository-pattern

我有一个小项目,我正在运行MVC3。 我使用LINQ从数据库中获取数据。 我使用与MVC3附带的预制示例相同的架构设计构建了我的项目。 在这样的项目中,应用程序被拆分,在本主题中,我想关注Model.cs文件。我现在每个控制器都有一个,所以作为一个例子,我有一个HighscoreController.cs和一个HighscoreModels.cs。在模型类中,我定义了一个Service类,该类具有对datacontext的引用,以及一些使用此datacontext查询数据库的方法。 现在我遇到了一些问题,其中一些方法正在执行相同的查询,因此我想建立一个访问数据库的中心点,所以我想我会实现存储库模式,所以我做了。 因此,我没有在Service类中引用datacontext,而是现在引用了存储库:

    private IRepository _repository;

    public HighscoreService()
        : this(new Repository())
    { }

    public HighscoreService(IRepository repository)
    {
        _repository = repository;
    }

现在,数据库调用在存储库中处理,存储库通过_repository引用从Service类中使用。

我的存储库是这样构建的:

   public class Repository : IRepository
   {
    private MyDataContext _dataContext;

    public Repository()
    {
        _dataContext = new MyDataContext();
    }

    public Member MemberByName(string memberName)
    {
        Member member = CompiledQueries.MemberByName(_dataContext, memberName);
        return member;
    }
    }

当我尝试将DataLoadOptions与此存储库模式结合使用时,出现了我遇到的问题。

因为当您使用dataloadoptions时,在对其应用新的dataloadoptions之前,您不得在datacontext上进行过先前的查询。由于我的存储库在所有方法中都重用了datacontext,因此根本无法解决问题。 我一直在尝试两件事,一件是通过using语句在每个方法中重新创建datacontext,以确保每次都刷新datacontext。但是当我将存储库中的结果返回到我的模型中并且作用域在存储库模式内耗尽时,我会遇到问题,因为using语句结束,这意味着结果不能用于例如.Count()或.ToList()因为提供数据的datacontext已被终止。我还尝试了另一种解决方案,它在整个存储库中使用相同的datacontext,但在每个使用dataloadoptions的方法中创建一个新实例。这感觉很脏;) 那么有人可以给我一个关于如何将DataLoadOptions与存储库模式一起使用的建议吗?并避免我刚才描述的问题。或者我应该不使用dataloadoptions并选择其他方式吗?我顺便使用DataLoadOptions的原因是我想从相关表中获取一些数据。

作为一个小问题:在上面的代码示例中,您可以看到我已将CompiledQueries放在自己的.cs文件中。这是一个糟糕的设计吗?是否有关于将编译查询放在MVC应用程序中的指南?

感谢阅读并希望我的问题有一些答案;)非常感谢。如果您需要更多信息,请询问。

1 个答案:

答案 0 :(得分:0)

我绝不是DataLoadOptions的专家,但是根据你的问题以及我对它的了解,似乎你需要将它用于急切加载。参考这个:

“因为当您使用dataloadoptions时,在对其应用新的dataloadoptions之前,您不得在datacontext上进行过先前的查询。”

..对我来说这听起来像DataLoadOptions的缺点或设计缺陷(我个人使用实体框架,而不是LINQ to SQL)。虽然我认为按照ngm和CrazyCoderz的前两条评论中提供的每个HTTP请求有一个数据上下文是个好主意,但我认为这不会解决您的问题。如果要在单个HTTP请求中重用单个数据上下文,则只要执行第一个查询,就会发现您无法将数据上下文中的DataLoadOptions设置为新值。

我在vslive vegas上看到了一个演示文稿,演示者提供了您提到的解决方案之一,在每个存储库方法中创建了一个新的数据上下文。你需要做的是在终止using语句并返回方法结果之前调用ToList()或ToArray()

正如您所提到的,这将使方法返回后未预加载到枚举中的对象不可访问。但是,如果您已执行查询并将其转换为ListCollectionArray或其他具体IEnumerable,则无需访问{ {1}}或Count() 方法不再。相反,您可以使用ToList()Array.LengthList.Count 属性

还有什么阻止您在每个存储库方法中创建新的数据上下文?意思是,在执行存储库方法之后,您需要什么样的数据上下文,因为它已被处理掉而无法获取?

回复评论

对于您的第一个查询,您可以这样做吗?

Collection.Count

如果您的Members表有很多行,那么可能不是最佳的。在这种情况下,您需要执行2个查询 - 1个计数,第二个选择行。您可以通过打开2个上下文来实现这一点:

public Member GetSomeRandomMember()
{
    Member[] members = null;
    using (var context = new MyDataContext())
    {
        // execute the query to get the whole table
        members = context.Members.ToArray();
    }

    // do not need to query again
    var totalRows = members.Length;
    var skipThisMany = PerformRandomNumberComputation(totalRows);
    return members.Skip(skipThisMany).FirstOrDefault();
}

对于评论的第二部分,我不确定我得到的是你在说什么。无论如何,数据的获取和对数据的更改都应该在单个操作中完成:

public Member GetSomeRandomMember()
{
    using (var context1 = new MyDataContext())
        var totalRows = context1.Members.Count();

    var skipThisMany = PerformRandomNumberComputation(totalRows);

    Member member = null;
    using (var context2 = new MyDataContext())
        member = context2.Members.Skip(skipThisMany).FirstOrDefault();

    return member;
}

如果要将整个实体传递给存储库方法,您仍应查询它,以便将其附加到正确的上下文:

public void SaveMember(int id, string email, bool isSuspended)
{
    using (var context = new MyDataContext())
    {
        var member = context.Members.Single(m => m.Id == id);
        member.Email = email;
        member.IsSuspended = isSuspended;
        context.SaveChanges(); // or whatever the linq to sql equivalent is
    }
}