EF 4.1 の Code First でデータベースを生成する単純なクラスのペアがあります。
public class User
{
public int UserId { get; set; }
public string UserName { get; set; }
}
public class Purchase
{
public int PurchaseId { get; set; }
public int UserId { get; set; }
public int? SalespersonUserId { get; set; }
public User User { get; set; }
public User SalespersonUser { get; set; }
}
public class NewItemsDataContext : DbContext
{
public DbSet<User> Users { get; set; }
public DbSet<Purchase> Purchases { get; set; }
}
私のプログラムでは、いくつかのデータを作成して書き込みます。
class Program
{
static void Main(string[] args)
{
Database.SetInitializer<NewItemsDataContext>(new DropCreateDatabaseIfModelChanges<NewItemsDataContext>());
using (NewItemsDataContext sadc = new NewItemsDataContext())
{
sadc.Users.Add(new User());
sadc.SaveChanges();
}
using (NewItemsDataContext sadc = new NewItemsDataContext())
{
sadc.Purchases.Add(new Purchase() { UserId = 1 });
sadc.SaveChanges();
}
using (NewItemsDataContext sadc = new NewItemsDataContext())
{
var sql = sadc.Purchases.Include(p => p.User);
foreach (Purchase purchase in sql)
Console.WriteLine(purchase.User.UserId.ToString());
}
}
}
Purchase レコードを読み返すと、 purchase.User が null であるという例外が発生することに注意してください。つまり、.Include はユーザーに対して何も取得しませんでした。ここで、OnModelCreating の salespersonUser ナビゲーション プロパティを無視すると (または単にコメントアウトすると):
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<Purchase>().Ignore(p => p.SalespersonUser);
base.OnModelCreating(modelBuilder);
}
コードは機能し、User は .Include にロードされます。
SalespersonUser nav プロパティを無視すると、作成されたデータベースは期待どおりに見えます。Purchase テーブルには PurchaseId、UserId、および SalespersonId があります。しかし、SalespersonUser nav プロパティを再度追加すると (無視しないでください)、テーブルには User_UserId と SalespersonUser_UserId (および元の UserId と SalespersonUserId) の 2 つのキーが追加されます。
また、生成された SQL は、問題が発生した場所を明確に示しています。
nav プロパティなし:
{SELECT
[Extent1].[PurchaseId] AS [PurchaseId],
[Extent1].[UserId] AS [UserId],
[Extent1].[SalespersonUserId] AS [SalespersonUserId],
[Extent2].[UserId] AS [UserId1],
[Extent2].[UserName] AS [UserName]
FROM [Purchases] AS [Extent1]
INNER JOIN [Users] AS [Extent2] ON [Extent1].[UserId] = [Extent2].[UserId]}
そして nav プロパティを使って:
{SELECT
[Extent1].[PurchaseId] AS [PurchaseId],
[Extent1].[UserId] AS [UserId],
[Extent1].[SalespersonUserId] AS [SalespersonUserId],
[Extent2].[UserId] AS [UserId1],
[Extent2].[UserName] AS [UserName],
[Extent1].[SalespersonUser_UserId] AS [SalespersonUser_UserId]
FROM [Purchases] AS [Extent1]
LEFT OUTER JOIN [Users] AS [Extent2] ON [Extent1].[User_UserId] = [Extent2].[UserId]}
UserId をプルする方法に注目してください。ただし、User_UserId と結合し、左結合も同様です。SalespersonUserId は結合でも参照されません。レコードを書き込んだ後のデータベース内では、UserId が設定されていますが、User_UserID は null です。したがって、参加するものはなく、Included User に対して null を取得します。
これは EF のバグのように思えますが、設計上の理由がある可能性があります。もしそうなら、誰かが私のためにそれを片付けて、おそらくそれを回避するかもしれないいくつかの流暢なAPIについて説明できますか? 私は自分のナビゲーション プロパティに少し偏っています。