I have the following LINQ statement:
x
var repoActivityRowsTest = appManager.GetRepository<ActivityRow>();
var activityRowsTest = await repoActivityRowsTest.Search(f => f.ExcelReport.uploadPhase == RPToolConstants.Phase_Planning, includeProperties: "PlanningInfo")
.Where(f => iso3Alpha3List.Contains(f.ExcelReport.countryOfficeIso3Alpha3))
.SelectMany(sm => sm.PlanningInfo).Select(s => new { s.Year, s.Count, s.ActivityRow.UnitCost })
.GroupBy(g=>new { g.Year }).Select(sg=>new { sg.Key.Year, Total = sg.Sum(sum => sum.UnitCost * sum.Count) })
.ToListAsync();
Which uses the repository pattern. The search function is the one below:
public IQueryable<TEntity> Search(Expression<Func<TEntity, bool>> filter = null,
string includeProperties = "", bool trackChanges = false)
{
IQueryable<TEntity> query = context.Set<TEntity>();
if (filter != null)
{
query = query.Where(filter);
}
foreach (var includeProperty in includeProperties.Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries))
{
query = query.Include(includeProperty.Trim());
}
if (!trackChanges)
{
query = query.AsNoTracking();
}
return query;
}
When I inspect the command that arrives in SQL Server I see that the query is translated in the following SQL:
SELECT [a0].[Year], SUM([a1].[UnitCost] * CAST([a0].[Count] AS decimal(18,2))) AS [Total]
FROM [ActivityRows] AS [a]
INNER JOIN [ExcelReports] AS [e] ON [a].[ExcelReportId] = [e].[Id]
INNER JOIN [ActivityRowPlanningInfo] AS [a0] ON [a].[Id] = [a0].[ActivityRowId]
INNER JOIN [ActivityRows] AS [a1] ON [a0].[ActivityRowId] = [a1].[Id]
WHERE ([e].[uploadPhase] = N'planning')
AND [e].[countryOfficeIso3Alpha3] IN (N'AFG', N'DZA', N'AGO', N'ARM', N'BGD')
GROUP BY [a0].[Year]
It works perfectly, but why there is an inner join duplicated:
INNER JOIN [ActivityRows] AS [a1] ON [a0].[ActivityRowId] = [a1].[Id]
is a non-sense to me!
If I remove it from the SQL it works as before. Is there any issue in my LINQ query that causes this strange SQL?
here is the definition of the entities:
public class ActivityRow : Entity<int>
{
public string Description { get; set; }
public int ExcelReportId { get; set; }
[ForeignKey("ExcelReportId")]
public virtual ExcelReport ExcelReport { get; set; }
public int ActivitySubTypeId { get; set; }
[ForeignKey("ActivitySubTypeId")]
public virtual ActivitySubType ActivitySubType { get; set; }
public int? ActivityCategoryId { get; set; }
[ForeignKey("ActivityCategoryId")]
public virtual ActivityCategory ActivityCategory { get; set; }
public string ResponsibleEntity { get; set; }
[Column(TypeName = "decimal(18,2)")]
public decimal UnitCost { get; set; }
public string Notes { get; set; }
public virtual ICollection<ActivityRowReportingInfo> ReportingInfo { get; set; }
public virtual ICollection<ActivityRowPlanningInfo> PlanningInfo { get; set; }
}
public class ActivityRowPlanningInfo : Entity<int>
{
public int ActivityRowId { get; set; }
[ForeignKey("ActivityRowId")]
public virtual ActivityRow ActivityRow { get; set; }
public int Year { get; set; }
public int Quarter { get; set; }
public int Count { get; set; }
}
and here the definition of the relationships with fluent API:
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
//activities
modelBuilder.Entity<ActivityRow>()
.HasMany(b => b.ReportingInfo)
.WithOne(t => t.ActivityRow)
.OnDelete(DeleteBehavior.Cascade);
modelBuilder.Entity<ActivityRow>()
.HasMany(b => b.PlanningInfo)
.WithOne(t => t.ActivityRow)
.OnDelete(DeleteBehavior.Cascade);
etc.
}
Advertisement
Answer
Rewrite query via LINQ Query syntax and you can simplify your query with ease. The following query do not create non wanted joins:
var repoActivityRowsTest = appManager.GetRepository<ActivityRow>();
var activityRows = repoActivityRowsTest
.Search(f => true);
var resultQuery =
from ar in activityRows
where
ar.ExcelReport.uploadPhase == RPToolConstants.Phase_Planning
&& iso3Alpha3List.Contains(ar.ExcelReport.countryOfficeIso3Alpha3)
from pi in ar.PlanningInfo
group new { ar, pi } by new { pi.Year } into g
select new
{
g.Key.Year,
Total = g.Sum(x => x.ar.UnitCost * x.pi.Count)
};
var result = await resultQuery.ToListAsync();