Hi, I noticed that the following stack trace was generated (at first) by passing an IEnumerable<T> to EntityTypeBuilder.HasData<T>(params T[] data).
I think we could have a simpler stack trace that at least indicates the error is related to HasData (if it is).
System.Reflection.TargetParameterCountException: Parameter count mismatch.
at System.Reflection.RuntimeMethodInfo.InvokeArgumentsCheck(Object obj, BindingFlags invokeAttr, Binder binder, Object[] parameters, CultureInfo culture)
at System.Reflection.RuntimeMethodInfo.Invoke(Object obj, BindingFlags invokeAttr, Binder binder, Object[] parameters, CultureInfo culture)
at System.Reflection.RuntimePropertyInfo.GetValue(Object obj, Object[] index)
at Microsoft.EntityFrameworkCore.Metadata.Internal.EntityType.GetData(Boolean providerValues)
at Microsoft.EntityFrameworkCore.Infrastructure.ModelValidator.ValidateData(IModel model)
at Microsoft.EntityFrameworkCore.Infrastructure.ModelValidator.Validate(IModel model)
at Microsoft.EntityFrameworkCore.Infrastructure.RelationalModelValidator.Validate(IModel model)
at Microsoft.EntityFrameworkCore.Internal.SqlServerModelValidator.Validate(IModel model)
at Microsoft.EntityFrameworkCore.Infrastructure.ModelSource.CreateModel(DbContext context, IConventionSetBuilder conventionSetBuilder, IModelValidator validator)
at Microsoft.EntityFrameworkCore.Infrastructure.ModelSource.<>c__DisplayClass5_0.<GetModel>b__1()
at System.Lazy`1.ViaFactory(LazyThreadSafetyMode mode)
at System.Lazy`1.ExecutionAndPublication(LazyHelper executionAndPublication, Boolean useDefaultConstructor)
at System.Lazy`1.CreateValue()
at Microsoft.EntityFrameworkCore.Infrastructure.ModelSource.GetModel(DbContext context, IConventionSetBuilder conventionSetBuilder, IModelValidator validator)
at Microsoft.EntityFrameworkCore.Internal.DbContextServices.CreateModel()
at Microsoft.EntityFrameworkCore.Internal.DbContextServices.get_Model()
at Microsoft.EntityFrameworkCore.Infrastructure.EntityFrameworkServicesBuilder.<>c.<TryAddCoreServices>b__7_1(IServiceProvider p)
at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteRuntimeResolver.VisitFactory(FactoryCallSite factoryCallSite, ServiceProviderEngineScope scope)
at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteVisitor`2.VisitCallSite(IServiceCallSite callSite, TArgument argument)
at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteRuntimeResolver.VisitScoped(ScopedCallSite scopedCallSite, ServiceProviderEngineScope scope)
at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteVisitor`2.VisitCallSite(IServiceCallSite callSite, TArgument argument)
at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteRuntimeResolver.VisitConstructor(ConstructorCallSite constructorCallSite, ServiceProviderEngineScope scope)
at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteVisitor`2.VisitCallSite(IServiceCallSite callSite, TArgument argument)
at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteRuntimeResolver.VisitScoped(ScopedCallSite scopedCallSite, ServiceProviderEngineScope scope)
at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteVisitor`2.VisitCallSite(IServiceCallSite callSite, TArgument argument)
at Microsoft.Extensions.DependencyInjection.ServiceLookup.DynamicServiceProviderEngine.<>c__DisplayClass1_0.<RealizeService>b__0(ServiceProviderEngineScope scope)
at Microsoft.Extensions.DependencyInjection.ServiceLookup.ServiceProviderEngine.GetService(Type serviceType, ServiceProviderEngineScope serviceProviderEngineScope)
at Microsoft.Extensions.DependencyInjection.ServiceLookup.ServiceProviderEngineScope.GetService(Type serviceType)
at Microsoft.Extensions.DependencyInjection.ServiceProviderServiceExtensions.GetRequiredService(IServiceProvider provider, Type serviceType)
at Microsoft.Extensions.DependencyInjection.ServiceProviderServiceExtensions.GetRequiredService[T](IServiceProvider provider)
at Microsoft.EntityFrameworkCore.DbContext.get_DbContextDependencies()
at Microsoft.EntityFrameworkCore.DbContext.get_InternalServiceProvider()
at Microsoft.EntityFrameworkCore.DbContext.Microsoft.EntityFrameworkCore.Infrastructure.IInfrastructure<System.IServiceProvider>.get_Instance()
at Microsoft.EntityFrameworkCore.Internal.InternalAccessorExtensions.GetService[TService](IInfrastructure`1 accessor)
at Microsoft.EntityFrameworkCore.Infrastructure.AccessorExtensions.GetService[TService](IInfrastructure`1 accessor)
at Microsoft.EntityFrameworkCore.Design.Internal.DbContextOperations.CreateContext(Func`1 factory)
at Microsoft.EntityFrameworkCore.Design.Internal.DbContextOperations.CreateContext(String contextType)
at Microsoft.EntityFrameworkCore.Design.Internal.MigrationsOperations.UpdateDatabase(String targetMigration, String contextType)
at Microsoft.EntityFrameworkCore.Design.OperationExecutor.UpdateDatabase.<>c__DisplayClass0_1.<.ctor>b__0()
at Microsoft.EntityFrameworkCore.Design.OperationExecutor.OperationBase.Execute(Action action)
Parameter count mismatch.
Update:
Trying to seed the database with json bellow to its respective entity still gives me the error above.
At first, the problem seemed to be related to the type of params passed to HasData, now I'm not so sure anymore. While testing, changing the IEnumerable<T> to array, allowed me to find a problem related to the owned type (described here: https://github.com/aspnet/EntityFrameworkCore/issues/12004); since before transforming the enumerable to array I couldn't even see the proper error message 🤷♂️ .
{
"id": 1,
"name": "Second Avenue",
"description": "Second Avenue..."
}
c#
public class Location : Entity /* 'Entity' has an Id and is abstract */
{
public string Name { get; set; }
public string Description { get; set; }
}
@thiagomajesk What does the code look like where you are passing an IEnumerable<T> to EntityTypeBuilder.HasData<T>(params T[] data)?
@ajcvickers I have a method that reads data from a JSON file using Newtonsoft's Json.NET and returns an IEnumerable<Location> or an IEnumerable<dynamic> (I've tried both) and pipes it to HasData. (I've tested passing both the enumerable and an array).
PS.: There's no much magic behind it; do you think that this amount of information is sufficient?
@thiagomajesk I investigated a bit more and it seems like what is really happening is that IEnumerable<T> is being passed to EntityTypeBuilder.HasData(params object[] data) which makes sense rather than EntityTypeBuilder.HasData<T>(params T[] data) which doesn't.
@ajcvickers Thanks for the attention, it makes sense! I'll test that and report back here.
Btw, is there something we can do to improve a little bit the stack-trace? There's nothing that indicates to me that this problem may be related to HasData and the error message sounds too generic.
@thiagomajesk We will discuss in triage.
@ajcvickers I'm testing simplified possibilities with HasData, and I noticed that (just to catalog):
```c#
// Works as expected
var data = new Location { Id = 1, Name = "Second Avenue" };
modelBuilder.Entity
// Fails complaining about required property 'CreatedOn' -
// I supose because dynamic won't initialize the props with defaults like the object does.
// It'll work if you provide all the properties
var data = new { Id = 1, Name = "Second Avenue" };
modelBuilder.Entity
// Same as above
var data = new List
modelBuilder.Entity
// Does not work, fails with Parameter count mismatch error.
// Requires transforming it to array (as expected)
var data = new List
modelBuilder.Entity
Ok, everything above seems to work the way is supposed too by design. I have to add that while testing I've discovered that the problem is related to `JsonConvert.DeserializeObject`'s result while using dynamic types.
```c#
// This will work
var data = JsonConvert.DeserializeObject<Location>("{ \"id\": 1, \"name\": \"Second Avenue\" }");
modelBuilder.Entity<Location>().HasData(data);
// This too
var data = JsonConvert.DeserializeObject<List<Location>>("[{ \"id\": 1, \"name\": \"Second Avenue\" }]").ToArray();
modelBuilder.Entity<Location>().HasData(data);
// This won't
var data = JsonConvert.DeserializeObject<List<dynamic>>("[{ \"id\": 1, \"name\": \"Second Avenue\" }]").ToArray();
modelBuilder.Entity<Location>().HasData(data);
// Will have the same effect as above
var data = JsonConvert.DeserializeObject<List<object>>("[{ \"id\": 1, \"name\": \"Second Avenue\" }]").ToArray();
modelBuilder.Entity<Location>().HasData(data);
The previous two examples using List<dynamic> and List<object> executes the same way because the inner type of both after deserializing will be Object, so what you said about calling EntityTypeBuilder.HasData(params object[] data) makes total sense.
So, it seems to me that I don't have much of a choice here beyond using DeserializeObject<List<T>>.
The thing is that this way I won't be able to seed the table with its relationships (because of not using dynamic). Do you see any workarounds here that won't compromise me on that?
Triage: We should add an overload for IEnumerable<T> and improve error handling for other cases.
Most helpful comment
@thiagomajesk We will discuss in triage.