rgvlee/EntityFrameworkCore.Testing

Data auto deleted once read and does not persist on the ServiceProvider

Closed this issue · 7 comments

I have created the mocked db context for dependency injection, the way you have suggested previously. I found a weird issue though. I am able to successful add read only data for a query, but once it is read for the first time the data is gone.
I also noticed even if I have not read the data yet, when I grab the db context from the service provider again after the data is generated, it is also all gone. Has anyone else seen this before? Is this intentional by design?

Data generator method that accepts the service provider after the mockedDbContext is added

        public static void Initialize(IServiceProvider serviceProvider)
        {

            try
            {
                using (var scope = serviceProvider.CreateScope())
                {

                    var context = serviceProvider.GetRequiredService<MainDBContext>();

                    
                    context.Set<App_Setting_View>().AddRangeToReadOnlySource(new List<App_Setting_View>
                        {
                            new App_Setting_View
                            {
                                Web_Service = "WS",
                                Setting_Type = "GENERAL",
                                Setting_Name = "CacheHours",
                                Setting_Value = "12"
                            },
                            new App_Setting_View
                            {
                                Web_Service = "WS",
                                Setting_Type = "GENERAL",
                                Setting_Name = "Name1",
                                Setting_Value = "3"
                            },
                            new App_Setting_View
                            {
                                Web_Service = "WS",
                                Setting_Type = "GENERAL",
                                Setting_Name = "Name2",
                                Setting_Value = "4"
                            }
                        }
                    );

                    context.SaveChanges();
                   // Data does exist here, 
                    var firstRead = context.App_Setting_View.ToList();
                    // Data does NOT exist here
                    var secondRead = context.App_Setting_View.ToList();
                }
            }
            catch (Exception ex)
            {
                Debug.WriteLine("Exception caught while building DB: " + ex.Message);
            }
        }

Also in this test the data is never there, regardless if it has been read already or not.

   [TestMethod]
        public void GetAllAppSettingsPositiveTest()
        {
            
            IAppSettingService appSettingService = ServiceProvider.GetService<IAppSettingService>();
            var dbContext = ServiceProvider.GetService<MainDBContext>();
            // No data found here
            var test = dbContext.App_Setting_View.ToList();
            IEnumerable<AppSetting> appSettings = appSettingService.GetAllAppSettings();
            Assert.IsTrue(appSettings.Any());
        }

I am creating the mocked db context like this

Services.AddTransient(sp =>
                Create.MockedDbContextFor<MainDBContext>(
                    new DbContextOptionsBuilder<MainDBContext>().UseInMemoryDatabase(databaseName: "UnitTestDb").Options));

No, there is no by design logic to auto delete data in a set.

I did encounter issues in an earlier version where the enumerator would need to be reset after enumerating over a sequence. I believe that was resolved and is covered in the tests which execute the same query/command twice.

For anyone playing at home, the library isn't intended to be used in this manner, but let's see how far we can take it.

The first thing that springs to mind is scope. A list created in one scope may not be available in another scope. One thing I would immediately change would be to initialize the list outside of a scope.

I'll attempt to repo it and advise further.

Thank you for the response. I did remove the

using (var scope = serviceProvider.CreateScope())

line in the code but still had the same issue.
Another note, if I put a breakpoint before the first read, and use the immediate window to pull the data, or even mouse over the context to read the data, it is immediately gone afterwards too. I'm going to look for those tests you mentioned to reset the enumerator.

This is looking like a transient scope issue. Using the mocked db context as a singleton works as expected.

I was able to modify the singleton example, change the set to a query and repeatedly query the repo and got a result every time. I did find a bug with Find and read only sets (which I will raise an issue for), however changing to SingleOrDefault fixed that for now.

I would say that this is occurring because each time an instance of the mocked db context is created, it doesn't know about the previous read only/query source set up - regardless of scope. There is no logic in the library to persist a read only source between instances of the mocked db context.

I had a bit more of a think about it and I guess if you treat the read only set source as immutable (which it is to a point) and set it up as part of the mocked db context creation, this gist shows how it could be done.

The same problem exists for FromSql* and ExecuteSql* set ups. When used in a manner where you are creating new mocked db contexts, you'd have to do something to ensure those set ups are added to the new instance.

I will look at the gist you linked and try that out. Thanks for the response again!

The gist you provided worked perfectly. I'll use that setup whenever we need to use readonly data.

Closing this for now as it appears we've resolved issue. Please let me know if you have any other issues.