Entity Framework 基于可空主键列分组

Entity Framework Group by based on a Nullable Primary Key column

我用于存储地址的实体如下所示,并引用多个 table 作为外键,如国家大陆等

public class ContactAddress
{
    [Key]
    public int Id { get; set; }

    [ForeignKey(nameof(Contact))]
    public int ContactId { get; set; }

    public virtual  Contact Contact { get; set; } 
    
    [ForeignKey(nameof(Continent))]
    public int? ContinentId { get; set; }

    public virtual  Continent Continent { get; set; }

    [ForeignKey(nameof(Country))]
    public int? CountryId { get; set; }

    public virtual  Country Country { get; set; }
}

尝试获取国家/地区和大陆地址值的计数,并将结果映射到 List<GeoStatsModel>,类似于

public class GeoStatsModel
{
    public int Id { get; set; } // Country Id
    public string Name { get; set; }  //Country Name
    public int Count { get; set; }
}           

设法生成如下查询

 List<GeoStatsModel> response = new List<GeoStatsModel>();

 response = context.ContactAddresses
                   .Where(a => a.IsPrimary == true && contactIds.Contains(a.ContactId))
                   .GroupBy(a => a.CountryId) 
                   .Select(g => new GeoStatsModel 
                                    {
                                        Id = (int)g.Key, 
                                        Name = (g.First().Country!=null)? g.First().Country.Name: "",
                                        Count = g.Count() 
                                    }).ToList(); 

执行的时候报错:

The LINQ expression 'GroupByShaperExpression: KeySelector: (Nullable)c.CountryId, ElementSelector:EntityShaperExpression: EntityType: ContactAddress ValueBufferExpression: ProjectionBindingExpression: EmptyProjectionMember IsNullable: False

.Select(s => s.Country) .First()' could not be translated. Either rewrite the query in a form that can be translated, or switch to client evaluation explicitly by inserting a call to 'AsEnumerable', 'AsAsyncEnumerable', 'ToList', or 'ToListAsync'. See https://go.microsoft.com/fwlink/?linkid=2101038 for more information.

我们如何才能正确地 return 来自外键 table 的名称并对结果进行分组?

GroupBy 对 EF 核心的支持有限。它基本上支持SQL支持的语句,在EF core 6中多一点。

在 SQL 中,不可能按 CountryId 分组并在结果集中包含国家名称(至少不是在所有 RDBMS 中)。要在 SQL 中执行此操作,该语句应与 Country 连接,然后按 CountryIdCountry.Name 分组。这就是 EF 还支持的内容:

var response = context.ContactAddresses
    .Where(a => a.IsPrimary && contactIds.Contains(a.ContactId))
    .GroupBy(a => new { a.CountryId, a.Country.Name }) 
    .Select(g => new GeoStatsModel 
    {
        Id = g.Key.CountryId ?? 0, 
        Name = g.Key.Name ?? string.Empty,
        Count = g.Count() 
    }).ToList();