Как объявить отношения один-к-одному, используя Entity Framework 4 Code First (POCO) - PullRequest
12 голосов
/ 02 сентября 2010

Как объявить отношения один-к-одному, используя Entity Framework 4 Code First (POCO)?

Я нашел этот вопрос (отношения один-к-одному в Entity Framework 4) , но статья, на которую ссылается ответ, оказалась бесполезной (есть одна строка кода, которая является отношением 1-1 , но нет упоминания о том, как это определить).

Ответы [ 4 ]

22 голосов
/ 31 мая 2012

Три метода:

A) Объявлять оба класса со свойствами навигации друг к другу.Пометьте одну из таблиц (зависимую таблицу) атрибутом ForeignKey в своем первичном ключе.EF выводит 1-к-1 из этого:

public class AppUser
{
    public int Id { get; set; }

    public string Username { get; set; }

    public OpenIdInfo OpenIdInfo { get; set; }
}

​public class OpenIdInfo
{
    [ForeignKey("AppUser")]
    public int Id { get; set; }

    public string OpenId { get; set; }

    public AppUser AppUser { get; set; }
}

http://weblogs.asp.net/manavi/archive/2011/05/01/associations-in-ef-4-1-code-first-part-5-one-to-one-foreign-key-associations.aspx

Я не использовал virtual, и вы тоже не должны. *

B) Объявите иерархию наследования с явно указанными именами таблиц, в результате чего получится таблица-на-тип и общий первичный ключ.

using System.ComponentModel.DataAnnotations;

[Table("AppUser")]
public class AppUser
{
    public int Id { get; set; }

    public string Username { get; set; }

    public OpenIdInfo OpenIdInfo { get; set; }
}

[Table("AdminUser")]      
public class AdminUser : AppUser
{
    public bool SuperAdmin { get; set; }
}

Вы получите 2 таблицы: одну для AppUser, одну для AdminUser.AdminUser равен 1: 1 с AppUser и является зависимым - это означает, что вы можете удалить AdminUser, но если вы удалите AppUser, когда AdminUser все еще указывает на него, вы получите ошибку нарушения ограничения.

C) В EF есть два промежуточных метода «один-к-одному»:

Разделение сущностей , где у вас есть один класс, ноон хранится в первичной таблице и в одной или нескольких связанных таблицах «один к одному».

Разделение таблицы , где дерево объектов сглаживается в одну таблицу.Например, класс со свойством Address будет иметь столбцы для объекта Address, например Address_City, сведенные в одну таблицу.

* Вы можете включить виртуальный в любое свойство EF или в коллекции , если хотите ленитьсязагрузить их .Это может привести к бесконечным циклам или загрузке всей БД, если вы передадите объект со свойствами с отложенной загрузкой, например, в конвертер JSON MVC или что-либо еще, что обходит иерархию объектов.Ленивая загрузка всегда выполняется синхронно, блокируя поток, и без какого-либо уведомления.Подводя итог, список способов, которыми вы можете заморозить свой код, приложение или сервер с его помощью, длинный.Избегайте использования виртуальных классов EF.Да, в Интернете есть много примеров кода, которые его используют.Нет, вы все равно не должны его использовать.

11 голосов
/ 02 сентября 2010

Вы просто ищете что-то подобное?

public class User
{
    public int Id { get; set; }
    public string Username { get; set; }
    public Profile Profile { get; set; }
    public int ProfileId { get; set; }
}

public class Profile
{
    public int Id { get; set; }
    public string FirstName { get; set; }
    public string LastName { get; set; }
    public string PostalCode { get; set; }
    // etc...
}

public class UserMapping : EntityConfiguration<User>
{
    public UserMapping()
    {
        this.HasKey(u => u.Id);
        this.Property(u => u.Username).HasMaxLength(32);

        // User has ONE profile.
        this.HasRequired(u => u.Profile);
    }
}

public class ProfileMapping : EntityConfiguration<Profile>
{
    public ProfileMapping()
    {
        this.HasKey(p => p.Id);
        this.Property(p => p.FirstName).HasMaxLength(32);
        this.Property(p => p.LastName).HasMaxLength(32);
        this.Property(p => p.PostalCode).HasMaxLength(6);
    }
}

EDIT : Да, передо мной не было VS, но вам нужно добавить следующую строку в UserMapping вместо текущего HasRequired, а также добавить свойство ProfileId (вместо добавленного вами Profile_Id):

this.HasRequired(u => u.Profile).HasConstraint((u, p) => u.ProfileId == p.Id);

В настоящее время я не думаю, что есть способ обойти это, но я уверен, что это изменится, так как мы только в CTP4. Было бы хорошо, если бы я мог сказать:

this.HasRequired(u => u.Profile).WithSingle().Map(
    new StoreForeignKeyName("ProfileId"));

Таким образом, мне не нужно было бы включать ProfileId свойство. Может быть, в настоящее время есть способ обойти это, и мне рано еще думать:).

Также не забудьте позвонить .Include("Profile"), если вы хотите включить «навигационное свойство».

1 голос
/ 01 июля 2016

Возьмите пример следующих сущностей Student и StudentAddress.
Настройка отношения «один к нулю или один» с использованием DataAnnotations:

public class Student
{
    public Student() { }

    public int StudentId { get; set; }
    public string StudentName { get; set; }

    public virtual StudentAddress Address { get; set; }

}

public class StudentAddress 
{
    [ForeignKey("Student")]
    public int StudentAddressId { get; set; }

    public string Address1 { get; set; }
    public string Address2 { get; set; }
    public string City { get; set; }
    public int Zipcode { get; set; }
    public string State { get; set; }
    public string Country { get; set; }

    public virtual Student Student { get; set; }
}

Когда объект StudentAddress не следует соглашениям:

Если, например, сущность StudentAddress не соответствует соглашению для PK, т. Е. Другое имя для свойства Id, то вам также необходимо настроить его для PK. Рассмотрим следующую сущность StudentAddress, которая имеет имя свойства StudentId вместо StudentAddressId.

public class Student
{
    public Student() { }

    public int StudentId { get; set; }
    public string StudentName { get; set; }

    public virtual StudentAddress Address { get; set; }

}

public class StudentAddress 
{
    [Key, ForeignKey("Student")]
    public int StudentId { get; set; }

    public string Address1 { get; set; }
    public string Address2 { get; set; }
    public string City { get; set; }
    public int Zipcode { get; set; }
    public string State { get; set; }
    public string Country { get; set; }

    public virtual Student Student { get; set; }
}

В приведенном выше примере нам необходимо настроить свойство StudentId как Key, а также ForeignKey. Это сделает свойство StudentId в сущности StudentAddress как PK и FK как.

Настройка отношения один-к-одному-одному с использованием Fluent API:
Когда Student и StudentAddress следуют соглашениям: Сущности Student и StudentAddress следуют стандартному соглашению «код в начале» для PrimaryKey. Таким образом, нам не нужно настраивать их для определения их основных ключей. Нам нужно только настроить сущность StudentAddress, где StudentAddressId должен быть ForeignKey.

В следующем примере устанавливаются отношения «один к нулю» или «один» между Student и StudentAddress с использованием Fluent API.

protected override void OnModelCreating(DbModelBuilder modelBuilder)
{

    // Configure Student & StudentAddress entity
    modelBuilder.Entity<Student>()
                .HasOptional(s => s.Address) // Mark Address property optional in Student entity
                .WithRequired(ad => ad.Student); // mark Student property as required in StudentAddress entity. Cannot save StudentAddress without Student

}

В приведенном выше примере сущность Student настраивается с использованием метода HasOptional (), который указывает, что свойство навигации StudentAddress в сущности Student является необязательным (не требуется при сохранении сущности Student). Затем метод WithRequired () настраивает сущность StudentAddress и при необходимости создает свойство Student Student для StudentAddress (требуется при сохранении сущности StudentAddress. Он вызывает исключение, когда сущность StudentAddress сохраняется без свойства навигации Student). Это также сделает StudentAddressId как ForeignKey.

Таким образом, вы можете настроить отношение «один к одному» или «один-один» между двумя сущностями, где сущность Student может быть сохранена без присоединения к ней объекта StudentAddress, но сущность StudentAddress не может быть сохранена без присоединения объекта сущности Student. Это требует одного конца.

Когда объект StudentAddress не следует соглашениям:
Теперь давайте рассмотрим пример объекта StudentAddress, в котором он не следует соглашению с первичным ключом, т. Е. Имя свойства Id отличается от имени Id. Рассмотрим следующие сущности Student и StudentAddress.

public class Student
{
    public Student() { }

    public int StudentId { get; set; }
    public string StudentName { get; set; }

    public virtual StudentAddress Address { get; set; }

}

public class StudentAddress 
{
    public int StudentId { get; set; }

    public string Address1 { get; set; }
    public string Address2 { get; set; }
    public string City { get; set; }
    public int Zipcode { get; set; }
    public string State { get; set; }
    public string Country { get; set; }

    public virtual Student Student { get; set; }
}

Итак, теперь нам нужно настроить свойство StudentId для StudentAddress для PrimaryKey для StudentAddress, а также ForeignKey, как показано ниже.

protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
    // Configure StudentId as PK for StudentAddress
    modelBuilder.Entity<StudentAddress>()
        .HasKey(e => e.StudentId);

    // Configure StudentId as FK for StudentAddress
    modelBuilder.Entity<Student>()
                .HasOptional(s => s.Address) 
                .WithRequired(ad => ad.Student); 

}

Настройка отношений один-к-одному с использованием Fluent API:
Мы можем настроить отношение «один к одному» между сущностями, используя Fluent API, где требуются оба конца, то есть объект сущности Student должен включать в себя объект сущности StudentAddress, а сущность StudentAddress должна включать в себя объект сущности Student, чтобы сохранить его.

Примечание. Отношение один к одному технически невозможно в MS SQL Server. Это всегда будет один к нулю или один. EF формирует отношения один-к-одному с объектами, не находящимися в БД.

protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
    // Configure StudentId as PK for StudentAddress
    modelBuilder.Entity<StudentAddress>()
        .HasKey(e => e.StudentId);

    // Configure StudentId as FK for StudentAddress
    modelBuilder.Entity<Student>()
                .HasRequired(s => s.Address) 
                .WithRequiredPrincipal(ad => ad.Student); 

}

В приведенном выше примере, modelBuilder.Entity (). HasRequired (s => s.Address) делает свойство Address студенческого адреса обязательным. .WithRequiredPrincipal (ad => ad.Student) делает свойство Student объекта StudentAddress соответствующим образом. Таким образом он настраивает оба требуемых конца. Так что теперь, когда вы попытаетесь сохранить сущность Student без адреса или сущность StudentAddress без Student, она выдаст исключение.

Ссылка: http://www.entityframeworktutorial.net/code-first/configure-one-to-one-relationship-in-code-first.aspx

1 голос
/ 15 сентября 2010
public class User
{
    public int Id { get; set; }
    public string Username { get; set; }

    public virtual Profile Profile { get; set; }
}

public class Profile
{
    public int Id { get; set; }

    public int UserID { get; set; }

    public string FirstName { get; set; }
    public string LastName { get; set; }
    public string PostalCode { get; set; }
}

Добавьте виртуальный профиль и идентификатор пользователя, и я думаю, что это поможет вам.

...