Я пытался отобразить однонаправленные отношения "один ко многим", используя код EF 4.1, например, сначала. У пользователя есть адрес, но адрес ничего не знает о пользователе. Это легко реализовать с помощью атрибута ForeignKey или свободного API (показано во включенном коде).
Проблема возникает при добавлении столбца Version (byte []) с атрибутом Timestamp в обоих сопоставленных классах. Если мы сейчас создадим экземпляр User, который имеет ссылку на существующий (в БД) Address и добавим его в контекст, при вызове SaveChanges профилировщик базы данных покажет два запроса к базе данных, один из которых - вставка User, а другой - обновление таблицы адресов для изменения версии. Не то, что я хочу. Если я не смоделировал никаких отношений в своем домене, то я не хочу также менять версию. Я хочу изменить версию только в случае изменения адреса экземпляра.
Я подозреваю, что, поскольку для отображения используется HasMany()
, EF DbContext внутренне считает, что существует коллекция, которую необходимо удовлетворить, и, поскольку коллекция изменилась (добавив нового пользователя), она автоматически обновляет версию Address. Все это, несмотря на то, что Address не имеет свойства коллекции типа ICollection<User>
.
Итак, на мой вопрос. Какое сопоставление мне нужно установить, чтобы отношения поддерживали структуры классов без изменения версии Address при добавлении нового пользователя?
EDIT:
Я обнаружил, что единственный способ предотвратить обновление версии в Address - это уменьшить отображение только на HasRequired (a => a.Address) и больше не иметь внешнего ключа AddressId в классе User. Похоже, если «свойство» внешнего ключа установлено на пользователя, явное или условное сопоставление обеспечит обновление версии Address.
Я бы предпочел применить какое-либо расширение к HasRequired, чтобы сообщить контексту, как обрабатывать отношения, вместо того, чтобы полностью удалять свойство внешнего ключа.
Вот код, который я использую для демонстрации проблемы:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.ComponentModel.DataAnnotations;
using System.Data.Entity;
namespace DbTest
{
class Program
{
static void Main(string[] args)
{
Address address = null;
// Make sure we have one address to test with
using (var context = new DemoContext())
{
address = context.Addresses.FirstOrDefault();
if (address == null)
{
address = new Address { Street = "My Street" };
context.Addresses.Add(address);
context.SaveChanges();
}
}
byte[] version = address.Version;
using (var context = new DemoContext())
{
// Uncomment to test attaching
// context.Addresses.Attach(address);
address = context.Addresses.FirstOrDefault();
var user = new User { Name = "Mark", Address = address };
context.Users.Add(user);
context.SaveChanges(); // Results in new user inserted and a version update to the Address referenced object
}
using (var context = new DemoContext())
{
var address2 = context.Addresses.FirstOrDefault();
Console.WriteLine("Versions: {0}, {1}", BitConverter.ToString(version), BitConverter.ToString(address2.Version));
}
Console.ReadLine();
}
}
public class User
{
[Key]
public int UserId { get; set; }
public string Name { get; set; }
public int AddressId { get; set; }
// [ForeignKey("AddressId")]
public Address Address { get; set; }
[Timestamp]
public byte[] Version { get; set; }
}
public class Address
{
[Key]
public int AddressId { get; set; }
public string Street { get; set; }
[Timestamp]
public byte[] Version { get; set; }
}
public class DemoContext : DbContext
{
public DbSet<User> Users { get; set; }
public DbSet<Address> Addresses { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<User>()
.HasRequired(a => a.Address)
.WithMany()
.HasForeignKey(u => u.AddressId);
}
}
}