LINQ To Entities

时间:2018-05-03 01:27:57

标签: c# sql entity-framework linq linq-to-entities

我有一个看起来像这样的EF类:

public class Item
public string ItemId{ get; set; } 
public string NormalDescription { get; set; } 
public string LongDescription { get; set; } 
public string ShortDescription { get; set; }
.. <snip>

另外,我有一个看起来像这样的DTO:

public class ItemDTO
public string Id { get; set; } 
public string DisplayName { get; set; }
.. <snip>

从“物品”加载数据后进入DTO课程,我需要有条件地设置&#39; DisplayName&#39;基于配置设置。换句话说,我正在寻找类似的东西:

return _repo.GetAsQueryable<Item>()
    .Select(i=> new ItemDTO
    {
        Id = i.ItemId,
        DisplayName = (setting == 1) ? i.NormalDescription :
                      (setting == 2) ? i.LongDescription :
                      (setting == 3) ? i.ShortDescription :
                      String.Empty
    }

当然,这会导致一些非常低效的SQL(使用&#39; CASE&#39;来评估每个可能的值)被发送到数据库。这是一个性能问题,因为项目上的TON描述字段。

话虽如此,有没有办法只选择填充&#39; DisplayName&#39;所需的字段?值?

换句话说,而不是填充&#39; CASE WHEN&#39;逻辑,我只想根据我的应用程序配置设置检索其中一个描述值。

2 个答案:

答案 0 :(得分:1)

这样的东西?

var repo = _repo.GetAsQueryable<Item>();

if (setting == 1)
{
    return repo.Select(i => new ItemDTO
    {
        Id = i.ItemId,
        DisplayName = i.NormalDescription
    });
}
if (setting == 2)
{
    return repo.Select(i => new ItemDTO
    {
        Id = i.ItemId,
        DisplayName = i.LongDescription
    });
}
if (setting == 3)
{
    return repo.Select(i => new ItemDTO
    {
        Id = i.ItemId,
        DisplayName = i.ShortDescription
    });
}
return repo.Select(i => new ItemDTO
{
    Id = i.ItemId,
    DisplayName = String.Empty
});

修改

你可以像Slava Utesinov所展示的那样动态创建表达式。如果您不想构建整个表达式,则可以只替换所需的部分:

public class UniRebinder : ExpressionVisitor
{
    readonly Func<Expression, Expression> replacement;

    UniRebinder(Func<Expression, Expression> replacement)
    {
        this.replacement = replacement;
    }

    public static Expression Replace(Expression exp, Func<Expression, Expression> replacement)
    {
        return new UniRebinder(replacement).Visit(exp);
    }

    public override Expression Visit(Expression p)
    {
        return base.Visit(replacement(p));
    }
}

Expression<Func<Item, ItemDTO>> ReplaceProperty(
    int setting, Expression<Func<Item, ItemDTO>> value)
{
    Func<MemberExpression, Expression> SettingSelector(int ss)
    {
        switch (ss)
        {
            case 1: return x => Expression.MakeMemberAccess(x.Expression, typeof(Item).GetProperty(nameof(Item.NormalDescription)));
            case 2: return x => Expression.MakeMemberAccess(x.Expression, typeof(Item).GetProperty(nameof(Item.LongDescription)));
            case 3: return x => Expression.MakeMemberAccess(x.Expression, typeof(Item).GetProperty(nameof(Item.ShortDescription)));
            default: return x => Expression.Constant(String.Empty);
        }
    }

    return (Expression<Func<Item, ItemDTO>>)UniRebinder.Replace(
        value,
        x =>
        {
            if (x is MemberExpression memberExpr
                && memberExpr.Member.Name == nameof(Item.NormalDescription))
            {
                return SettingSelector(setting)(memberExpr);
            }

            return x;
        });
}


private void Test()
{
    var repo = (new List<Item>() {
        new Item() {
            ItemId ="1",
            LongDescription = "longd1",
            NormalDescription = "normald1",
            ShortDescription = "shortd1" },
        new Item() {
            ItemId ="2",
            LongDescription = "longd2",
            NormalDescription = "normald2",
            ShortDescription = "shortd2" }
    }).AsQueryable();

    for (int selector = 1; selector < 5; ++selector)
    {
        var tst = repo.Select(ReplaceProperty(selector,
            i => new ItemDTO
            {
                Id = i.ItemId,
                DisplayName = i.NormalDescription
            })).ToList();


        Console.WriteLine(selector + ": " + string.Join(", ", tst.Select(x => x.DisplayName)));
        //Output:
        //1: normald1, normald2
        //2: longd1, longd2
        //3: shortd1, shortd2
        //4: , 
    }
}

答案 1 :(得分:1)

您应该动态创建lambda Expression

var typeOfItem = typeof(Item);
var argParam = Expression.Parameter(typeOfItem, "x");
var itemIdProperty = Expression.Property(argParam, "ItemId");

var properties = typeOfItem.GetProperties();
Expression descriptionProperty;
if (setting < properties.Count())            
    descriptionProperty = Expression.Property(argParam, properties[setting].Name);
else
    descriptionProperty = Expression.Constant(string.Empty);

var ItemDTOType = typeof(ItemDTO);

var newInstance = Expression.MemberInit(
    Expression.New(ItemDTOType),
    new List<MemberBinding>()
    {
        Expression.Bind(ItemDTOType.GetMember("Id")[0], itemIdProperty),
        Expression.Bind(ItemDTOType.GetMember("DisplayName")[0], descriptionProperty),
    }
);

var lambda = Expression.Lambda<Func<Item, ItemDTO>>(newInstance, argParam);

return _repo.GetAsQueryable<Item>().Select(lambda);