Joining two tables in EF Core, invalid column name

流过昼夜 提交于 2021-01-29 17:59:46

问题


I am trying to join two tables in EntityFramework Core using linq syntax in the following way:

var results = dc.EntityA.Join(dc.EntityB, a => a.StringProperty, b => b.StringProperty, (a, b) => DoSomeStuff(a, b)).ToList();

Where StringProperty is a string column in the database. I am getting the error

{"Invalid column name 'EntityId'."}

I found this similar question Entity Framework: Invalid column name 'OrganizationStructure_ID' where it is suggested to set the foreignkeys during ModelCreation like this

   modelBuilder.Entity<EntityA>()
            .HasOne(a => a.StringProperty)
            .WithOne().HasForeignKey<EntityB>(b => b.StringProperty);

When I try this I get the error:

Property or navigation with the same name already exists on entity type

Any suggestions how to fix this?


回答1:


Difficult to say from the information you gave, where the actual issue lies. But here is a very simple working console project, that explicitly uses Join() as you want:

using System.Diagnostics;
using System.Linq;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Logging;

namespace IssueConsoleTemplate
{
    public class IceCream
    {
        public int IceCreamId { get; set; }
        public string Name { get; set; }
        public string BrandName { get; set; }
    }

    public class IceCreamBrand
    {
        public int IceCreamBrandId { get; set; }
        public string Name { get; set; }
    }

    public class Context : DbContext
    {
        public DbSet<IceCream> IceCreams { get; set; }
        public DbSet<IceCreamBrand> IceCreamBrands { get; set; }

        protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
        {
            optionsBuilder
                .UseSqlServer(@"Data Source=.\MSSQL14;Integrated Security=SSPI;Initial Catalog=So62853243")
                .UseLoggerFactory(
                    LoggerFactory.Create(
                        b => b
                            .AddConsole()
                            .AddFilter(level => level >= LogLevel.Information)))
                .EnableSensitiveDataLogging()
                .EnableDetailedErrors();
        }

        protected override void OnModelCreating(ModelBuilder modelBuilder)
        {
            modelBuilder.Entity<IceCreamBrand>()
                .HasData(
                    new IceCreamBrand {IceCreamBrandId = 1, Name = "Cold as Ice"},
                    new IceCreamBrand {IceCreamBrandId = 2, Name = "Sweet as Sweets"});
            
            modelBuilder.Entity<IceCream>()
                .HasData(
                    new IceCream {IceCreamId = 1, Name = "Vanilla", BrandName = "Cold as Ice"},
                    new IceCream {IceCreamId = 2, Name = "Chocolate", BrandName = "Cold as Ice"},
                    new IceCream {IceCreamId = 3, Name = "Vanilla", BrandName = "Sweet as Sweets"});
        }
    }

    internal static class Program
    {
        private static void Main()
        {
            using var context = new Context();

            context.Database.EnsureDeleted();
            context.Database.EnsureCreated();

            var iceCreamWithBrands = context.IceCreams.Join(
                    context.IceCreamBrands,
                    i => i.BrandName,
                    b => b.Name,
                    (i, b) => new {IceCream = i, Brand = b})
                .Where(j => j.IceCream.BrandName == "Cold as Ice")
                .OrderBy(j => j.IceCream.IceCreamId)
                .ThenBy(j => j.Brand.IceCreamBrandId)
                .ToList();
            
            Debug.Assert(iceCreamWithBrands.Count == 2);
            Debug.Assert(iceCreamWithBrands[0].IceCream.Name == "Vanilla");
            Debug.Assert(iceCreamWithBrands[0].Brand.Name == "Cold as Ice");
        }
    }
}

It generates the following SQL query:

SELECT [i].[IceCreamId], [i].[BrandName], [i].[Name], [i0].[IceCreamBrandId], [i0].[Name]
FROM [IceCreams] AS [i]
INNER JOIN [IceCreamBrands] AS [i0] ON [i].[BrandName] = [i0].[Name]
WHERE [i].[BrandName] = N'Cold as Ice'
ORDER BY [i].[IceCreamId], [i0].[IceCreamBrandId]

In case your string property is unique (but not the primary key), you can also define them in the following way and use Include():

using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Logging;

namespace IssueConsoleTemplate
{
    public class IceCream
    {
        public int IceCreamId { get; set; }
        public string Name { get; set; }
        public string BrandName { get; set; }
        
        public IceCreamBrand Brand { get; set; }
    }

    public class IceCreamBrand
    {
        public int IceCreamBrandId { get; set; }
        public string Name { get; set; }
        
        public ICollection<IceCream> IceCreams { get; set; } = new HashSet<IceCream>();
    }

    public class Context : DbContext
    {
        public DbSet<IceCream> IceCreams { get; set; }
        public DbSet<IceCreamBrand> IceCreamBrands { get; set; }

        protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
        {
            optionsBuilder
                .UseSqlServer(@"Data Source=.\MSSQL14;Integrated Security=SSPI;Initial Catalog=So62853243")
                .UseLoggerFactory(
                    LoggerFactory.Create(
                        b => b
                            .AddConsole()
                            .AddFilter(level => level >= LogLevel.Information)))
                .EnableSensitiveDataLogging()
                .EnableDetailedErrors();
        }

        protected override void OnModelCreating(ModelBuilder modelBuilder)
        {
            modelBuilder.Entity<IceCream>(
                entity =>
                {
                    entity.Property(i => i.BrandName)
                        .IsRequired();
                    
                    entity.HasOne(i => i.Brand)
                        .WithMany(b => b.IceCreams)
                        .HasForeignKey(i => i.BrandName)
                        .HasPrincipalKey(b => b.Name);

                    entity.HasData(
                        new IceCream {IceCreamId = 1, Name = "Vanilla", BrandName = "Cold as Ice"},
                        new IceCream {IceCreamId = 2, Name = "Chocolate", BrandName = "Cold as Ice"},
                        new IceCream {IceCreamId = 3, Name = "Vanilla", BrandName = "Sweet as Sweets"});
                });

            modelBuilder.Entity<IceCreamBrand>(
                entity =>
                {
                    entity.HasAlternateKey(b => b.Name);

                    entity.Property(e => e.Name)
                        .IsRequired();

                    entity.HasData(
                        new IceCreamBrand {IceCreamBrandId = 1, Name = "Cold as Ice"},
                        new IceCreamBrand {IceCreamBrandId = 2, Name = "Sweet as Sweets"});
                });
        }
    }

    internal static class Program
    {
        private static void Main()
        {
            using var context = new Context();

            context.Database.EnsureDeleted();
            context.Database.EnsureCreated();

            var iceCreamWithBrands = context.IceCreams
                .Include(i => i.Brand)
                .Where(i => i.Brand.Name == "Cold as Ice")
                .OrderBy(i => i.IceCreamId)
                .ThenBy(i => i.Brand.IceCreamBrandId)
                .ToList();
            
            Debug.Assert(iceCreamWithBrands.Count == 2);
            Debug.Assert(iceCreamWithBrands[0].Name == "Vanilla");
            Debug.Assert(iceCreamWithBrands[0].Brand.Name == "Cold as Ice");
        }
    }
}

It generates the following SQL query:

SELECT [i].[IceCreamId], [i].[BrandName], [i].[Name], [i0].[IceCreamBrandId], [i0].[Name]
FROM [IceCreams] AS [i]
INNER JOIN [IceCreamBrands] AS [i0] ON [i].[BrandName] = [i0].[Name]
WHERE [i0].[Name] = N'Cold as Ice'
ORDER BY [i].[IceCreamId], [i0].[IceCreamBrandId]

Finally, in case the string property is the primary key, then it gets even simpler:

using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Logging;

namespace IssueConsoleTemplate
{
    public class IceCream
    {
        public string Name { get; set; }
        public string BrandName { get; set; }
        
        public IceCreamBrand Brand { get; set; }
    }

    public class IceCreamBrand
    {
        public string Name { get; set; }
        
        public ICollection<IceCream> IceCreams { get; set; } = new HashSet<IceCream>();
    }

    public class Context : DbContext
    {
        public DbSet<IceCream> IceCreams { get; set; }
        public DbSet<IceCreamBrand> IceCreamBrands { get; set; }

        protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
        {
            optionsBuilder
                .UseSqlServer(@"Data Source=.\MSSQL14;Integrated Security=SSPI;Initial Catalog=So62853243")
                .UseLoggerFactory(
                    LoggerFactory.Create(
                        b => b
                            .AddConsole()
                            .AddFilter(level => level >= LogLevel.Information)))
                .EnableSensitiveDataLogging()
                .EnableDetailedErrors();
        }

        protected override void OnModelCreating(ModelBuilder modelBuilder)
        {
            modelBuilder.Entity<IceCream>(
                entity =>
                {
                    entity.HasKey(i => new {i.Name, i.BrandName});
                    
                    entity.HasOne(i => i.Brand)
                        .WithMany(b => b.IceCreams)
                        .HasForeignKey(i => i.BrandName);

                    entity.HasData(
                        new IceCream {Name = "Vanilla", BrandName = "Cold as Ice"},
                        new IceCream {Name = "Chocolate", BrandName = "Cold as Ice"},
                        new IceCream {Name = "Vanilla", BrandName = "Sweet as Sweets"});
                });

            modelBuilder.Entity<IceCreamBrand>(
                entity =>
                {
                    entity.HasKey(b => b.Name);

                    entity.HasData(
                        new IceCreamBrand {Name = "Cold as Ice"},
                        new IceCreamBrand {Name = "Sweet as Sweets"});
                });
        }
    }

    internal static class Program
    {
        private static void Main()
        {
            using var context = new Context();

            context.Database.EnsureDeleted();
            context.Database.EnsureCreated();

            var iceCreamWithBrands = context.IceCreams
                .Include(i => i.Brand)
                .Where(i => i.Brand.Name == "Cold as Ice")
                .OrderBy(i => i.Name)
                .ThenBy(i => i.Brand.Name)
                .ToList();
            
            Debug.Assert(iceCreamWithBrands.Count == 2);
            Debug.Assert(iceCreamWithBrands[0].Name == "Chocolate");
            Debug.Assert(iceCreamWithBrands[0].Brand.Name == "Cold as Ice");
        }
    }
}

It generates the following SQL query:

SELECT [i].[Name], [i].[BrandName], [i0].[Name]
FROM [IceCreams] AS [i]
INNER JOIN [IceCreamBrands] AS [i0] ON [i].[BrandName] = [i0].[Name]
WHERE [i0].[Name] = N'Cold as Ice'
ORDER BY [i].[Name], [i0].[Name]


来源:https://stackoverflow.com/questions/62853243/joining-two-tables-in-ef-core-invalid-column-name

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!