Разделение объекта, когда ключевой столбец имеет разные имена?
Я использую 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)
... ну, что самое лучшее, что я мог сделать:)
Ответ 6
Похоже, он исправлен в Entity Framework 6. См. этот вопрос http://entityframework.codeplex.com/workitem/388
Ответ 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; }