如何在 EF 中获取与组合列表(键/值)匹配的记录?

问题描述 投票:0回答:3

我有一个数据库表,其中包含每个用户/年份组合的记录。

如何使用 EF 和 userId/year 组合列表从数据库中获取数据? 样本组合

UserId      Year
1           2015
1           2016 
1           2018
12          2016
12          2019
3           2015
91          1999

我只需要上面组合中定义的记录。不知道如何使用 EF/Linq 编写此代码?

 List<UserYearCombination> userYears =  GetApprovedYears();

 var records = dbcontext.YearResults.Where(?????);

课程

public class YearResult
{
    public int UserId;
    public int Year;
    public DateTime CreatedOn;
    public int StatusId;
    public double Production;
    public double Area;
    public double Fte;
    public double Revenue;
    public double Diesel;
    public double EmissionsCo2;
    public double EmissionInTonsN;
    public double EmissionInTonsP;
    public double EmissionInTonsA;
        ....
}

public class UserYearCombination
{
    public int UserId;
    public int Year;
}
c# entity-framework linq
3个回答
4
投票

这是我之前讨论过的臭名昭著的问题here。 Krishna Muppalla 的解决方案是我在那里提出的解决方案之一。它的缺点是它不可搜索,即它不能从涉及的数据库字段上的任何索引中受益。

与此同时,我创造了另一个在某些情况下可能有用的解决方案。基本上它按字段之一对输入数据进行分组,然后通过分组键和包含组元素的查询来查找和联合数据库数据:

IQueryable<YearResult> items = null;

foreach (var yearUserIds in userYears.GroupBy(t => t.Year, t => t.UserId))
{
    var userIds = yearUserIds.ToList();
    var grp = dbcontext.YearResults
        .Where(x => x.Year == yearUserIds.Key 
                 && userIds.Contains(x.UserId));
    items = items == null ? grp : items.Concat(grp);
}

我在这里使用

Concat
是因为
Union
会浪费时间使结果不同,并且在 EF6 中
Concat
将生成带有链式
UNION
语句的 SQL,而
Union
生成嵌套的
UNION
语句并且可能会达到最大嵌套级别.

当索引就位时,此查询可能会执行得很好。理论上,一条 SQL 语句中

UNION
的最大数目是无限的,但是
IN
子句(即
Contains
翻译成)中的项数不应超过几千个。这意味着 您的数据内容将决定哪个分组字段表现更好,
Year
UserId
。挑战在于最小化
UNION
的数量,同时将所有
IN
子句中的项目数量保持在大约以下。 5000.


1
投票

你可以试试这个

//add the possible filters to LIST
var searchIds = new List<string> { "1-2015", "1-2016", "2-2018" };

//use the list to check in Where clause
var result = (from x in YearResults
        where searchIds.Contains(x.UserId.ToString()+'-'+x.Year.ToString())
        select new UserYearCombination
        {
            UserId = x.UserId,
            Year = x.Year
        }).ToList();

方法二

var d = YearResults
           .Where(x=>searchIds.Contains(x.UserId.ToString() + '-' + x.Year.ToString()))
           .Select(x => new UserYearCombination
                 {
                      UserId = x.UserId,
                      Year = x.Year
                 }).ToList();

0
投票

我一直在研究解决方案。请注意,这是基于我对 C# 表达式的有限了解,如果您有任何改进建议,我将不胜感激。

public static class EfExtensions
{
    public static IQueryable<T> WhereCompoundIn<T, TKey>(this IQueryable<T> source, IEnumerable<TKey> keys,
        Expression<Func<T, TKey>> keySelectorExpression)
    {
        var keyExpressions = GetPropertyExpressions(keySelectorExpression)
            .ToDictionary(x => x.Member.Name);

        // get the properties and build a selector expression for each property
        var propertyKeySelectors = typeof(TKey)
            .GetProperties()
            .Select(propertyInfo =>
            {
                var parameter = Expression.Parameter(typeof(TKey));
                var property = Expression.Property(parameter, propertyInfo);
                var conversion = Expression.Convert(property, typeof(object));
                return new
                {
                    PropertyName = propertyInfo.Name,
                    ValueSelector = Expression.Lambda<Func<TKey, object>>(conversion, parameter).Compile()
                };
            });

        var predicate = keys
            .Select(compoundKey =>
                {
                    var andExpressions = propertyKeySelectors
                        .Select(key =>
                        {
                            var keyValue = key.ValueSelector(compoundKey);
                            var propertySelectorExpression = keyExpressions[key.PropertyName];
                            // T.Property == keyValue
                            return Expression.Equal(propertySelectorExpression, Expression.Constant(keyValue));
                        })
                        // T.Property1 == keyValue1 && T.Property2 == keyValue2 && ...
                        .Aggregate(Expression.AndAlso);
                    return andExpressions;
                }
            )
            // T.Property1 == keyValue1 && T.Property2 == keyValue2 && ... || T.Property1 == keyValue1 && T.Property2 == keyValue2 && ...
            .Aggregate(Expression.OrElse);
        return source.Where(Expression.Lambda<Func<T, bool>>(predicate, keySelectorExpression.Parameters));
    }

    private static IEnumerable<MemberExpression> GetPropertyExpressions<T, TResult>(
        this Expression<Func<T, TResult>> expression)
    {
        if (expression.Body is not NewExpression newExpression)
            throw new ArgumentException("Expression must be a NewExpression", nameof(expression));

        foreach (var argumentExpression in newExpression.Arguments)
        {
            if (argumentExpression is not MemberExpression { Expression: not null } memberExpression) continue;
            var memberName = memberExpression.Member.Name;
            yield return Expression.Property(memberExpression.Expression, memberName);
        }
    }
}

可以如下使用:

var compoundKeys =
    "2480209000000469302,2480209000000469347,2480209000000469374,2480209000000470068"
        .Split(',')
        .Select(productId => new { ProductId = productId, StoreId = "MGA_SUR" })
        .ToArray();

var productStocks = context.ProductStocks
    .Where(x => x.BusinessId == "ZUPER")
    .WhereCompoundIn(compoundKeys, x => new { x.ProductId, x.StoreId })
    .ToArray();

上面的查询生成如下SQL代码:

SELECT `p`.`business_id`,
       `p`.`store_id`,
       `p`.`product_id`,
       `p`.`created_by`,
       `p`.`created_on`,
       `p`.`is_active`,
       `p`.`last_updated_by`,
       `p`.`last_updated_on`,
       `p`.`min_stock`,
       `p`.`purchase_price`,
       `p`.`sales_category`,
       `p`.`sales_price`,
       `p`.`stock`
FROM `product_stocks` AS `p`
WHERE (`p`.`business_id` = 'ZUPER')
  AND (((((`p`.`product_id` = '2480209000000469302') AND (`p`.`store_id` = 'MGA_SUR')) OR
         ((`p`.`product_id` = '2480209000000469347') AND (`p`.`store_id` = 'MGA_SUR'))) OR
        ((`p`.`product_id` = '2480209000000469374') AND (`p`.`store_id` = 'MGA_SUR'))) OR
       ((`p`.`product_id` = '2480209000000470068') AND (`p`.`store_id` = 'MGA_SUR')))
© www.soinside.com 2019 - 2024. All rights reserved.