【问题标题】:Is there a way to use Linq projections with extension methods有没有办法将 Linq 投影与扩展方法一起使用
【发布时间】:2011-01-07 20:46:41
【问题描述】:

我正在尝试使用 AutoMapper 和存储库模式以及流畅的界面,但在使用 Linq 投影时遇到了困难。值得一提的是,仅使用内存中的对象时,此代码就可以正常工作。但是,当使用数据库提供程序时,它会在构建查询图时中断。我已经尝试过 SubSonic 和 Linq to SQL,结果相同。感谢您的想法。

这是一个在所有场景中都使用的扩展方法 - 这是问题的根源,因为不使用扩展方法一切正常

public static IQueryable<MyUser> ByName(this IQueryable<MyUser> users, string firstName)
{
     return from u in users
            where u.FirstName == firstName
            select u;
}

这是运行良好的内存代码

var userlist = new List<User> {new User{FirstName = "Test", LastName = "User"}};

Mapper.CreateMap<User, MyUser>();
var result = (from u in userlist
                   select Mapper.Map<User, MyUser>(u))
                   .AsQueryable()
                   .ByName("Test");

foreach (var x in result)
{
     Console.WriteLine(x.FirstName);
}

这是使用失败的 SubSonic(或 Linq to SQL 或其他)的同一件事。这就是我想通过扩展方法以某种方式实现的功能......

Mapper.CreateMap<User, MyUser>();

var result = from u in new DataClasses1DataContext().Users
                          select Mapper.Map<User, MyUser>(u);

var final = result.ByName("Test");
foreach(var x in final) // Fails here when the query graph built.
{
     Console.WriteLine(x.FirstName);
}

这里的目标是避免必须手动将生成的“用户”对象映射到“我的用户”域对象——换句话说,我正在尝试找到一种使用 AutoMapper 的方法,所以我没有这种在需要数据库读取操作的任何地方映射代码:

var result = from u in new DataClasses1DataContext().Users
                          select new MyUser // Can this be avoided with AutoMapper AND extension methods?  
                          {
                             FirstName = v.FirstName,
                             LastName = v.LastName
                          };

【问题讨论】:

    标签: linq extension-methods linq-to-objects


    【解决方案1】:

    嗯,我不知道 SubSonic 的 LINQ 实现。然而,问题的原因可能是 LINQ-Provider 未能使用“Mapper”调用。它期望可以翻译成 SQL 的东西。

    顺便说一句,我会在“用户”查询上使用 .ByName() 而不是当前的实现。因为目前必须映射结果才能运行 .ByName()。因此,您从数据库中检索大量用户实例,然后映射它们并过滤它们。如果您在“用户”上使用 .ByName,则可以将其翻译成 SQL,而无需检索。

    所以我猜这样的东西会起作用:

    public static IQueryable<User> ByName(this IQueryable<User> users,
                                          string firstName)
    {
        return from u in users
            where u.FirstName == firstName
            select u;
    }
    

    现在您将映射部分添加到末尾: 映射器.CreateMap();

    var result = from u in new DataClasses1DataContext().Users.ByName("Test")
                    select Mapper.Map<User, MyUser>(u);
    // now the 'ByName'-constain can be ran on the database
    
    foreach(var x in result)
    {
         Console.WriteLine(x.FirstName);
    }
    

    如果它仍然不起作用,您可能需要强制为 Mapper 部分使用 LINQ-to-Object: 映射器.CreateMap();

    var result = DataClasses1DataContext().Users.ByName("Test").ToList();
    // now the result is a regualar list, so LINQ-to-object is used for the mapping part
    
    var final = from u in result
                       select Mapper.Map<User, MyUser>(u);
    
    foreach(var x in final)
    {
         Console.WriteLine(x.FirstName);
    }
    

    顺便说一句,您可能想添加一个“SubSonic”标签,以便 SubSonic 专家回答您的问题。

    【讨论】:

    • 我考虑过首先调用 ToList 的方法,但我想避免在代码实际需要枚举数据之前调用它。换句话说,您的中间代码 sn-p 在 foreach 循环内的枚举器调用中命中数据库(在这种情况下是一件好事)。调用 ToList 时,您的最后一个 sn-p 会访问数据库。这在我的情况下不起作用,因为扩展方法不在数据库项目中......在 IoC 的情况下,它是注入的几个数据库实现之一。
    猜你喜欢
    • 2011-03-16
    • 2013-11-26
    • 1970-01-01
    • 1970-01-01
    • 2018-03-15
    • 2016-11-01
    • 1970-01-01
    • 2018-09-07
    • 1970-01-01
    相关资源
    最近更新 更多