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

Effort does not load related data after insert #177

Closed dotcom9 closed 5 years ago

dotcom9 commented 5 years ago

Description

When a row is added to a table, it is common to want to refresh the object to pick up auto-generated field values, related objects specified by foreign keys and so on. Effort currently does not support this scenario. Methods of the DbEntityEntry object that is retrieved from DbContext.Entry(object) fail with an InvalidOperationException against an Effort context.

Project

Database context creation:

var connection = EntityConnectionFactory
    .CreateTransient(ConfigurationManager.ConnectionStrings["myEntities"].ConnectionString);
return new MyEntities(connection);

Repository method:

    public async Task<CourseDeclaration> AddCourseDeclaration(int categoryId, 
        long userId, 
        long managerId, 
        int roleId, 
        CourseDeclarationType declarationType, 
        string comment)
    {
        var declaration = new CourseDeclaration
        {
            CategoryId = categoryId,
            UserId = userId,
            ManagerId = managerId,
            RoleId = roleId,
            DeclarationTypeId = (byte)declarationType,
            Comment = comment,
            Date = DateTime.Now
        };
        DbContext.CourseDeclaration.Add(declaration);
        await DbContext.SaveChangesAsync();

        var entry = DbContext.Entry(declaration);
        entry.Reload();
        return entry.Entity;
    }

Unit Test

    [Fact]
    public async void AddCourseDeclaration()
    {
        var courseRepository = BuildCourseRepository();
        var categoryId = 4;
        const long userId = 0;
        const long managerId = 22;
        const int roleId = 3;

        var result = await courseRepository.AddCourseDeclaration(categoryId, userId, managerId, roleId, CourseDeclarationType.Approved, "a comment");

        Assert.NotNull(result);
    }

Exception

The following exception is thrown when entry.Reload() is called in the repository method, above.

System.InvalidOperationException
Member 'Reload' cannot be called for the entity of type 'CourseDeclaration' because the entity does not exist in the context. To add an entity to the context call the Add or Attach method of DbSet<CourseDeclaration>.
   at System.Data.Entity.Internal.InternalEntityEntry.ValidateNotDetachedAndInitializeRelatedEnd(String method)
   at System.Data.Entity.Internal.InternalEntityEntry.ValidateStateToGetValues(String method, EntityState invalidState)
   at System.Data.Entity.Internal.InternalEntityEntry.Reload()
   at Tessello.Services.Core.Course.CourseRepository.<AddCourseDeclaration>d__7.MoveNext() in C:\BITBUCKET\tessello-services\Tessello.Services.Core\Course\CourseRepository.cs:line 78
--- End of stack trace from previous location where exception was thrown ---
   at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
   at System.Runtime.CompilerServices.TaskAwaiter`1.GetResult()
   at Tessello.Services.Tests.Course.CourseRepositoryTests.<AddCourseDeclaration>d__15.MoveNext() in C:\BITBUCKET\tessello-services\Tessello.Services.Tests\Course\CourseRepositoryTests.cs:line 234
--- End of stack trace from previous location where exception was thrown ---
   at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
   at Xunit.Sdk.AsyncTestSyncContext.<>c__DisplayClass7_0.<Post>b__1(Object _)

Further technical details

JonathanMagnan commented 5 years ago

Thank you @dotcom9 for reporting,

We will look if that's possible for us or not to support the method Reload.

Best Regards,

Jonathan


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

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

JonathanMagnan commented 5 years ago

Hello @dotcom9 ,

We tried your code asynchronously and without async and everything seems to work on our side.

using System;
using System.Collections.Generic;
using System.Data.Common;
using System.Data.Entity;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace Effort.Lab.EF6
{
    public class Request_Reload
    {
        public static void Execute()
        {
            var connection = Effort.DbConnectionFactory.CreateTransient();

            using (var context = new EntityContext(connection))
            {
                var entitySimple = new EntitySimple {ColumnInt = 1};
                context.EntitySimples.Add(entitySimple);
                context.EntitySimples.Add(new EntitySimple { ColumnInt = 2 });
                context.EntitySimples.Add(new EntitySimple { ColumnInt = 3 });
                context.SaveChanges();

                var entry = context.Entry(entitySimple);
                entry.Reload();
            }
        }

        public class EntityContext : DbContext
        {
            public EntityContext(DbConnection connection) : base(connection, true)
            {
            }

            public DbSet<EntitySimple> EntitySimples { get; set; }      }

        public class EntitySimple
        {
            public int ID { get; set; }
            public int ColumnInt { get; set; }
        }
    }
}

Could you provide a full example as we did with the issue?

Best Regards,

Jonathan