2

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

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

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

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

4

1 回答 1

0

我绝不是专家DataLoadOptions,但从您的问题和我所读到的内容来看,您似乎需要使用它来进行急切加载。参考这个:

“因为当您使用 dataloadoptions 时,在应用新的 dataloadoptions 之前,您必须没有对 datacontext 进行过先前的查询。”

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

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

正如您所提到的,这会使未预加载到枚举中的对象在方法返回后无法访问。但是,如果您已经执行了查询并将其转换为 a ListCollectionArray或其他一些具体IEnumerable的 ,则不再需要访问Count()orToList() 方法。相反,您可以使用Array.LengthorList.CountCollection.Count properties

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

回复评论

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

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();
}

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

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
    }
}

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

public void SaveMember(Member member)
{
    var memberDto = member;
    using (var context = new MyDataContext())
    {
        member = context.Members.Single(m => m.Id == memberDto.Id);
        member.Email = memberDto.Email;
        member.IsSuspended = memberDto.IsSuspended;
        context.SaveChanges(); // or whatever the linq to sql equivalent is
    }
}
于 2012-04-09T22:17:42.170 回答