zzzprojects / EntityFramework-Effort

Entity Framework Effort is a powerful tool that enables a convenient way to create automated tests for Entity Framework based applications.
https://entityframework-effort.net/
MIT License
431 stars 99 forks source link

Object of type 'a' cannot be converted to type 'b' on Effort.Internal.Common.DatabaseReflectionHelper.InsertEntity #212

Closed hello-consumer closed 3 years ago

hello-consumer commented 3 years ago

I'm using .NET Framework 4.7.1, MSTest 2.2.5, and Effort.EF6 2.2.14

In my MSTest Code, I'm using a TestInitialize method to seed the table with some data. For this post, I'm going to oversimplify what's happening by only including two entities, but in practice, I have closer to 30 tables with a handful (between 2 and 10) of records each. I'm calling one SaveChanges operation at the end which probably queues up a whole bunch of inserts for Effort to chew on at once.

[TestInitialize]
public override void SetUp()
{
    connection = Effort.DbConnectionFactory.CreateTransient();
    dbContext = new MyDB(connection, true);

    dbContext.Foos.AddRange(new List<Foo>{
        new Foo { Id = 1 },
        new Foo { Id = 2 },
        //...
    });

    dbContext.Bars.AddRange(new List<Bar>{
        new Bar { Id = 1 },
        new Bar { Id = 2 },
        //...
    });

    dbContext.SaveChanges();
}

Test execution works great 99.9% of the time, but every so often, it seems like we get a weird hiccup on the SaveChanges operation where the data being added and the table are mismatched. It almost always occurs when we're first "seeding" data in the database.

Exception

Initialization method Tests.SetUp threw exception. System.Data.Entity.Infrastructure.DbUpdateException: An error occurred while updating the entries. See the inner exception for details. ---> System.Data.Entity.Core.UpdateException: An error occurred while updating the entries. See the inner exception for details. ---> System.ArgumentException: Object of type 'dbo_Foo' cannot be converted to type 'dbo_Bar'..

Stack Trace

 at System.RuntimeType.TryChangeType(Object value, Binder binder, CultureInfo culture, Boolean needsSpecialCast)
   at System.RuntimeType.CheckValue(Object value, Binder binder, CultureInfo culture, BindingFlags invokeAttr)
   at System.Reflection.MethodBase.CheckArguments(Object[] parameters, Binder binder, BindingFlags invokeAttr, CultureInfo culture, Signature sig)
   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.MethodBase.Invoke(Object obj, Object[] parameters)
   at Effort.Internal.Common.DatabaseReflectionHelper.InsertEntity(ITable table, Object entity, Transaction transaction)
   at Effort.Internal.CommandActions.InsertCommandAction.CreateAndInsertEntity(ITable table, IList`1 memberBindings, Transaction transaction)
   at Effort.Internal.CommandActions.InsertCommandAction.ExecuteNonQuery(ActionContext context)
   at Effort.Provider.EffortEntityCommand.ExecuteNonQuery()
   at System.Data.Entity.Infrastructure.Interception.DbCommandDispatcher.<NonQuery>b__0(DbCommand t, DbCommandInterceptionContext`1 c)
   at System.Data.Entity.Infrastructure.Interception.InternalDispatcher`1.Dispatch[TTarget,TInterceptionContext,TResult](TTarget target, Func`3 operation, TInterceptionContext interceptionContext, Action`3 executing, Action`3 executed)
   at System.Data.Entity.Infrastructure.Interception.DbCommandDispatcher.NonQuery(DbCommand command, DbCommandInterceptionContext interceptionContext)
   at System.Data.Entity.Internal.InterceptableDbCommand.ExecuteNonQuery()
   at System.Data.Entity.Core.Mapping.Update.Internal.DynamicUpdateCommand.Execute(Dictionary`2 identifierValues, List`1 generatedValues)
   at System.Data.Entity.Core.Mapping.Update.Internal.UpdateTranslator.Update()
--- End of inner exception stack trace ---
    at System.Data.Entity.Core.Mapping.Update.Internal.UpdateTranslator.Update()
   at System.Data.Entity.Core.EntityClient.Internal.EntityAdapter.<Update>b__2(UpdateTranslator ut)
   at System.Data.Entity.Core.EntityClient.Internal.EntityAdapter.Update[T](T noChangesResult, Func`2 updateFunction)
   at System.Data.Entity.Core.EntityClient.Internal.EntityAdapter.Update()
   at System.Data.Entity.Core.Objects.ObjectContext.<SaveChangesToStore>b__35()
   at System.Data.Entity.Core.Objects.ObjectContext.ExecuteInTransaction[T](Func`1 func, IDbExecutionStrategy executionStrategy, Boolean startLocalTransaction, Boolean releaseConnectionOnSuccess)
   at System.Data.Entity.Core.Objects.ObjectContext.SaveChangesToStore(SaveOptions options, IDbExecutionStrategy executionStrategy, Boolean startLocalTransaction)
   at System.Data.Entity.Core.Objects.ObjectContext.<>c__DisplayClass2a.<SaveChangesInternal>b__27()
   at System.Data.Entity.Infrastructure.DefaultExecutionStrategy.Execute[TResult](Func`1 operation)
   at System.Data.Entity.Core.Objects.ObjectContext.SaveChangesInternal(SaveOptions options, Boolean executeInExistingTransaction)
   at System.Data.Entity.Core.Objects.ObjectContext.SaveChanges(SaveOptions options)
   at System.Data.Entity.Internal.InternalContext.SaveChanges()
--- End of inner exception stack trace ---
    at System.Data.Entity.Internal.InternalContext.SaveChanges()
   at System.Data.Entity.Internal.LazyInternalContext.SaveChanges()
   at System.Data.Entity.DbContext.SaveChanges()
   at Example.MyDB.SaveChanges()
   at Tests.SetUp()

This might be an EF issue, but as you can see from the stack trace, it seems like Effort's DatabaseReflectionHelper is the more likely culprit.

JonathanMagnan commented 3 years ago

Hello @hello-consumer ,

Do you think you could provide a runnable project sample for this issue? It will help my developer to get started to investigate it faster and make sure nothing is missing.

Even if you provided already a lot of information in your message Joe, it sometimes takes us hours to just try to reproduce the issue as something is often missing. For obvious reasons, we prefer to pass this time to fix the issues instead to try to reproduce it.

Providing a project sample is now REQUIRED. It happened too many times that something was missing to investigate and/or answer an issue.

Try to create a new project with only the minimal code (having too many non-related codes doesn’t help either).

You can send it to info@zzzprojects.com if you need to keep the source private

Best Regards,

Jon


Sponsorship Help us improve this library

Performance Libraries context.BulkInsert(list, options => options.BatchSize = 1000); Entity Framework ExtensionsBulk OperationsDapper Plus

Runtime Evaluation Eval.Execute("x + y", new {x = 1, y = 2}); // return 3 C# Eval FunctionSQL Eval Function

hello-consumer commented 3 years ago

I really appreciate the prompt response!

The current codebase I'm working off of has some proprietary stuff in it that I need to avoid posting online, but I'll see if I can adapt the provided snippet into a runnable sample and zip if up here. This totally makes sense, there are a handful of other runtime configuration data points that might be creating this so I'll try to get some code reproducing this with the smallest possible footprint for you to run.

I'll follow up with a zip shortly.

hello-consumer commented 3 years ago

https://github.com/hello-consumer/ef6-effort-bug

Got it to successfully reproduce in this project. In addition to what's here, I'll note that I'm running Microsoft Visual Studio Community 2019 Version 16.11.2 today.

Just clone this, open it up, build, and run the unit tests from inside of visual studio. On my environment, this test suite takes about ~3 minutes, and even then, the test suite might succeed- it failed 1 time out of 900 attempts on my local machine. Give it a few runs, though and I'll wager you'll see the issue pop up:

image
JonathanMagnan commented 3 years ago

Hello @hello-consumer ,

Thank you for the project, this has been assigned to my developer.

Best Regards,

Jon

JonathanMagnan commented 3 years ago

Hello @hello-consumer ,

We finally have found out the issue.

The v2.2.15 has been released with this issue fixed.

Make sure those test doesn't run in concurrency as the way they have been created will keep overriding the connection. However, the main issue (the one reported here) should be fixed.

Let me know if everything is now working as expected.

Best Regards,

Jon

hello-consumer commented 3 years ago

That's great news, @JonathanMagnan

I'll take a look at this ASAP and let you know if I see anything else but I'm sure this'll fix it.

Thanks for the prompt turnaround, that's really impressive.