AddOrUpdate抛出"无法插入外键值"与一对多的关系

时间:2013-05-14 17:50:50

标签: entity-framework sql-server-ce entity-framework-5

我的应用程序面向.NET 4.5并使用EntityFramework 5.0,Sql Server Compact 4.0。

我正在尝试使用某些实体为我的数据库播种,但它一直在抛出:

“System.Data.SqlServerCe.SqlCeException:无法插入外键值,因为不存在相应的主键值。[外键约束名称= FK_dbo.User_dbo.Account_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();
    }

}

连接字符串:

<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();
    }
}

Account类与User类有一对多的关系。我的种子方法尝试使用默认用户初始化默认帐户。有人会认为这是AddOrUpdate方法的常见用法,但在这种情况下似乎不起作用。如果我只是添加相同的帐户两次,没有用户,它没有任何问题。

有谁能看出我错过了哪一点?

这种简单的关系有什么问题吗?

AddOrUpdate方法是否适用于这种情况?

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

2 个答案:

答案 0 :(得分:0)

这应该让你走上正轨:

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();
}

修改

在执行此操作之前,您可能希望删除并重新创建数据库,因为如果您多次播种数据库并“将同一帐户添加两次,没有用户”,则帐户表中会有多行用户表中没有。

您还可以将以下注释添加到User.Account

[ForeignKey("AccountKey")]
public Account Account { get; set; }

此外,您的导航属性都不是虚拟的原因是什么?

答案 1 :(得分:0)

我使用了以下修改:

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();

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