保存的投影表达式,用于在具有两个源对象的不同linq表达式中重用

时间:2015-04-17 11:07:53

标签: c# linq dry

这就是我通常会保存投影表达式以重复用于我的DTO的方法:

public class MyUserDTO
{
    public string Forename { get; set; }
    public string Surname { get; set; }

    public static Expression<Func<MyUserDTO, MyUserDBObject>> FromDBUserProjection = u => new MyUserDTO() { Forename = u.Forename, Surname = u.Surname };

    public static IQueryable<MyUserDTO> FromDBUser(IQueryable<MyUserDBObject> dbRecords)
    {
        return dbRecords.Select(FromDBUserProjection);
    }
}

这是一个简单的DTO,具有从特定数据库对象投影所必需的投影表达式。

每当我想从数据库中查询这些数据时,我就可以像这样使用它,这样我就不必每次都写这个投影了:

IQueryable<MyUserDTO> myUsers = MyUserDTO.FromDBUser(context.MyUserDBObjects.Where(u => u.Forename = "Bob"));

通过这种方式,我将数据映射与数据库查询分开,并确保映射在不同查询中保持一致。这通常适用于我,但我现在有一种情况,我需要从两个不同的数据库对象投影,例如像这样的东西:

public class MyCompositeDTO
{
    public string User1Forename { get; set; }
    public string User1Surname { get; set; }
    public string User2Forename { get; set; }
    public string User2Surname { get; set; }
}

//simplified example to demonstrate my question
IQueryable<MyCompositeDTO> matchingUsers = from u1 in context.MyUserDBObjects
                                           join u2 in context.MyUserDBObjects on u1.Surname equals u2.Surname
                                           select new MyCompositeDTO()
                                           {
                                               User1Forename = u1.Forename,
                                               User1Surname = u1.Surname,
                                               User2Forename = u2.Forename,
                                               User2Surname = u2.Surname
                                           };

在上面的示例中,我可以轻松地在原始linq查询中创建投影,因为此时u1和u2都可用。但是有没有什么方法可以将投影分成不同的函数,就像我在第一个例子中所做的那样并将它保持为IQueryable?没有链接两个源记录的外键。

1 个答案:

答案 0 :(得分:1)

我对查询语法不熟悉所以我将不得不使用方法语法。实际上,我不确定甚至可以使用查询语法来完成;也许别人可以肯定地说。

鉴于此表达式:

public static readonly Expression<Func<MyUserDBObjects, MyUserDBObjects, MyCompositeDTO>> CompositeDtoProject = 
    (u1, u2) => 
        new MyCompositeDTO()
        {
            User1Forename = u1.Forename,
            User1Surname = u1.Surname,
            User2Forename = u2.Forename,
            User2Surname = u2.Surname
        };

你可以这样做:

IQueryable<MyCompositeDTO> matchingUsers =
    MyUserDBObjects
    .Join(
        MyUserDBObjects, 
        u1 => u1.Surname, 
        u2 => u2.Surname, 
        CompositeDtoProject);

以下意见:

我通常不提供人们从未要求的意见或替代方案,而不是回答问题,因为,当人们对我的问题这样做时,我并不关心它。但是,既然我已经提出了一个问题的解决方案,那么也许我可以通过提供我未经请求的意见来解决问题。对我而言,你所发生的事情令人困惑。我认为可以用更直接的方式完成同样的事情。考虑一下:

public class MyUserDTO
{
    public string Forename { get; set; }
    public string Surname { get; set; }
}

public static class MyUserDtoExtensions
{
    public static IQueryable<MyUserDTO> AsDto(this IQueryable<MyUserDBObject> source)
    {
        return 
            source
            .Select(x => new MyUserDTO()
                {
                    Forename = u.Forename,
                    Surname = u.Surname
                }
    }
}

然后代替这个

IQueryable<MyUserDTO> myUsers = MyUserDTO.FromDBUser(context.MyUserDBObjects.Where(u => u.Forename = "Bob"));

你可以这样写:

IQueryable<MyUserDto> myUser =
    context
    .MyUserDBObjects
    .Where(u => u.Forename = "Bob")
    .AsDto();

哪种阅读更容易。将投影分成单独的扩展类还可以让您更轻松地添加其他功能。例如,如果您经常在forename上搜索,可以添加另一个扩展方法,如下所示:

public static IQueryable<MyUserDto> WhereForenameIs(this IQueryable<MyUserDto> source, string name)
{
    return
        source
        .Where(u => u.Forename = name);
}

然后执行此操作:

IQueryable<MyUserDto> myUser =
    context
    .MyUserDBObjects
    .AsDto()
    .WhereForenameIs("Bob");