Entity Framework 4.1 — отношения между неключевыми столбцами

У меня есть 2 связанных объекта, но устаревшая схема sql по существу имеет 2 ключевых столбца для одной и той же таблицы (а не ключ из 2 столбцов: см. ниже). Мне нужно создать отношение обратно к столбцу «фальшивый ключ». Есть ли способ сделать это декларативно в Entity Framework 4.1?

Public Class Client
    Inherits ModelBase

    <Key(), Required()>
    Public Property ClientID As Decimal

    <Required(), StringLength(50)>
    Public Property ClientCode As String

    ........


Public Class ClientLocation
    Inherits ModelBase

    ........

    <Required(), StringLength(50)>
    Public Property ClientCode As String

    ........

    <ForeignKey("ClientCode")>
    Public Overridable Property Client As Clients.Client

И ошибка, которую я получаю:

* Во время создания модели были обнаружены одна или несколько ошибок проверки: System.Data.Edm.EdmAssociationConstraint: : типы всех свойств в зависимой роли ссылочного ограничения должны совпадать с соответствующими типами свойств в основной роли. Тип свойства ClientCode объекта ClientLocation не соответствует типу свойства ClientID объекта Client в реляционном ограничении ClientLocation_Client*.

Потому что он думает, что я пытаюсь сопоставить ClientLocation.ClientCode > Client.ClientID, хотя на самом деле я пытаюсь сопоставить ClientLocation.ClientCode > Client.ClientCode...

Есть предположения?

Спасибо!


person Tom Halladay    schedule 24.08.2011    source источник
comment
...унаследованная схема sql по существу имеет 2 ключевых столбца для одной и той же таблицы...: Вы имеете в виду, что Client.ClientCode является столбцом с уникальным индексом в базе данных? Или что такое 2 ключевых столбца... но не составной ключ? И вы хотите каким-то образом сопоставить ClientLocation.Client с этим уникальным столбцом Client.ClientCode?   -  person Slauma    schedule 24.08.2011
comment
В таблице есть 2 эффективных ключа, но второй не идентифицируется как ключ и не имеет индекса. Например, ClientID может быть равен 4, а ClientCode может быть FOGCREEK. Они не связаны и не зависят друг от друга, просто они оба уникальны. И да, мне нужно сопоставить исходную таблицу с помощью Client.ClientCode, даже если он не помечен как ключ в моем объекте.   -  person Tom Halladay    schedule 25.08.2011
comment
А, понятно, тогда ClientCode это обычная колонка. Уникальность просто случайно обеспечивается бизнес-логикой. Боюсь, что ответ Ладилава будет последним словом.   -  person Slauma    schedule 25.08.2011


Ответы (4)


Структура Entity требует, чтобы связь строилась между целым первичным ключом в основной таблице и соответствующими столбцами (внешним ключом) в зависимой таблице.

person Ladislav Mrnka    schedule 24.08.2011
comment
Итак, как вы понимаете, нет способа сопоставить отношения так же, как мы можем сопоставить имена таблиц и столбцов? - person Tom Halladay; 25.08.2011
comment
Вы должны отобразить отношение так же, как и в базе данных. Если в вашей базе данных неправильно настроены таблицы, EF не сможет это исправить. - person Ladislav Mrnka; 25.08.2011
comment
Несмотря на то, что этот ответ, вероятно, правильный, я пока не хочу принимать его без большего консенсуса. Может ли кто-нибудь еще проверить, что нет никакого метода, похожего на аннотации данных ‹Table(oldTableName)› и ‹Column(oldColumnName)›, чтобы помочь переопределить поля отношений? - person Tom Halladay; 29.08.2011

Даже если это невозможно, вы все равно можете соединить две таблицы, используя такой запрос LINQ (C#) (см. также этот вопрос).

var result = from a in ctx.Client
             join b in ctx.ClientLocation
             on a.ClientCode equals b.ClientCode
             select new { Client = a, Location = b };

Вам не хватает только навигационного свойства Client.ClientLocation и ClientLocation.Client. Это немного более громоздко сделать это таким образом, но, тем не менее, это возможно.

Если вы хотите расширить схему SQL, вы можете добавить еще одну таблицу, такую ​​как ClientLocationClient, которая действует как таблица M:N с внешними ключами как для Client, так и для ClientLocation, а также в качестве составного ключа. Затем вы можете перемещаться так (С#)

var client = myClientLocation.ClientLocationClients.First().Client; // there's only one

Однако эта строка завершится ошибкой, как только у вас будет ClientLocation, у которого нет соответствующего Client, И, конечно, вам нужно будет определить триггер в Client для синхронизации вашей таблицы расширений и настроить каскадное удаление, И ClientLocation необходимо вставить перед Клиент, иначе триггер не сработает... В целом, я просто хочу предупредить, что это может быть довольно опасным маршрутом.

person Andreas    schedule 30.09.2011

(Это всего лишь приложение к ответу Ладислава, принять и награда не должна идти на мой ответ.)

Я был бы удивлен, если бы такая функция когда-либо была реализована в EF. Почему? Потому что вы даже не можете создать такую ​​связь в реляционной базе данных. Связь внешнего ключа в реляционной БД (по крайней мере, в SQL Server и, возможно, в большинстве или во всех остальных) требует, чтобы основной стороной был столбец, который является либо первичным ключом, либо имеет ограничение уникального ключа. Это имеет смысл, поскольку внешний ключ должен ссылаться на одну уникальную строку в основной таблице.

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

Что вы ожидаете, если значение в целевом столбце основной таблицы не уникально? Хотите ли вы исключение, когда вы, например, пытаетесь загрузить ClientLocation.Client - "Не удается загрузить свойство навигации "Клиент", поскольку внешний ключ не относится к уникальной цели" или предупреждение "Загрузил клиент, но есть другой, не могу убедиться, что я загрузил тот, который вы хотели" или что-то в этом роде?

Если вы хотите сделать себе одолжение, я бы отказался от этой идеи, удалил свойство навигации и подумал о том, чтобы много работать с Join в ваших запросах LINQ.

person Slauma    schedule 23.09.2011

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

Какой-то код вроде этого:

[ForeignKey()]
[Association("SomeNameForAssociation","TheKeyInThisEntity","TheKeyOnTheAssociationTargetEntity")]
public virtual Examination Examination { get; set; }

И если я понимаю ваш вопрос, ваш код должен измениться на:

Public Class Client   
    Inherits ModelBase   

    <Key(), Required()>   
    Public Property ClientID As Decimal   

    <Required(), StringLength(50)>   
    Public Property ClientCode As String   

    ........   


Public Class ClientLocation   
    Inherits ModelBase   

    ........   

    <Required(), StringLength(50)>   
    Public Property ClientCode As String   

    ........   

    <ForeignKey("ClientCode")> 
    <Association("ClientClientCodes","ClientCode","ClientCode")>
    Public Overridable Property Client As Clients.Client  

Первый «ClientCode»: имя ключевого столбца в ClientCode. Второй «ClientCode»: имя ключевого столбца в ClientCode, который вы хотите использовать.

Примечание. Я еще не использовал этот атрибут, но его документация, его имя и имена аргументов предполагают, что он должен удовлетворить ваши потребности.

person 000    schedule 28.09.2011
comment
Я сомневаюсь, что атрибут [Association] имеет какой-либо эффект в Entity Framework. Это всего лишь атрибут сопоставления для LINQ to SQL. - person Slauma; 28.09.2011