First off, I would probably still class myself as a novice in EF. Done some code first migrations but only simple things.
Anyway, I have a WPF app I made which is using EF 6. I am now having a look at .Net Core 3 and decided to have a go at converting the app.
Generating the database models I used scaffolding.
Ok so the problem. In the current app running EF6 this code works fine.
var items2 = DbContext.UutResult.Where(x =>
x.StartDateTime != null && (x.StartDateTime.Value.Date >= startDate &&
x.StartDateTime.Value.Date <= endDate &&
x.StationId == testerId)).Select(x => new TestResultItem()
{
TesterId = (int) x.TestSocketIndex,
TesterType = x.StationId,
TestDateTime = (DateTime) x.StartDateTime,
TestResult = x.UutStatus,
PanelBarcode = x.BatchSerialNumber,
UutBarcode = x.UutStatus,
TestTimeSec = (double) x.ExecutionTime,
TestSteps = x.StepResult.Where(y => y.StepGroup == "Main").Select(y => new TestResultStepItem()
{
StepName = y.StepName,
Report = y.ReportText,
TestResult = y.Status,
UpperLimit = y.StepNumericlimit1.FirstOrDefault().StepNumericlimit2.FirstOrDefault().HighLimit.ToString(),
OrderId = (int)y.OrderNumber
}).ToList()
}).ToList();
But running the same in EF core I get the following error.
System.ArgumentNullException: 'Value cannot be null. Parameter name: source'
Stack Trace
at System.Linq.ThrowHelper.ThrowArgumentNullException(ExceptionArgument argument) at System.Linq.Enumerable.TryGetFirst[TSource](IEnumerable
1 source, Boolean& found) at lambda_method(Closure , QueryContext , TransparentIdentifier
2 ) at Microsoft.EntityFrameworkCore.Query.ExpressionVisitors.Internal.ProjectionShaper.TypedProjectionShaper3.Shape(QueryContext queryContext, ValueBuffer& valueBuffer) at Microsoft.EntityFrameworkCore.Query.ExpressionVisitors.Internal.ProjectionShaper.TypedProjectionShaper
3.Microsoft.EntityFrameworkCore.Query.ExpressionVisitors.Internal.IShaper.Shape(QueryContext queryContext, ValueBuffer& valueBuffer) at Microsoft.EntityFrameworkCore.Query.Internal.QueryingEnumerable1.Enumerator.BufferlessMoveNext(DbContext _, Boolean buffer) at Microsoft.EntityFrameworkCore.SqlServer.Storage.Internal.SqlServerExecutionStrategy.Execute[TState,TResult](TState state, Func
3 operation, Func3 verifySucceeded) at Microsoft.EntityFrameworkCore.Query.Internal.QueryingEnumerable
1.Enumerator.MoveNext() at Microsoft.EntityFrameworkCore.Query.Internal.QueryBuffer.CorrelateSubquery[TInner,TOut,TCollection](Int32 correlatedCollectionId, INavigation navigation, Func2 resultCollectionFactory, MaterializedAnonymousObject& outerKey, Boolean tracking, Func
1 correlatedCollectionFactory, Func3 correlationPredicate) at lambda_method(Closure , ValueBuffer ) at System.Linq.Enumerable.SelectEnumerableIterator
2.MoveNext() at Microsoft.EntityFrameworkCore.Query.Internal.LinqOperatorProvider.ExceptionInterceptor1.EnumeratorExceptionInterceptor.MoveNext() at System.Collections.Generic.List
1..ctor(IEnumerable1 collection) at System.Linq.Enumerable.ToList[TSource](IEnumerable
1 source) at EPQT.ServiceLayer.TesterService.GetTestData(DateTime startDate, DateTime endDate, String testerId, String productFilter) in C:\Visual Studio 2017\Projects\Extractor\Core\EPQT.ServiceLayer\TesterService.cs:line 120 at EPQT.Modules.TesterQuery.Views.TestQueryViewModel.<>c__DisplayClass54_0.b__0() in C:\Visual Studio 2017\Projects\Extractor\Modules\EPQT.Modules.TesterQuery\Views\TestQueryViewModel.cs:line 218 at System.Threading.Tasks.Task.InnerInvoke() at System.Threading.Tasks.Task.<>c.<.cctor>b__277_0(Object obj) at System.Threading.ExecutionContext.RunFromThreadPoolDispatchLoop(Thread threadPoolThread, ExecutionContext executionContext, ContextCallback callback, Object state)
So I narrowed down the issue to this line.
UpperLimit = y.StepNumericlimit1.FirstOrDefault().StepNumericlimit2.FirstOrDefault().HighLimit.ToString(),
So y
potentially has many children called StepNumericlimit1
and that potentially has many children called StepNumericlimit2
.
Basically I am trying to say if y
has any StepNumericlimit1
children then grab the first one. Then, if there is a child found, check to see if that has any StepNumericlimit2
children. If it does, then grab the property Data
from the first child.
Not sure if it's how EF core handles this or the way it has genereated the code or maybe even my query it written poorly... or maybe all the above lol
UPDATE: Some EF generated code, deleted irrelevant code.
public partial class StepResult
{
public StepResult()
{
StepNumericlimit1 = new HashSet<StepNumericlimit1>();
}
public Guid Id { get; set; }
public Guid? UutResult { get; set; }
public virtual UutResult UutResultNavigation { get; set; }
public virtual ICollection<StepNumericlimit1> StepNumericlimit1 { get; set; }
}
public partial class StepNumericlimit1
{
public StepNumericlimit1()
{
StepNumericlimit2 = new HashSet<StepNumericlimit2>();
}
public Guid Id { get; set; }
public Guid? StepResult { get; set; }
public double? Data { get; set; }
public virtual StepResult StepResultNavigation { get; set; }
public virtual ICollection<StepNumericlimit2> StepNumericlimit2 { get; set; }
}
public partial class StepNumericlimit2
{
public Guid? PropResult { get; set; }
public double? HighLimit { get; set; }
public double? LowLimit { get; set; }
public virtual StepNumericlimit1 PropResultNavigation { get; set; }
}
OnModelCreating
modelBuilder.Entity<StepNumericlimit1>(entity =>
{
entity.ToTable("STEP_NUMERICLIMIT1");
entity.HasIndex(e => e.StepResult)
.HasName("StepResultIndex");
entity.Property(e => e.StepResult).HasColumnName("STEP_RESULT");
entity.HasOne(d => d.StepResultNavigation)
.WithMany(p => p.StepNumericlimit1)
.HasForeignKey(d => d.StepResult)
.OnDelete(DeleteBehavior.Cascade)
.HasConstraintName("STEP_NUMERICLIMIT1_STEP_RESULT_FK");
});
modelBuilder.Entity<StepNumericlimit2>(entity =>
{
entity.ToTable("STEP_NUMERICLIMIT2");
entity.Property(e => e.HighLimit).HasColumnName("HIGH_LIMIT");
entity.Property(e => e.LowLimit).HasColumnName("LOW_LIMIT");
entity.HasOne(d => d.PropResultNavigation)
.WithMany(p => p.StepNumericlimit2)
.HasForeignKey(d => d.PropResult)
.OnDelete(DeleteBehavior.Cascade)
.HasConstraintName("STEP_NUMERICLIMIT2_STEP_NUMERICLIMIT1_FK");
});
UPDATE 2: So in order to provide more information I was aiming to create a new project and database with dummy data, but very minamal i.e. stripping out tables not required and columns not related to the query.
I did this and now the query works fine, even with NULL child properties. I have checked the generated code from scafolding in both projects and they are the same.
So how can I find what the issue is?