对IQueryable的EF区分不会删除重复项

Aun*_*Aun 5 c# linq entity-framework

我有一个泛型类,应该IQueryable通过调用此方法来过滤,排序,投影和分页:

public async Task<PagedResult<TResult>> GetFilteredOrderedPageAsync<TResult>(IQueryable<TSource> source,
            IFilterModel filterModel,
            ISortModel sortModel,
            int page, int pageSize,
            Expression<Func<TSource, TResult>> converter)
            where TResult : class 
        {
            var filtered = Filter(source, filterModel);

            var projected = filtered
                .Select(converter)
                .Distinct();

            var ordered = Sort<TResult>(projected, sortModel);

            var result = await GetPageAsync<TResult>(ordered, page, pageSize, converter);

            return result;
        }
Run Code Online (Sandbox Code Playgroud)

我在Distinct()这里打电话

        var projected = filtered
            .Select(converter)
            .Distinct();
Run Code Online (Sandbox Code Playgroud)

删除投影后可能出现的任何重复项.

我的假设是EF6会产生类似的东西

SELECT DISTINCT Col1, Col2, Col3 FROM (SELECT Col1, Col2, Col3, Col4 FROM SOME_TABLE WHERE <some conditions on co1, col2, col3, col4 ...>)
Run Code Online (Sandbox Code Playgroud)

即它将应用于DISTINCT投影,因此,如果有两行具有相同的col1,col2,col3但不同的col4,则只有一行会使其成为结果.但是,我得到的SQL看起来像这样:

SELECT Col1, Col2, Col3, Col4 FROM (SELECT DISTINCT Col1, Col2, Col3, Col4 FROM SOME_TABLE)
Run Code Online (Sandbox Code Playgroud)

- 没有投影,DISTINCT被转移到子查询中,好像我这样做:

var projected = filtered                    
                .Distinct()
                .Select(converter) 
Run Code Online (Sandbox Code Playgroud)

我希望这项服务是通用的,即可能与任何TSource和服务一起使用TResult,但看起来这里存在一些陷阱,而我对EF所做的事情的理解是不正确的.

这里发生了什么?

UPDATE

我相信问题出在我的转换器上.我使用以下函数生成传递给的lambda表达式Select:

public class ProvidersViewModel
{
    public string Name { get; set; }
    public Rate Rate { get; set; } 
    publi QA QA { get; set; }
    ...
    public static Expression<Func<ProviderJoinRateAndQA, ProvidersViewModel>> FromProvider(bool showRateAndQA) 
    {
       return x => new ProvidersViewModel {
        Name = x.Name, 
        Rate = showRateAndQA ? new Rate { Amount = x.Rate.Amount ... } : null,
        Rate = showRateAndQA ? new QA { Grade = x.QA.Grade ... } : null
    };
}
Run Code Online (Sandbox Code Playgroud)

}

ProviderJoinRateAndQA是提供商,他们的费率和QAs的连接.每个都Provider可以有多种服务,费率和qas.在某些情况下,我希望视图模型隐藏速率和QA信息.我假设我可以通过分配一个nullto RateQA属性来做到这一点,但它似乎不起作用: Distinct工作不正确.在我更换
Rate = showRateAndQA之后?新QA {Grade = x.QA.Grade ...}:null,Rate = new QA {Grade = showRateAndQA?x.QA.Grade:null ...},

Distinct 工作正常.

显然,EF并不喜欢我null在lambda中分配对象.

Tre*_*ent 1

根据记忆,默认Distinct()实现是通过使用相应类的默认Equals()方法来工作的,这通常是不需要的(我发现至少我的工作就是这种情况)。

您需要设置如下内容;

public class ProvidersViewModel : IEqualityComparer<ProvidersViewModel>
{
    public bool Equals(ProvidersViewModel x, ProvidersViewModel y)
    {
        if (x.col1 == y.col1 && x.col2 == y.col2 && x.col3 == y.col3)
            return true;
        else
            return false;
    }

    public int GetHashCode(ProvidersViewModel obj)
    {
        int hCode = obj.col1 ^ obj.col2 ^ obj.col3;
        return hCode.GetHashCode();
    }

    // Existing code, fields, etc.
}
Run Code Online (Sandbox Code Playgroud)

这样就可以让调用Distinct()正常工作。