Объединение двух таблиц в EF Core, недопустимое имя столбца - PullRequest
0 голосов
/ 11 июля 2020

Я пытаюсь объединить две таблицы в EntityFramework Core с использованием синтаксиса linq следующим образом:

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

Где StringProperty - это строковый столбец в базе данных. Я получаю сообщение об ошибке

{"Invalid column name 'EntityId'."}

Я нашел этот похожий вопрос Entity Framework: недопустимое имя столбца 'OrganizationStructure_ID' , где предлагается установить внешние ключи во время создания модели, как это

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

Когда я пробую это, я получаю сообщение об ошибке:

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

Есть предложения, как это исправить?

1 Ответ

1 голос
/ 12 июля 2020

На основании предоставленной вами информации сложно сказать, в чем именно заключается проблема. Но вот очень простой рабочий консольный проект, который явно использует Join(), как вы хотите:

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");
        }
    }
}

Он генерирует следующий запрос SQL:

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]

Если ваше строковое свойство уникально (но не первичный ключ), вы также можете определить их следующим образом и использовать 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");
        }
    }
}

Он генерирует следующий запрос SQL :

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]

Наконец, в случае, если свойство строки является первичным ключом, это становится еще проще:

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");
        }
    }
}

Он генерирует следующий SQL запрос:

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]
...