C#,Linq2SQL - 用关系数据获取ViewModel对象的技巧?

时间:2011-03-10 22:42:50

标签: c# asp.net-mvc linq-to-sql mvvm

我还不太了解Linq2Sql,我想知道这个可能是常见的MVVM场景是否有诀窍。我有包含域模型的Linq2Sql数据上下文,但我从中获取自定义ViewModel对象的数据。

var query = from ord in ctx.Table_Orders
        select new OrderViewModel()
        {
            OrderId = ord.OrderId,
            OrderSum = ord.OrderSum,
            OrderCurrencyId = ord.OrderCurrencyId,
            OrderCurrencyView = ord.Currency.CurrencyText
        };

所以我希望我的ViewModel从域对象中包含CurrencyId和从相关表中包含CurrencyText,以便在View中很好地显示它。

此代码效果很好。它使用join生成一个DB调用以获取CurrencyText。但该模型简化,真正的模型有更多的字段。我想使代码可重用,因为我有许多不同的查询,它返回相同的ViewModel。现在,对OrderViewModel的每一个小改动都需要大量的维护。

所以我将代码移动到OrderViewModel本身作为构造函数。

public OrderViewModel(Table_Order ord)
{
    OrderId = ord.OrderId,
    OrderSum = ord.OrderSum,
    OrderCurrencyId = ord.OrderCurrencyId,
    OrderCurrencyView = ord.Currency.CurrencyText
}

并称之为。

var query = from ord in ctx.Table_Orders
        select new OrderViewModel(ord);

问题:联接已经消失数据库查询不再优化。现在我得到1 + N次调用数据库来获取每行的CurrencyText。

欢迎任何评论。也许我错过了不同的方法。

这就是 far 我可以独立完成的方式,以获得代码可重用性。我创建了一个完成工作并具有多个参数的函数。然后我需要明确地传递它与交叉实体线的所有内容。

var query = ctx.Table_Orders.Select(m =>
       newOrderViewModel(m, m.Currency.CurrencyText));

再次优化数据库调用。但它仍然不觉得我在那里!对于这种情况你知道什么技巧?


编辑:最终解决方案 感谢@Muhammad Adeel Zahid的暗示,我得到了这个解决方案。 我为IQueryable创建了一个扩展

public static class Mappers
{
    public static IEnumerable<OrderViewModel> OrderViewModels(this IQueryable<Table_Order> q)
    {
        return from ord in q
                select new OrderViewModel()
                {
                    OrderId = ord.OrderId,
                    OrderSum = ord.OrderSum,
                    OrderCurrencyId = ord.OrderCurrencyId,
                    OrderCurrencyView = ord.Currency.CurrencyText
                };
    }
}

现在我可以这样做以获取所有列表

var orders = ctx.Table_Order.OrderViewModels().ToList();

或者这样可以获得单个项目,或者其中任何内容与Where(x =&gt; ..)

var order = ctx.Table_Order
   .Where(x => x.OrderId == id).OrderViewModels().SingleOrDefault();

这完全解决了这个问题。生成的SQL非常完美,翻译对象的代码是可重用的。像这样的方法应该适用于LINQ to SQL和LINQ to Entities。 (未经后者测试)再次感谢你@Muhammad Adeel Zahid

2 个答案:

答案 0 :(得分:1)

通过让Linq2SQL急切地加载构建视图模型所需的引用entites,可以避免N + 1查询问题。这样,您可以构建一个对象列表(以及一些引用的对象)并使用它来构造所有内容。看看这个blog post

但有一个警告:这种技术(为Linq2SQL数据上下文设置LoadOptions)每个数据上下文只能执行一次。如果需要使用不同的预先加载配置执行第二个查询,则必须重新初始化数据上下文。我使用围绕我的上下文的简单包装类来自动化它。

答案 1 :(得分:1)

每当我们查询数据库时,我们通常要求枚举对象(db中有多个记录),或者我们需要单个实体(db中的一个记录)。您可以在返回整个表的枚举的方法中编写映射代码,如

public IEnumerable<OrderViewModel> GetAllOrders()
{
    return from ord in ctx.Table_Orders
        select new OrderViewModel()
        {
            OrderId = ord.OrderId,
            OrderSum = ord.OrderSum,
            OrderCurrencyId = ord.OrderCurrencyId,
            OrderCurrencyView = ord.Currency.CurrencyText
        };

} 

现在您可能希望过滤这些记录并返回另一个枚举,例如在currencyID

public IEnumerable<OrderViewModel> GetOrdersByCurrency(int CurrencyID)
{
      return GetAllOrders().Where(x=>x.CurrencyId == CurrencyID);
}

现在您可能还希望从所有这些视图模型中找到单个记录

public OrderViewModel GetOrder(int OrderID)
{
      return GetAllOrders().SingleOrDefault(x=>x.OrderId == OrderID);
}

IEnumerable的优点在于它不断为查询添加条件,并且在需要之前不执行它。因此,除非您真的需要,否则您的整个表格都不会被加载,并且您将代码保存在一个位置。现在,如果ViewModel Mapping或查询本身有任何更改,则必须在GetAllOrders()方法中完成,其余代码将保持不变