Настройка необязательно: обязательные отношения Entity Framework - свободный API - PullRequest
3 голосов
/ 24 ноября 2011

У меня есть отношение USER - PROFILE, где пользователь может (необязательно) иметь связанный профиль.В базе данных таблица PROFILES имеет FK для таблицы USERS (FK обнуляется).

Я хочу настроить это с помощью EF fluent API.Мне удалось это сделать, и все работает нормально, но проблема в том, что я не могу получить FK из профиля в качестве свойства в классах.

Так что в БД у меня есть:

[USERS]
ID uniqueidentifier PK not null

[PROFILES]
ID uniqueidentifier PK not null
USERID uniqueidentifer FK null

И в коде у меня есть:

public class User { public virtual Profile { get; set; } }
public class Profile { public virtual User { get; set; } }

Обратите внимание, что имена столбцов в БД не соответствуют соглашению об именовании EF.

Я могу настроить EF следующим образом:

HasOptional(u => u.Profile).WithRequired(p => p.User).Map(m => m.MapKey("USERID")).WillCascadeOnDelete();

или наоборот:

HasRequired(p => p.User).WithOptional(u => u.Profile).Map(m => m.MapKey("USERID")).WillCascadeOnDelete();

Мне действительно нужно в этом случае отобразить FK с помощью MapKey (из-за соглашения об именах, которое использует EF), и, похоже, это проблемане позволю мне добавить FK в модель.

Так что я не могу иметь:

public class Profile { public virtual User { get; set; } public Guid? UsreId { get; set; } }

У кого-нибудь есть идеи, как мне это сделать?

РЕДАКТИРОВАТЬ Источником проблемы является тот факт, что если я не использую метод .Map (m => m.MapKey ("USERID")), соглашение нарушает отношения (он ожидает, что FK равен User_Id), чтов основном это как использовать его без каких-либо параметров.Если я использую метод .Map (m => m.MapKey ("USERID")), то он говорит, что у меня уже есть UserId в качестве свойства в модели (из встроенной справки: настройка отношения для использования свойства внешнего ключа)что не представлены в объектной модели . Столбцы и таблицу можно настроить, указав действие конфигурации. Если указано пустое действие конфигурации, будут сгенерированы имена столбцов по соглашению ).

Облом ... ... 1034 *

РЕДАКТИРОВАТЬ 2 Удалось как-то это исправить (в основном для другого подхода).Изменили таблицы на:

[USERS]
ID uniqueidentifier PK not null

[PROFILES]
-- ID uniqueidentifier PK not null (removed the PK)
USERID uniqueidentifer PK not null, FK not null -- (the PK is also the FK)

Я попал сюда после запуска профилировщика SQL и просмотра запущенных там запросов.Опция PROFILE для каждого пользователя запрашивалась на основе USERID.Предложение were было примерно таким:

SELECT *
FROM PROFILES
WHERE ID = @userId -- ID was the PK

Итак, я выяснил, что в этом случае PK из таблицы PROFILES также должен быть FK, и сопоставления могут быть простыми:

HasOptional(u => u.Profile).WithRequired(p => p.User);

или на другом конце

HasRequired(p => p.User).WithOptional(u => u.Profile);

И этот был полезным: http://msdn.microsoft.com/en-us/library/hh295843%28v=vs.103%29.aspx

Ура!

1 Ответ

2 голосов
/ 24 ноября 2011

Вам нужно сосредоточиться на правильном отображении самих сущностей, и тогда отношения будут просто работать. Соглашения о ключах - что-то вроде:

  1. Если у объекта есть свойство с именем Id, оно будет использовано в качестве ключа
  2. Если есть свойство EntityId (например, UserId), то это ключ
  3. В противном случае необходимо указать, какое свойство ключа имеет значение

Что касается того, какому столбцу соответствует это ключевое свойство в базе данных, вы можете указать это, используя методы Property () и HasColumnName ().

Как только это будет сделано, вам не нужно беспокоиться о сопоставлении ключей в сопоставлении отношений (по крайней мере, в вашем случае сопоставления один к одному).

Что касается отображения отношений, вам не нужно определять отношения как пользователем, так и из профиля. Как только вы определяете отношения одним способом, получается другой путь. Это также поддерживает ваш код в чистоте.

Код ниже должен помочь вам понять все это.

class Program
{
    static void Main(string[] args)
    {
        // replace your connection string here
        using (var userContext = new UserContext("UserProfile"))
        {
            var u = new User { UserId = Guid.NewGuid(), Name = "John", Profile = new Profile() { ProfileId = Guid.NewGuid(), SomeValue = "x" } };

            userContext.Users.Add(u);
            userContext.SaveChanges();

            var users = userContext.Users.ToList();

            foreach (var user in users)
            {
                Console.WriteLine(user.Name);
                if (user.Profile != null)
                {
                    Console.WriteLine(user.Profile.ProfileId);
                }
            }

            var profiles = userContext.Profiles.Where(p => p.User.Name == "John").ToList();
        }
    }
}

public class UserContext : DbContext
{
    public UserContext(string connectionString)
        : base(connectionString)
    {
    }

    public DbSet<User> Users { get; set; }

    public DbSet<Profile> Profiles { get; set; }

    protected override void OnModelCreating(DbModelBuilder modelBuilder)
    {
        base.OnModelCreating(modelBuilder);

        modelBuilder.Entity<User>().Property(u => u.UserId).HasColumnName("ID");
        modelBuilder.Entity<Profile>().Property(p => p.ProfileId).HasColumnName("ID");

        modelBuilder.Entity<User>().HasOptional(u => u.Profile).WithRequired(p => p.User);
    }
}

public class User
{
    public Guid UserId { get; set; }

    public string Name { get; set; }

    public Profile Profile { get; set; }
}

public class Profile
{
    public Guid ProfileId { get; set; }

    public User User { get; set; }

    public string SomeValue { get; set; }
}
...