dotnet / efcore

EF Core is a modern object-database mapper for .NET. It supports LINQ queries, change tracking, updates, and schema migrations.
https://docs.microsoft.com/ef/
MIT License
13.65k stars 3.15k forks source link

Support table/column mapping in the in-memory database #15015

Closed Oblomoff closed 1 year ago

Oblomoff commented 5 years ago

I try to use two different DbContexts (and different entities too!) with the same InMemory database. I created ContextAB which contains model that is compatible with both ContextA and ContextB. The following code works with SqlServer database, but fails with InMemory provider. What`s wrong?

Steps to reproduce

using System.Linq;

using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.InMemory.Metadata.Conventions.Internal;
using Microsoft.EntityFrameworkCore.Metadata;
using Microsoft.EntityFrameworkCore.Metadata.Conventions;
using Microsoft.EntityFrameworkCore.Storage;

using NUnit.Framework;

namespace EntityFrameworkCoreLabs
{
    public class SharedLab
    {
        public static InMemoryDatabaseRoot DatabaseRoot = new InMemoryDatabaseRoot();

        [TestCase(false)]
        [TestCase(true)]
        public void ShareDifferentContexts(bool inMemory)
        {
            // Create common context
            using (var contextAB = new ContextAB(CreateOptions<EntityB>()))
            {
                contextAB.Database.EnsureDeleted();
                contextAB.Database.EnsureCreated();
            }

            // Context with EntityA mapped to SharedEntities table
            using (var contextA = new ContextA(CreateOptions<EntityA>()))
            {
                // Step 1: add EntityA to table SharedEntities
                contextA.Set<EntityA>().Add(new EntityA());
                contextA.SaveChanges();
            }

            // Context with EntityB mapped to SharedEntities table
            using (var contextB = new ContextB(CreateOptions<EntityB>()))
            {
                // Step 2: try get EntityB from table SharedEntities
                // Failure Expected: not null But was: null
                Assert.NotNull(contextB.Set<EntityB>().FirstOrDefault());
            }

            IModel CreateModel<TEntity>()
                where TEntity : class
            {
                var modelBuilder = new ModelBuilder(
                    inMemory
                        ? InMemoryConventionSetBuilder.Build()
                        : SqlServerConventionSetBuilder.Build());

                modelBuilder.Entity<TEntity>(b => b.ToTable("SharedEntities"));
                return modelBuilder.FinalizeModel();
            }

            DbContextOptions CreateOptions<TEntity>()
                  where TEntity : class
            {
                var builder = new DbContextOptionsBuilder();
                if (inMemory)
                {
                    builder.UseInMemoryDatabase("SharedContext", DatabaseRoot);
                }
                else
                {
                    builder.UseSqlServer("Server=(localdb)\\mssqllocaldb;Database=SharedContext;Trusted_Connection=True;MultipleActiveResultSets=True");
                }
                builder.UseModel(CreateModel<TEntity>());
                return builder.Options;
            }
        }

        public class ContextA : DbContext
        {
            public ContextA(DbContextOptions options) : base(options)
            {
            }
        }

        public class ContextB : DbContext
        {
            public ContextB(DbContextOptions options) : base(options)
            {
            }
        }

        public class ContextAB : DbContext
        {
            public ContextAB(DbContextOptions options) : base(options)
            {
            }
        }

        /// <summary>
        /// Entity from ContextA mapped to table SharedEntities
        /// </summary>
        public class EntityA
        {
            public int Id { get; set; }
        }

        /// <summary>
        /// Entity from ContextB mapped to table SharedEntities
        /// </summary>
        public class EntityB
        {
            public int Id { get; set; }

            public string Name { get; set; }
        }
    }
}

Further technical details

EF Core version: 2.2.3 Database Provider: Microsoft.EntityFrameworkCore.InMemory Operating system: Windows 10

ajcvickers commented 5 years ago

@Oblomoff The in-memory database does not support changing the names of "tables" or "columns" since these are relational concepts. This means that the call to ToTable() is ignored. There isn't a way to map entities with different names to the same "table" using the in-memory database.

Leaving issue open for now to discuss in triage.

Oblomoff commented 5 years ago

Thanks

ajcvickers commented 5 years ago

Triage: Adding to the backlog as something that would be useful to add in the future.

SARAVANA1501 commented 5 years ago

I would like to contribute to this. i am a first time contributor. please help me to kick start

ajcvickers commented 5 years ago

@SARAVANA1501 We will discuss and get back to you.

ajcvickers commented 5 years ago

@SARAVANA1501 Sorry for being slow here. After discussion, this is actually something that requires some more thought about how it should be designed/implemented. I have removed good-first-issue here and added needs-design. We have also done a scrub of good-first-issues to try to make sure things are now correctly labeled.

ajcvickers commented 4 years ago

Closing as this is no longer something we intend to implement. We instead plan to improve the experience when testing with the SQLite in-memory database.

InspiringCode commented 1 year ago

I also stumbled upon this issue. We have multiple DbContext's (bounded contexts) sharing the same database and table but this breaks down when we are using the InMemoryDB in our unit tests. Is there any way (work around) to get this to work with InMemoryDb? This would really be useful. Is there any way to explicitly specify the "entity name",so that the In-Memory database knows that EntityClass1 is the same as EntityClass2?

ajcvickers commented 1 year ago

@InspiringCode We recommend that you don't use the in-memory provider.