Разделение объекта, когда ключевой столбец имеет разные имена?

Я использую Entity Framework 4.3.1 Code-First, и мне нужно разделить сущность между двумя таблицами. В таблицах общий общий ключ, и он равен 1 к 1, но столбцы не называются одинаковыми для каждой таблицы.

Я не контролирую расположение данных и не могу запросить никаких изменений.

Так, например, таблицы SQL могли бы быть

SQL data tables

И это будет моя сущность...

public class MyEntity
{
    public int Id {get; set;}
    public string Name {get;set}
    public string FromAnotherTable {get;set;}
}

И вот отображение, которое у меня есть.

public class MyEntityMapping : EntityTypeConfiguration<MyEntity>
{
    public MyEntityMapping()
    {
        this.Property(e => e.Id).HasColumnName("ThePrimaryKeyId");
        this.Property(e => e.Name).HasColumnName("MyDatabaseName");
        this.Property(e => e.FromAnothertable).HasColumnName("AnotherTableColumn");
        this.Map(m =>
            {
                m.Properties(e =>
                     {
                         e.Id,
                         e.Name
                     });
                m.ToTable("MainTable");
            });
        this.Map(m =>
            {
                m.Properties(e =>
                     {
                         e.Id,
                         e.FromAnotherTable
                     });
                m.ToTable("ExtendedTable");
            });
}

Поскольку ключ, совместно используемый между собой, имеет другое имя столбца, я не уверен, как его сопоставить. Это сопоставление будет компилироваться, но не выполняется во время выполнения, потому что EF испускает SQL, который ищет столбец "ThePrimaryKeyId" в таблице "ExtendedTable", которого не существует.

ИЗМЕНИТЬ Чтобы уточнить, то, что я определил выше, может (и работает) работать, если ПК в "ExtendedTable" соблюдает соглашения об именах. Но это не так, и я не могу изменить схему.

В принципе, мне нужен EF для испускания - это оператор SQL, например

SELECT
    [e1].*,   /*yes, wildcards are bad. doing it here for brevity*/
    [e2].*
FROM [MainTable] AS [e1]
INNER JOIN [ExtendedTable] AS [e2]  /*Could be left join, don't care. */
    ON  [e1].[ThePrimaryKeyId] = [e2].[NotTheSameName]

Но единственное, что, похоже, хочет испустить, - это

 SELECT
        [e1].*,
        [e2].*
    FROM [MainTable] AS [e1]
    INNER JOIN [ExtendedTable] AS [e2]
        ON  [e1].[ThePrimaryKeyId] = [e2].[ThePrimaryKeyId] /* this column doesn't exist */

Edit Я попробовал подход 1 к 1 снова в предложении NSGaga. Это не сработало, но вот результаты. Объекты

public class MyEntity
{
    public int Id { get; set; }
    public int Name { get; set; }
    public virtual ExtEntity ExtendedProperties { get; set; }
}
public class ExtEntity
{
    public int Id { get; set; }
    public string AnotherTableColumn { get; set; }
    public virtual MyEntity MainEntry { get; set; }
}

Вот классы отображения

public class MyEntityMapping : EntityTypeConfiguration<MyEntity>
{
    public MyEntityMapping()
    {
        this.Property(e => e.Id).HasColumnName("ThePrimaryKeyId");
        this.Property(e => e.Name).HasColumnName("MyDatabaseName");
        this.ToTable("MainTable");
        this.HasKey(e => e.Id);
        this.HasRequired(e => e.ExtendedProperties).WithRequiredPrincipal(f => f.MainEntry);
    }
}

public class ExtEntityMapping : EntityTypeConfiguration<ExtEntity>
{
    public ExtEntityMapping()
    {
        this.Property(e => e.Id).HasColumnName("NotTheSameName");
        this.Property(e => e.AnotherTableColumn).HasColumnName("AnotherTableColumn");
        this.ToTable("ExtendedTable");
        this.HasKey(e => e.Id);
        this.HasRequired(e => e.MainEntry).WithRequiredDependent(f => f.ExtendedProperties);
    }
}

Эта настройка получает сообщение

"Column or attribute 'MyEntity_ThePrimaryKeyId' is not defined in 'ExtendedTable'"

Изменение окончательной строки карты на

this.HasRequired(e => e.MainEntry).WithRequiredDependent(f => f.ExtendedProperties).Map(m => M.MapKey("NotTheSameName"));

Возвращает это сообщение

"Each property name in a type must be unique. property name 'NotTheSameName' was already defined."

Изменение сопоставленного ключа для использования столбца из родительской таблицы MapKey("ThePrimaryKeyId"). возвращает это сообщение

"Column or attribute 'ThePrimaryKeyId' is not defined in 'ExtendedTable'"

Удаление свойства Id из класса ExtEntity вызывает ошибку, потому что тогда сущность не имеет определенного ключа.

Ответ 1

Я не могу найти ничего, что конкретно указывает, что имя столбца должно быть одинаковым в обеих таблицах; но я не могу найти ничего, что говорит, что это не так, или объясняет, как вы могли бы сопоставить этот сценарий. Каждый пример, который я могу найти, имеет ключ с тем же именем в обеих таблицах. Мне кажется, что это дыра в дизайне DbContext.

Ответ 2

Я работаю над этой самой проблемой в течение нескольких дней, и, наконец, я решил установить имя столбца поля Id внутри контекста фрагмента отображения. Таким образом вы можете присвоить идентификатору (или внешнему ключу, зависящему от идентификатора) другое имя из идентификатора основной таблицы.

this.Map(m =>
    {
        m.Property(p => p.Id).HasColumnName("NotTheSameName");
        m.Properties(e =>
             {
                 e.Id,
                 e.FromAnotherTable
             });
        m.ToTable("ExtendedTable");
    });

Если вы запустите и отлаживаете это, вы обнаружите, что он даст вам что-то вроде того, что вы хотите:

[e1].[ThePrimaryKeyId] = [e2].[NotTheSameName]

Ответ 3

Переместите имя HasColumnName внутри отображения:

this.Property(e => e.FromAnothertable).HasColumnName("AnotherTableColumn");
this.Map(m =>
    {
        m.Properties(e => new
             {
                 e.Id,
                 e.Name
             });
             m.Property(e => e.Id).HasColumnName("ThePrimaryKeyId");
             m.Property(e => e.Name).HasColumnName("MyDatabaseName");

           m.Property(e => e.Id).HasColumnName("ThePrimaryKeyId");
        m.ToTable("MainTable");
    });
this.Map(m =>
    {
        m.Properties(e => new
             {
                 e.Id,
                 e.FromAnotherTable
             });
        m.ToTable("ExtendedTable");
    });
}

Ответ 4

Нет Visual Studio здесь, но попробуйте это с помощью подхода 1 к 1:

this.HasRequired(e = > e.ExtendedProperties).HasConstraint((e, m) = > e.Id == m.Id);

Update:
Вот некоторые ссылки, которые могут помочь (не удалось найти ссылку на ссылку)

Как объявить отношения один к одному с помощью Entity Framework 4 Code First (POCO)
Entity Framework 4 CTP 4 Code First: как работать с нетрадиционными именами первичных и внешних ключей

Ответ 5

И просто предоставить (как я и обещал) сопоставление 1 к 1 (два сущности, две таблицы), для чего это стоит.
Вот что работает для меня и должно в вашем случае...

public class MainTable
{
    public int ThePrimaryKeyId { get; set; }
    public string Name { get; set; }
}
public class ExtendedTable
{
    public int NotTheSameNameID { get; set; }
    public string AnotherTableColumn { get; set; }
    public MainTable MainEntry { get; set; }
}
public class MainDbContext : DbContext
{
    public DbSet<MainTable> MainEntries { get; set; }
    public DbSet<ExtendedTable> ExtendedEntries { get; set; }
    protected override void OnModelCreating(DbModelBuilder modelBuilder)
    {
        modelBuilder.Entity<MainTable>()
            .HasKey(x => new { x.ThePrimaryKeyId });

        modelBuilder.Entity<ExtendedTable>()
            .HasKey(x => new { x.NotTheSameNameID });

        // Extended To Main 1 on 1
        modelBuilder.Entity<ExtendedTable>()
            .HasRequired(i => i.MainEntry)
            .WithRequiredDependent();
    }
}

... и тестовый код что-то вроде...

using (var db = new UserDbContext())
{
    foreach (var userid in Enumerable.Range(1, 100))
    {
        var main = new MainTable { Name = "Main" + userid };
        db.MainEntries.Add(main);

        var extended = new ExtendedTable { AnotherTableColumn = "Extended" + userid, MainEntry = main };
        db.ExtendedEntries.Add(extended);
    }
    int recordsAffected = db.SaveChanges();
    foreach (var main in db.MainEntries)
        Console.WriteLine("{0}, {1}", main.Name, main.ThePrimaryKeyId);
    foreach (var extended in db.ExtendedEntries)
        Console.WriteLine("{0}, {1}, {2}, {3}", extended.AnotherTableColumn, extended.NotTheSameNameID, extended.MainEntry.Name, extended.MainEntry.ThePrimaryKeyId);
}

Это создает следующие SQL script, таблицы...

CREATE TABLE [MainTables] (
    [ThePrimaryKeyId] [int] NOT NULL IDENTITY,
    [Name] [nvarchar](4000),
    CONSTRAINT [PK_MainTables] PRIMARY KEY ([ThePrimaryKeyId])
)
CREATE TABLE [ExtendedTables] (
    [NotTheSameNameID] [int] NOT NULL,
    [AnotherTableColumn] [nvarchar](4000),
    CONSTRAINT [PK_ExtendedTables] PRIMARY KEY ([NotTheSameNameID])
)
CREATE INDEX [IX_NotTheSameNameID] ON [ExtendedTables]([NotTheSameNameID])
ALTER TABLE [ExtendedTables] ADD CONSTRAINT [FK_ExtendedTables_MainTables_NotTheSameNameID] FOREIGN KEY ([NotTheSameNameID]) REFERENCES [MainTables] ([ThePrimaryKeyId])

И примечание, согласно нашему обсуждению выше...
Это не "расщепление" - но
(а) первый код IMO не позволяет ничего подобного (я сначала это пробовал, а также вручную, но он "внутренне" изменял все, основываясь на ожидаемых именах столбцов, одинаковых, и, похоже, нет никакого способа обойти это, для эта версия EF как минимум.
(b) структура таблицы - таблицы могут быть сделаны так, чтобы они выглядели точно так, как вам нужно (как я уже говорил, прежде чем использовать ее для сопоставления существующих таблиц членства aspnet (которые я не мог изменить) в своей пользовательской таблице, которая имеет собственный пользователь -id, указывающий на таблицу/идентификатор внешней /aspnet. Правда, вы не можете сделать это с использованием одного класса модели С#, но сторона С# намного более гибкая, и если вы можете контролировать С#, который должен дать тот же эффект, по моему мнению, по крайней мере (как в тесте, вы можете получить к нему доступ всегда через расширенный объект, как расширенные, так и основные столбцы, и они всегда совпадают с 1 по 1 и остаются "в синхронизации".
Надеюсь, это поможет некоторым ПРИМЕЧАНИЕ. Вам не нужно беспокоиться о fk id и т.д. - просто всегда обращайтесь и добавляйте главную запись через MainEntry, а id-s будет в порядке.

EDIT:
Вы также можете сделать следующее, чтобы получить видимость иметь дело только с одним классом (т.е. С разделом)

public class ExtendedTable
{
    public int NotTheSameNameID { get; set; }
    public string AnotherTableColumn { get; set; }

    public string Name { get { return MainEntry.Name; } set { MainEntry.Name = value; } }
    // public int MainID { get { return MainEntry.ThePrimaryKeyId; } set { MainEntry.ThePrimaryKeyId = value; } }
    internal MainTable MainEntry { get; set; }

    public ExtendedTable()
    {
        this.MainEntry = new MainTable();
    }
}

... и используйте его так:

var extended = new ExtendedTable { AnotherTableColumn = "Extended" + userid, Name = "Main" + userid };  

... также вы можете вернуть направление fk, выполнив WithRequiredPrincipal вместо зависимого.
(также все ссылки должны быть не "виртуальными", если вам требуется один-к-одному)
(и MainTable можно сделать "внутренним", как здесь, поэтому он не виден снаружи - он не может быть вложен, поскольку EF не позволяет - обрабатывается как NotMapped)
... ну, что самое лучшее, что я мог сделать:)

Ответ 7

Я хотел бы предложить использовать некоторые аннотации данных, например:

MainTable
---------
MainTableId
DatabaseName

ExtendedTable
----------
NotTheSameName
AnotherColumn

public class MainTable
{
 [Key]
 public int MainTableId { get; set; }
 public string DatabaseName { get; set; }

 [InverseProperty("MainTable")]
 public virtual ExtendedTable ExtendedTable { get; set; }
}

public class ExtendedTable
{
 [Key]
 public int NotTheSameName { get; set; }

 public string AnotherColumn { get; set; }

 [ForeignKey("NotTheSameName")]
 public virtual MainTable MainTable { get; set; }
}

Ответ 8

Я столкнулся с этой проблемой и решил добавить атрибут Column для соответствия именам обоих столбцов. [Key] [Column("Id")] public int GroupId { get; set; }