Entity Framework Core 2.1 проблемы с отношениями - PullRequest
0 голосов
/ 09 ноября 2018

Я пытаюсь преобразовать следующий SQL-запрос в платформу сущностей, но у меня возникают проблемы с тем, что столбцы не присоединяются к таблицам.

SELECT 
a.TABLE_NAME AS tableName,
b.COLUMN_NAME AS columnName,
b.DATA_TYPE AS dataType,
CASE WHEN b.IS_NULLABLE = 'NO' THEN 'FALSE' ELSE 'TRUE' END AS allowNull
FROM INFORMATION_SCHEMA.TABLES a
INNER JOIN INFORMATION_SCHEMA.COLUMNS b ON a.TABLE_NAME = b.TABLE_NAME

Это то, что у меня есть до сих пор

Контекст БД:

using Microsoft.EntityFrameworkCore;

namespace EFCoreTest.Models 
{
    public class InformationContext : DbContext
    {   
        protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) 
        {
            optionsBuilder.UseSqlServer(@"Server=localhost;Database=master;Trusted_Connection=True;");
        }

        protected override void OnModelCreating(ModelBuilder modelBuilder)
        {
            modelBuilder.Entity<Table>()
                .HasKey(t => new {t.tableName, t.catalogName, t.schemaName});

            modelBuilder.Entity<Column>()
                .HasOne(c => c.table)
                .WithMany(c => c.columns)
                .HasForeignKey(c => new {c.tableName, c.catalogName, c.schemaName});

        }

        public DbSet<Table> Tables {get; set;}
        public DbSet<Column> Columns {get; set;}
    }
}

Класс колонки:

using System;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;

namespace EFCoreTest.Models
{
    [Table("COLUMNS", Schema = "INFORMATION_SCHEMA")]
    public class Column
    {
        [Key]
        [Column("COLUMN_NAME")]
        public String columnName {get; set;}
        [Column("DATA_TYPE")]
        public String dataType {get; set;}
        [Column("IS_NULLABLE")]
        public String allowNUlls {get; set;}
        [ForeignKey("Table")]
        [Column("TABLE_NAME")]
        public String tableName {get; set;}
        [ForeignKey("Table")]
        [Column("TABLE_CATALOG")]
        public String catalogName {get; set;}
        [ForeignKey("Table")]
        [Column("TABLE_SCHEMA")]
        public String schemaName {get; set;}
        public Table table {get; set;}

    }
}

Класс стола:

using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;

namespace EFCoreTest.Models
{
    [Table("TABLES" , Schema = "INFORMATION_SCHEMA")]
    public class Table
    {
        [Key]
        [Column("TABLE_NAME")]
        public String tableName {get; set;}
        [Key]
        [Column("TABLE_CATALOG")]
        public String catalogName {get; set;}
        [Key]
        [Column("TABLE_SCHEMA")]
        public String schemaName {get; set;}
        public ICollection<Column> columns {get; set;}

        protected Table() {columns = new List<Column>();}
    }
}

Main:

using System;
using Microsoft.EntityFrameworkCore;
using System.Linq;
using EFCoreTest.Models;

namespace EFCoreTest
{
    class Program
    {
        static void Main(string[] args)
        {
            using(InformationContext context = new InformationContext())
            {
                var results = context.Tables.Include(t => t.columns).ToList();

                foreach(var t in results)
                {
                    Console.WriteLine(t.tableName);
                    Console.WriteLine("-----------------------------");
                    var columns = t.columns.ToList();

                    foreach(var c in columns)
                    {
                        Console.WriteLine(c.columnName);
                    }

                    Console.WriteLine("");
                }
            }
        }
    }
}

Код работает нормально, но при проверке экземпляров таблицы все экземпляры столбца равны нулю. У меня такое чувство, что это как-то связано с отношениями между таблицей и столбцом, но после просмотра документов на наличие связей для efcore2.1 я не могу понять, что я делаю неправильно.

Буду признателен за любую помощь.

Обновление: Обновлен код с дополнительными ключами и загрузкой соответствующих данных.

Ответы [ 2 ]

0 голосов
/ 09 ноября 2018

Прежде всего, добро пожаловать в переполнение стека.

Согласно ответу Гонсало, оператор «Включить» позволит вам включить коллекцию:

context.Tables.Include(t => t.columns).ToList();

Я хотел бы, однако, выделить несколько других незначительных улучшений, которые вы могли бы сделать, чтобы сделать ваш код более надежным и поддерживаемым с течением времени.

  1. Не забудьте инициализировать ваши сущности защищенным конструктором, чтобы избежать исключений нулевого указателя, так как в большинстве случаев возврат пустых коллекций с сущностью должен быть допустимым сценарием для бизнес-приложений:

    защищенный стол () { колонки = новый список (); }

  2. Использовать ICollection для определения коллекции вместо списка.

  3. Общепринятые стандарты именования в C # используют оболочку Pascal при объявлении общедоступных свойств и коллекций.

  4. Вы смешиваете 2 разных способа определения отношений.

Это:

modelBuilder.Entity<Column>()
    .HasOne(c => c.table)
    .WithMany(c => c.columns)
    .HasForeignKey(c => c.tableForeignKey);

и аннотации, которые вы используете для соответствующих свойств объектов, таких как [Ключ], на самом деле являются двумя разными способами сделать одно и то же. Используйте один, предпочтительно код, сначала через конфигурации.

5, я бы предложил использовать отдельный файл конфигурации типа сущности, в противном случае вашу схему в конечном итоге будет очень сложно поддерживать, например базовая конфигурация:

public class BaseEntityConfiguration<TEntity> : IEntityTypeConfiguration<TEntity>
    where TEntity : BaseEntity
{
    public virtual void Configure(EntityTypeBuilder<TEntity> builder)
    {
        builder.HasKey(be => be.Guid);

        builder.Property(be => be.CreatedBy).IsRequired();

        builder.Property(be => be.CreatedDate).IsRequired();
    }
}

public class AddressConfiguration : BaseEntityConfiguration<Address>
{
    public override void Configure(EntityTypeBuilder<Address> builder)
    {
        builder.HasOne(a => a.Contact)
            .WithMany(c => c.Addresses)
            .HasForeignKey(a => a.ContactGuid);

        builder.HasOne(a => a.Partner)
            .WithMany(a => a.Addresses)
            .HasForeignKey(a => a.PartnerGuid);

        base.Configure(builder);
    }
}

и в контексте:

modelBuilder.ApplyConfiguration(new AddressConfiguration());

Как вы, наверное, заметили, я также использую BaseEntity для хранения всех повторяющихся свойств, таких как Id, и просто извлекаю из него все свои сущности. Я бы посоветовал вам сделать то же самое.

Надеюсь, это поможет.

0 голосов
/ 09 ноября 2018

Попробуйте это:

context.Tables.Include(t => t.columns).ToList();
...