2012-02-21 2 views
12

Я использую 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 */ 

Редактировать Я попробовал 1 -to-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")); 

Вернуться s это сообщение

"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 класса выдает ошибку, потому что тогда объект не имеет определенного ключа.

+0

Ах! поэтому «ExtendedTable» не находится в схеме, которую вы создаете; если вы просто сдуваете и создаете новую базу данных через DropCreateDatabaseAlways, тогда нет способа сопоставления с несуществующей таблицей; единственный способ состоит в том, чтобы включить эту таблицу как часть схемы или иметь предварительно установленный БД с «расширенной таблицей» уже там и использовать миграцию кода EF 4.3 для изменения базы данных, противоположной созданию нового. –

+0

Я не уверен, что следую тому, что вы имеете в виду, но это существующая схема базы данных. Фактически, это сопоставление с базой данных DB2 на мэйнфрейме. Я не хочу, чтобы EF пытался сбросить или создать что-либо. – Josh

+1

Привет, Джош У меня такая же проблема, и выложили ее здесь на форумах EF [здесь] (http://social.msdn.microsoft.com/Forums/en-US/adodotnetentityframework/thread/be9f5e6a-e5bf-45a6-b86a -b2a74cfda105), им не повезло. Вы слышали от какой-либо команды EF? – TheDuke

ответ

2

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

+0

Да, сейчас я тоже наклоняюсь. – Josh

1

Нет Visual Studio здесь, но попробовать это с 1-на-1 подход:

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

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

How to declare one to one relationship using Entity Framework 4 Code First (POCO)
Entity Framework 4 CTP 4 Code First: how to work with unconventional primary and foreign key names

+0

Я не нахожу никакого метода, как .HasConstraint(). Можете ли вы указать мне документы или ссылку? Благодаря! – Josh

+0

Похоже, что HasConstraint() был в CTP, но не в релизе. Не нашли эквивалента. Я использую выпуск 4.3.1. – Josh

+0

BTW, разделение на сущности было бы предпочтительным решением этого. 1-к-1 является менее желательной альтернативой. Я думаю, что EF не нравится, потому что он хочет видеть PK из каждой таблицы, представленной на другой таблице, тогда как это FK и PK для ее принудительного применения. – Josh

1

И просто предоставить (как я обещал) сопоставление 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 скрипт, таблицы ...

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

И примечание, согласно нашей дискуссии выше ...
Это не «расщепления» - но
(а) первый код ИМО не допускает ничего подобного (я пробовал это сначала, а также вручную изменял миграцию, но он «внутренне» все на основе ожидаемых имен столбцов, являющихся одним и тем же, и, похоже, , по крайней мере для этой версии EF.
(b) структура таблицы мудрая - таблицы могут быть сделаны так, чтобы они выглядели точно, что вам нужно (как я уже говорил, прежде чем использовать его для связи существующих таблиц членства aspnet (которые я не мог изменить) в своей таблице пользователя, которая имеет собственный идентификатор пользователя, указывающий на внешнюю/aspnet-таблицу и идентификатор.
Правда, вы не можете сделать это с использованием одного класса модели C#, но сторона C# намного более гибкая, и если вы можете контролировать C#, который должен дать тот же эффект, мое мнение по крайней мере (как и в тесте, вы можете получить доступ к нему всегда через расширенный объект, как расширенные, так и основные столбцы, и они всегда совпадают с 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 }; 

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

+0

Пробовал это, и он взорвался с ошибкой {«Недопустимое имя столбца« ExtendedTable_ThePrimaryKeyId ».»} – Josh

+0

. Я уже прошел через это, и я думаю, что часть проблемы - это поставщик сущностей, который мы используем. SQL по умолчанию действует, как вы описываете, но это поставщик DB2 от IBM. – Josh

+0

Вы пробовали «чистить» Db, миграции? Я использую EF 4.3, последний - и мне приходилось включать миграции - каждый раз, когда я обычно удаляю db и миграции из проекта, который выполняется повторно (посмотрите на этот пост мин для получения более подробной информации об этом http://stackoverflow.com/questions/9364750/сущность-рамки-4-3-оленья кожа создать-базы данных/9745125 # 9745125). Я могу отправить вам рабочий проект, если это необходимо - просто не уверен, как это сделать в SO – NSGaga

0

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

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; } 
} 
+0

Благодарим вас за предложение, но аннотации данных для нас в данном случае не являются опцией. Более того, Microsoft заявляет, что все, что возможно с Data Annotations, можно выполнить с Fluent API, и есть что-то, что вы можете сделать с Fluent API, который вы не можете сделать с аннотациями. – Josh

+0

@Josh - Fluent API может стать утомительным и не читается так же, как мои аннотации данных. Вот API, но определение обратного свойства довольно расплывчато: http://msdn.microsoft.com/en-us/library/hh295843(v=vs.103).aspx. Очень жаль, что вы не можете использовать аннотации данных, они делают использование EF намного проще для чтения и кодирования. Принятый ответ «Мне кажется, что это дыра в дизайне DbContext». просто не соответствует действительности. Используя либо плавные аннотации api, либо данные будут выполнять обратное отображение свойств. –

+1

Я не согласен с вами в том, что Fluent API является утомительным или менее прямым. Я также не согласен с тем, что аннотации легче читать. Но мое мнение в этом не имеет значения. Наша проблема заключается в использовании аннотаций данных, которые устраняют зависимость Entity Framework от объектов сущности, что означает, что зависимые проекты также должны иметь ссылку EF. Разделяя классы сопоставления сущностей, мы изолировали зависимость EF от проекта, который реализует DbContext и потребляет сопоставления. Кроме того, мы сопоставляем одни и те же объекты в двух разных хранилищах данных, которые требуют разных сопоставлений. – Josh

2

Перемещение 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"); 
    }); 
} 
+0

Почти, он использует два имени для столбцов идентификатора, первый фрагмент отображения имеет имя Id, но не второй. Предоставление идентификатора имени во втором фрагменте отображения с m.Property (p => p.Id).HasColumnName ("NotTheSameName"); решила бы это :) –

3

я работал на этой самой проблеме в течение нескольких дней, что я наконец-то сделал, чтобы установить столбец па меня поле 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] 
+0

Просто любопытно. Какую версию EF вы используете? – Josh

+0

@Josh Последние 6.1.3 –

+0

@Josh, собирался предположить, что это будет отмечено как ответ, но просто увидел, что исходный вопрос был для EF 4.3. Надеюсь, вы смогли обновить версию и решить вашу проблему! –

0

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

Смежные вопросы