Entity framework AddOrUpdate抛出“错误”;“无法插入外键值”;一对多关系

Entity framework AddOrUpdate抛出“错误”;“无法插入外键值”;一对多关系,entity-framework,sql-server-ce,entity-framework-5,Entity Framework,Sql Server Ce,Entity Framework 5,我的应用程序以.NET 4.5为目标,使用EntityFramework 5.0和Sql Server Compact 4.0 我试图在数据库中添加一些实体,但它不断抛出: “System.Data.SqlServerCe.SqlCeException:无法插入外键值,因为相应的主键值不存在。[外键约束名称=FK\U dbo.User\U dbo.Account\U AccountKey]” 以下是我的域实体的简化版本: public class Account { public int

我的应用程序以.NET 4.5为目标,使用EntityFramework 5.0和Sql Server Compact 4.0

我试图在数据库中添加一些实体,但它不断抛出:

“System.Data.SqlServerCe.SqlCeException:无法插入外键值,因为相应的主键值不存在。[外键约束名称=FK\U dbo.User\U dbo.Account\U AccountKey]”

以下是我的域实体的简化版本:

public class Account
{
    public int AccountKey { get; set; }
    public string Name { get; set; }

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

internal class AccountMap : EntityTypeConfiguration<Account>
{
    public AccountMap()
    {
        this.HasKey(e => e.AccountKey);
        this.Property(e => e.AccountKey).HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity);
        this.Property(e => e.Name).IsRequired();
    }
}


public class User
{
    public int UserKey { get; set; }
    public string Name { get; set; }

    public Account Account { get; set; }
    public int AccountKey { get; set; }
}

internal class UserMap : EntityTypeConfiguration<User>
{
    public UserMap()
    {
        this.HasKey(e => e.UserKey);
        this.Property(e => e.UserKey).HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity);
        this.Property(e => e.Name).IsRequired();


        this.HasRequired(e => e.Account)
            .WithMany(e => e.Users)
            .HasForeignKey(e => e.AccountKey);
    }
}

public class TestContext : DbContext
{
    public TestContext()
    {
        this.Configuration.LazyLoadingEnabled = false;
    }

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

    public DbSet<Account> Accounts { get; set; }



    protected override void OnModelCreating(DbModelBuilder modelBuilder)
    {
        modelBuilder.Conventions.Remove<PluralizingTableNameConvention>(); modelBuilder.Conventions.Remove<StoreGeneratedIdentityKeyConvention>();

        modelBuilder.LoadConfigurations();
    }

}
公共类帐户
{
public int AccountKey{get;set;}
公共字符串名称{get;set;}
公共ICollection用户{get;set;}
}
内部类AccountMap:EntityTypeConfiguration
{
公共帐户映射()
{
this.HasKey(e=>e.AccountKey);
this.Property(e=>e.AccountKey).HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity);
this.Property(e=>e.Name).IsRequired();
}
}
公共类用户
{
公共int用户密钥{get;set;}
公共字符串名称{get;set;}
公共帐户{get;set;}
public int AccountKey{get;set;}
}
内部类UserMap:EntityTypeConfiguration
{
公共用户映射()
{
this.HasKey(e=>e.UserKey);
this.Property(e=>e.UserKey).HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity);
this.Property(e=>e.Name).IsRequired();
this.HasRequired(e=>e.Account)
.WithMany(e=>e.Users)
.HasForeignKey(e=>e.AccountKey);
}
}
公共类TestContext:DbContext
{
公共TestContext()
{
this.Configuration.LazyLoadingEnabled=false;
}
公共数据库集用户{get;set;}
公共数据库集帐户{get;set;}
模型创建时受保护的覆盖无效(DbModelBuilder modelBuilder)
{
modelBuilder.Conventions.Remove();modelBuilder.Conventions.Remove();
LoadConfigurations();
}
}
连接字符串:

<connectionStrings>
    <add name="TestContext" connectionString="Data Source=|DataDirectory|\TestDb.sdf;" providerName="System.Data.SqlServerCe.4.0" />
  </connectionStrings>

下面是一个示例应用程序,它显示了问题:

class Program
{
    static void Main(string[] args)
    {
        try
        {
            Database.SetInitializer(new DropCreateDatabaseIfModelChanges<TestContext>());
            using (var context = new TestContext())
                context.Database.Initialize(false);

            for (int i = 0; i < 2; i++ )
                using (var context = new TestContext())
                {
                    var account1 = new Account()
                    {
                        Name = "Account1"
                    };

                    var user1 = new User()
                    {
                        Name = "User1",
                        Account = account1
                    };

                    context.Accounts.AddOrUpdate(
                        e => e.Name,
                        account1
                    );

                    context.Users.AddOrUpdate(
                        e => e.Name,
                        user1
                    );

                    context.SaveChanges();

                    Console.WriteLine("\nChanges saved.");
                }
        }
        catch (Exception e)
        {
            Console.WriteLine(e.ToString());
        }

        Console.WriteLine("\nPress any key to exit...");
        Console.ReadLine();
    }
}
类程序
{
静态void Main(字符串[]参数)
{
尝试
{
SetInitializer(新的DropCreateDatabaseIfModelChanges());
使用(var context=newtestcontext())
context.Database.Initialize(false);
对于(int i=0;i<2;i++)
使用(var context=newtestcontext())
{
var account1=新帐户()
{
Name=“Account1”
};
var user1=新用户()
{
Name=“User1”,
Account=account1
};
context.Accounts.AddOrUpdate(
e=>e.Name,
帐户1
);
context.Users.AddOrUpdate(
e=>e.Name,
用户1
);
SaveChanges();
Console.WriteLine(“\n已保存更改”);
}
}
捕获(例外e)
{
Console.WriteLine(如ToString());
}
Console.WriteLine(“\n按任意键退出…”);
Console.ReadLine();
}
}
Account类与User类具有一对多关系。我的种子方法尝试使用默认用户初始化默认帐户。有人会认为这是AddOrUpdate方法的常见用法,但在这种情况下它似乎不起作用。如果我只添加同一个帐户两次,没有用户,它的工作没有问题

有人知道我遗漏了什么吗

这种简单的关系有什么不对吗

AddOrUpdate方法是为在这种情况下工作而设计的吗


如果没有,完成这种播种的正确方法是什么?

这将使您步入正轨:

using (var context = new TestContext())
{
    // with 1:many we don't need to specify PK when seeding, EF will pick this up
    var accounts = new List<Account>
    {
        // new a list of type User for each account
        new Account() { Name = "Account1", Users = new List<User>() },
        new Account() { Name = "Account2", Users = new List<User>() }
    };
    accounts.ForEach(a => context.Accounts.AddOrUpdate(a));
    context.SaveChanges();

    var users = new List<User>
    {
        // set the account key for each User
        new User() { Name = "User1", AccountKey = 1 },
        new User() { Name = "User2", AccountKey = 1 },
        new User() { Name = "User3", AccountKey = 2 },
        new User() { Name = "User4", AccountKey = 2 }
    };
    users.ForEach(u => context.Users.AddOrUpdate(u));
    context.SaveChanges();

    // add users to list of type user in an account
    accounts[0].Users.Add(users[0]);
    accounts[0].Users.Add(users[1]);
    accounts[1].Users.Add(users[2]);
    accounts[1].Users.Add(users[3]);
    context.SaveChanges();
}

另外,您的导航属性没有虚拟属性的原因是什么?

我对其进行了以下修改:

var account1 = new Account()
{
    Name = "Account1"
};

var user1 = new User()
{
    Name = "User1",
    //Account = account1
};

account1.Users = new List<User>(new[]{user1});

context.Accounts.AddOrUpdate(
    e => e.Name,
    account1
);

context.SaveChanges();
var account1=新帐户()
{
Name=“Account1”
};
var user1=新用户()
{
Name=“User1”,
//Account=account1
};
account1.Users=新列表(new[]{user1});
context.Accounts.AddOrUpdate(
e=>e.Name,
帐户1
);
SaveChanges();

然而,我不知道为什么会这样。我仍然认为第一次尝试是有意义的,应该不会出现任何问题。

我不使用属性,因为我喜欢使用sintax而不是它们的配置,并且您建议的配置应该与UserMap中的“.HasForeignKey(e=>e.AccountKey)”的行为基本相同。我的导航属性不是虚拟的,因为我不需要/不想支持延迟加载。我同意使用Fluent API也应该有同样的影响-您可以尝试使用注释而不是Fluent API来查看您的Fluent配置是否导致了问题。当您尝试使用我提供的示例时,是否遇到了相同的错误?您的示例将不起作用,因为您拒绝AddOrUpdate方法的目的,让它通过id验证实体的存在(当您没有指定标识属性时是默认方法),而您没有id。在我的示例中,您的方法将创建四个帐户,两个名为“Account1”,另外两个名为“Account2”。此外,您假设创建了帐户密钥(1,2),但您不能这样做,因为种子可能会在现有数据库的迁移中使用。它非常适用于数据库的首次种子设定,从您在问题中提供的信息量来看,这似乎就是您想要做的。我的示例不会创建四个帐户,您是否看到它在一个将迭代两次的循环中?为了便于说明,我加了一个额外的帐户。注意我是怎么说的“这应该
var account1 = new Account()
{
    Name = "Account1"
};

var user1 = new User()
{
    Name = "User1",
    //Account = account1
};

account1.Users = new List<User>(new[]{user1});

context.Accounts.AddOrUpdate(
    e => e.Name,
    account1
);

context.SaveChanges();