Один важный факт @slauma дал в комментариях. Так что посмотрите на ответ И комментарии!
Я пытаюсь использовать компоненты, которые на самом деле используются с NHibernate, с EF6. Проблема в том, что у меня есть некоторые наследования TPT с первичными ключами разных имен. База данных и классы POCO даны, и я не могу изменить ни один из них, так что и CodeFirst, и разработчик EF не могут быть и речи.
Есть ли способ просто сопоставить существующий Db с существующими классами POCO, например, с данными файлами сопоставления.hbm.xml в NHibernate?
ОБНОВИТЬ:
Фактическая проблема, с которой я столкнулся, - это, прежде всего, отображение TPT нескольких классов, в результате чего эти классы имеют именованные первичные ключи по-разному, которые, по-видимому, сначала не поддерживаются кодом.
Так вот:
public class Record
{
public virtual int Ndx { get; set; } // table column 'ndx'
public virtual DateTime CreatedAt { get; set; } // table column 'created'
// ... further properties
}
public class Patient : Record
{
public virtual int RecordNdx {get; set;} // table column 'record_ndx) with FK => records.ndx
// ... further properties
}
И, как уже говорилось, изменение имени свойства или столбца не является вариантом.
Обновление II:
Это мой регистрационный код:
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<Record>()
.ToTable("record_descriptors", "schema");
modelBuilder.Entity<Record>()
.HasKey<int>(e => e.ndx);
modelBuilder.Entity<Record>()
.Property(e => e.read_flag)
.IsFixedLength()
.IsUnicode(false);
modelBuilder.Entity<Record>()
.Property(e => e.row_version)
.IsFixedLength();
modelBuilder.Entity<Record>()
.Property(e => e.update_info)
.IsUnicode(false);
modelBuilder.Entity<Patient>()
.ToTable("patienten", "schema");
modelBuilder.Entity<Patient>()
.Property(e => e.mpi)
.IsUnicode(false);
modelBuilder.Entity<Patient>()
.Property(e => e.ndx)
.HasColumnName("record_ndx");
modelBuilder.Entity<Patient>()
.Ignore(r => r.RecordNdx);
}
ОБНОВЛЕНИЕ III
Для тестирования я использую:
db.patients.First(p => p.Ndx == 6040);
И это дает следующий SQL (более обширный из-за реальных записей и классов пациентов):
SELECT
[Limit1].[C1] AS [C1],
[Limit1].[ndx] AS [ndx],
[Limit1].[owner_user_object_ndx] AS [owner_user_object_ndx],
[Limit1].[creator_department_user_object_ndx] AS [creator_department_user_object_ndx],
[Limit1].[creator_user_user_object_ndx] AS [creator_user_user_object_ndx],
[Limit1].[created] AS [created],
[Limit1].[read_flag] AS [read_flag],
[Limit1].[last_update] AS [last_update],
[Limit1].[last_update_user] AS [last_update_user],
[Limit1].[last_update_department] AS [last_update_department],
[Limit1].[freitext] AS [freitext],
[Limit1].[row_version] AS [row_version],
[Limit1].[update_info] AS [update_info],
[Limit1].[mpi] AS [mpi]
FROM ( SELECT TOP (1)
[Extent1].[ndx] AS [ndx],
[Extent1].[mpi] AS [mpi],
[Extent2].[owner_user_object_ndx] AS [owner_user_object_ndx],
[Extent2].[creator_department_user_object_ndx] AS [creator_department_user_object_ndx],
[Extent2].[creator_user_user_object_ndx] AS [creator_user_user_object_ndx],
[Extent2].[created] AS [created],
[Extent2].[read_flag] AS [read_flag],
[Extent2].[last_update] AS [last_update],
[Extent2].[last_update_user] AS [last_update_user],
[Extent2].[last_update_department] AS [last_update_department],
[Extent2].[freitext] AS [freitext],
[Extent2].[row_version] AS [row_version],
[Extent2].[update_info] AS [update_info],
'0X0X' AS [C1]
FROM [schema].[patienten] AS [Extent1]
INNER JOIN [schema].[record_descriptors] AS [Extent2] ON [Extent1].[ndx] = [Extent2].[ndx]
WHERE 6040 = [Extent1].[ndx]
) AS [Limit1]
что неверно, поскольку он выбирает [ndx]
из [patienten]
(должен быть record_ndx
), а также пытается присоединиться к [ndx]
Замечание о закрытии этой рабочей статьи в CodePlex утверждает, что, поскольку EF 6, определяющий разные имена ключевых столбцов родительских и дочерних объектов в сопоставлении TPT, работает с Code-First. Если это так, следующее отображение Code-First должно отображать вашу модель и базу данных:
modelBuilder.Entity<Record>()
.ToTable("YourRecordTableName");
modelBuilder.Entity<Record>()
.HasKey(r => r.Ndx);
modelBuilder.Entity<Record>()
.Property(r => r.Ndx)
.HasColumnName("ndx"); // probably redundant because case doesn't matter
modelBuilder.Entity<Record>()
.Property(r => r.CreatedAt)
.HasColumnName("created");
modelBuilder.Entity<Patient>()
.ToTable("YourPatientTableName");
modelBuilder.Entity<Patient>()
.Property(r => r.Ndx) // Yes, no typo: It must be Ndx, NOT RecordNdx !
.HasColumnName("record_ndx");
modelBuilder.Entity<Patient>()
.Ignore(r => r.RecordNdx);
Последнее отображение (игнорируя свойство RecordNdx
) важно. Это означает, что ваше ключевое свойство будет Patient.Ndx
. Я не думаю, что вы можете сделать любое свойство в производном классе ключевым свойством. Свойство ключа всегда должно быть в базовом классе иерархии наследования. Однако это свойство может отображаться дважды (или, как правило, один раз на сущность в цепочке наследования TPT), с разными именами столбцов в таблице - с EF 6.
Избавление от свойства RecordNdx
было бы самым чистым решением. Но поскольку вы сказали, что не можете коснуться своих свойств, по крайней мере, было бы целесообразно RecordNdx
значение RecordNdx
с свойством Ndx
(если вы можете изменить свойство getter и setter):
public virtual int RecordNdx
{
get { return Ndx; }
set { Ndx = value; }
}
редактировать
Я только что проверил отображение Code-First выше с EF 6.1, и он действительно работает! Столбец первичного ключа в таблице Record
- ndx
а в таблице Patient
- record_ndx
. Между этими EF создается взаимно однозначное отношение, которое требуется для сопоставления TPT.
Изменить 2
Что полная тестовая программа, которую я использовал (текущий пакет EF 6.1 Nuget,.NET 4.5, VS 2012, SQL Server 2012 Express):
using System;
using System.Data.Entity;
namespace EFTPT6
{
public class Record
{
public virtual int Ndx { get; set; }
public virtual DateTime CreatedAt { get; set; }
}
public class Patient : Record
{
public virtual int RecordNdx { get; set; }
public virtual string Name { get; set; }
}
public class MyContext : DbContext
{
public DbSet<Record> Records { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<Record>()
.ToTable("Records");
modelBuilder.Entity<Record>()
.HasKey(r => r.Ndx);
modelBuilder.Entity<Record>()
.Property(r => r.Ndx)
.HasColumnName("ndx");
modelBuilder.Entity<Record>()
.Property(r => r.CreatedAt)
.HasColumnName("created");
modelBuilder.Entity<Patient>()
.ToTable("Patients");
modelBuilder.Entity<Patient>()
.Property(r => r.Ndx)
.HasColumnName("record_ndx");
modelBuilder.Entity<Patient>()
.Ignore(p => p.RecordNdx);
}
}
class Program
{
static void Main(string[] args)
{
Database.SetInitializer(new DropCreateDatabaseAlways<MyContext>());
using (var ctx = new MyContext())
{
ctx.Database.Initialize(true);
string sql = ctx.Records.ToString();
}
}
}
}
Строка sql
в конце программы:
SELECT
CASE WHEN ( NOT (([Project1].[C1] = 1) AND ([Project1].[C1] IS NOT NULL)))
THEN '0X'
ELSE '0X0X'
END AS [C1],
[Extent1].[ndx] AS [ndx],
[Extent1].[created] AS [created],
CASE WHEN ( NOT (([Project1].[C1] = 1) AND ([Project1].[C1] IS NOT NULL)))
THEN CAST(NULL AS varchar(1))
ELSE [Project1].[Name]
END AS [C2]
FROM [dbo].[Records] AS [Extent1]
LEFT OUTER JOIN (SELECT
[Extent2].[record_ndx] AS [record_ndx],
[Extent2].[Name] AS [Name],
cast(1 as bit) AS [C1]
FROM [dbo].[Patients] AS [Extent2] ) AS [Project1]
ON [Extent1].[ndx] = [Project1].[record_ndx]
Похоже, что отображение соблюдается, т. ndx
Таблицы Records
и Patients
record_ndx
столбцами ndx
и record_ndx
.
Редактировать 3
Важно, чтобы класс контекста не содержал набор для производного объекта, т. public DbSet<Patient> Patients { get; set; }
Ни один public DbSet<Patient> Patients { get; set; }
public DbSet<Patient> Patients { get; set; }
public DbSet<Patient> Patients { get; set; }
. Если это modelBuilder.Entity<Patient>().Property(r => r.Ndx).HasColumnName("record_ndx");
игнорируется, и EF ожидает, что имя первичного ключа в Patient
- ndx
а не record_ndx
. Например, последняя строка в SQL выше становится ON [Extent1].[ndx] = [Project1].[ndx]
.