多重约束违反SQL Server 2008 - CodeFirst [英] Multiplicity constraint violated SQL Server 2008 - CodeFirst

查看:102
本文介绍了多重约束违反SQL Server 2008 - CodeFirst的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在努力解决一个非常乏味的问题。
我有一个名为Nation的类和一个名为NationAlly的类

  public class Nation 
{
public int ID {get; set;}
public int name {get; set;}
public List< NationAlly> NationAllies {get; set;}
}

public class NationAlly
{
public int ID {get; set;}
public int level {get; set;}
public Nation toNation {get; set;}
}

我正在使用名为NationsDB的DbContext的EF 4和CodeFirst来管理SQL Server 2008上的数据库。
如果我创建一个类型为Nation的新对象尝试调用countriesDB.SaveChanges,我得到以下异常:



违反多重约束。关系CodeFirstNamespace.NationAlly_toNation的角色NationAlly_toNation_Target具有多重性1或0..1。



我尝试使用NationAllies字段保存一个Nation字段为null,并且不会抛出此异常,数据库中的国家表获取所有正确的值。



在我的数据库中,表国家有2个字段:ID(主键),名称
表NationAlly有3个字段:ID(主键),级别,NationID
这两个表与NationAlly.NationID是外键的关系链接,Nation.ID是主键。



不奇怪?在我的眼中,表NationAlly应该有一个称为NationID1的字段,另一个称为NationID2,以创建一个国家和其他国家的列表之间的关系。



做错了?

解决方案

您可能是EF Code-First映射约定的受害者,它们自动创建 NationAllies toNation 你不想拥有。



如果我正确理解你(但我不是100%确定,如果我这样做),你实际上想要有两个关系,并且你已经暴露了每个实体的关系的一端。因此, NationAllies 不指向 toNation ,而是指向中的隐形所有者国家NationAlly 实体。



如果是这种情况,您需要显式覆盖约定映射。在EF 4.1的Fluent API中,可能如下所示:

  public class MyContext:DbContext 
{
public DbSet< Nation>国家{get;组; }
public DbSet< NationAlly> NationAllies {get;组;

protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity< Nation>()
.HasMany(n => n.NationAllies)
.WithRequired()
.Map(conf => conf.MapKey(OwnerID))
.WillCascadeOnDelete(false);

modelBuilder.Entity< NationAlly>()
.HasRequired(a => a.toNation)
.WithMany()
.Map(conf => conf.MapKey(NationID))
.WillCascadeOnDelete(false);
}
}

此映射将创建两个外键 OwnerID NationID NationAllies 表中,都指向主键



以下是我测试过的应用程序:




  • 在VS2010 / .NET 4.0中创建一个新的控制台应用程序,将其命名为NationsApp

  • 添加对EntityFramework.dll的引用

  • 清除Program.cs的内容,并粘贴到以下内容中:



Program.cs的内容:

 使用系统; 
使用System.Collections.Generic;
使用System.Data.Entity;

namespace NationsApp
{
public class Nation
{
public int ID {get;组; }
public int name {get;组; }
public List< NationAlly> NationAllies {get;组; }
}

public class NationAlly
{
public int ID {get;组; }
public int level {get;组; }
public Nation toNation {get;组; }
}

public class NationsContext:DbContext
{
public DbSet< Nation>国家{get;组; }
public DbSet< NationAlly> NationAllies {get;组;

protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity< Nation>()
.HasMany(n => n.NationAllies)
.WithRequired()
.Map(conf => conf.MapKey(OwnerID))
.WillCascadeOnDelete(false);

modelBuilder.Entity< NationAlly>()
.HasRequired(a => a.toNation)
.WithMany()
.Map(conf => conf.MapKey(NationID))
.WillCascadeOnDelete(false);
}
}

类程序
{
static void Main(string [] args)
{
using context = new NationsContext())
{
try
{
//我们有三个国家和两个盟友
国家国家1 =新的国家(){
NationAllies = new List< NationAlly>()};
Nation nation2 = new Nation(){
NationAllies = new List< NationAlly>()};
Nation nation3 = new Nation(){
NationAllies = new List< NationAlly>()};
NationAlly ally1 = new NationAlly();
NationAlly ally2 = new NationAlly();

// Nation1有两个盟友
//(Nation1是两个盟国的所有者)
nation1.NationAllies.Add(ally1);
nation1.NationAllies.Add(ally2);

// tonation of ally1指的是Nation2
ally1.toNation = nation2;
// tonation of ally2指的是Nation3
ally2.toNation = nation3;

context.Nations.Add(nation1);
context.Nations.Add(nation2);
context.Nations.Add(nation3);

context.SaveChanges();
}
catch(异常e)
{
throw;
}
}
}
}
}

您可以在throw中设置断点,以便在调试器中查看e中的可能异常。



这将创建一个名为 NationsApp的数据库.NationsContext 如果您使用的是SQL Server Express,并且没有任何进一步的连接字符串定义。



它提供两个关系 Nation_NationAllies (FK是OwnerID)和 NationAlly_toNation (FK是NationID)。所有列都不可为空。 DB中的结果如下:




I'm working to solve a very tedious problem. I have a class called Nation and a class called NationAlly

public class Nation   
{
    public int ID {get; set;}
    public int name {get;set;}
    public List<NationAlly> NationAllies {get;set;}
}

public class NationAlly
{
    public int ID {get; set;}
    public int level {get;set;}
    public Nation toNation {get;set;}
}

I'm using EF 4 and CodeFirst with a DbContext called NationsDB to manage my database on SQL Server 2008. If I create a new object of type Nation and I try to call nationsDB.SaveChanges, I got the following exception:

"Multiplicity constraint violated. The role 'NationAlly_toNation_Target' of the relationship 'CodeFirstNamespace.NationAlly_toNation' has multiplicity 1 or 0..1."

I tried to save a Nation with NationAllies field null and this exception is not thrown, the nation table in the database gets all the correct values.

In my database the table Nation has 2 fields: ID(primary key), name The table NationAlly has 3 fields: ID(primary key), level, NationID The two tables are linked with a relationship where NationAlly.NationID is foreign key and Nation.ID is primary key.

Isn't strange? In my eyes the table NationAlly should have a field called NationID1 and another called NationID2 to create the "relationship" between a nation and a list of other nations.

What did I do wrong?

解决方案

You are perhaps a victim of the EF Code-First mapping conventions which create automatically a relationship between NationAllies and toNation you don't want to have.

If I understand you correctly (but I am not 100 percent sure, if I do), you actually want to have two relationships and you have exposed only one end of the relationship in each of the entities. So, NationAllies does NOT point to toNation but to an "invisible" Owner nation in your NationAlly entity.

If that is the case you need to explicitly overwrite the convention mappings. In the Fluent API of EF 4.1 this could look like:

public class MyContext : DbContext
{
    public DbSet<Nation> Nations { get; set; }
    public DbSet<NationAlly> NationAllies { get; set; }

    protected override void OnModelCreating(DbModelBuilder modelBuilder)
    {
        modelBuilder.Entity<Nation>()
            .HasMany(n => n.NationAllies)
            .WithRequired()
            .Map(conf => conf.MapKey("OwnerID"))
            .WillCascadeOnDelete(false);

        modelBuilder.Entity<NationAlly>()
            .HasRequired(a => a.toNation)
            .WithMany()
            .Map(conf => conf.MapKey("NationID"))
            .WillCascadeOnDelete(false);
    }
}

This mapping would create the two foreign keys OwnerID and NationID in the NationAllies table, both pointing to the primary key ID in the Nations table.

Edit

Here is the application I have tested with:

  • Create a new Console App in VS2010 / .NET 4.0, name it "NationsApp"
  • Add a reference to "EntityFramework.dll"
  • Clear the content of "Program.cs" and paste instead the following in:

Content of Program.cs:

using System;
using System.Collections.Generic;
using System.Data.Entity;

namespace NationsApp
{
    public class Nation
    {
        public int ID { get; set; }
        public int name { get; set; }
        public List<NationAlly> NationAllies { get; set; }
    }

    public class NationAlly
    {
        public int ID { get; set; }
        public int level { get; set; }
        public Nation toNation { get; set; }
    }

    public class NationsContext : DbContext
    {
        public DbSet<Nation> Nations { get; set; }
        public DbSet<NationAlly> NationAllies { get; set; }

        protected override void OnModelCreating(DbModelBuilder modelBuilder)
        {
            modelBuilder.Entity<Nation>()
                .HasMany(n => n.NationAllies)
                .WithRequired()
                .Map(conf => conf.MapKey("OwnerID"))
                .WillCascadeOnDelete(false);

            modelBuilder.Entity<NationAlly>()
                .HasRequired(a => a.toNation)
                .WithMany()
                .Map(conf => conf.MapKey("NationID"))
                .WillCascadeOnDelete(false);
        }
    }

    class Program
    {
        static void Main(string[] args)
        {
            using (var context = new NationsContext())
            {
                try
                {
                    // We have three Nations and two Allies
                    Nation nation1 = new Nation() {
                        NationAllies = new List<NationAlly>() };
                    Nation nation2 = new Nation() {
                        NationAllies = new List<NationAlly>() };
                    Nation nation3 = new Nation() {
                        NationAllies = new List<NationAlly>() };
                    NationAlly ally1 = new NationAlly();
                    NationAlly ally2 = new NationAlly();

                    // Nation1 has two Allies
                    // (Nation1 is the "owner" of both Allies)
                    nation1.NationAllies.Add(ally1);
                    nation1.NationAllies.Add(ally2);

                    // toNation of ally1 refers to Nation2
                    ally1.toNation = nation2;
                    // toNation of ally2 refers to Nation3
                    ally2.toNation = nation3;

                    context.Nations.Add(nation1);
                    context.Nations.Add(nation2);
                    context.Nations.Add(nation3);

                    context.SaveChanges();
                }
                catch (Exception e)
                {
                    throw;
                }
            }
        }
    }
}

You can set a breakpoint on "throw" to watch possible exceptions in e in the debugger.

This creates a database called NationsApp.NationsContext if you are using SQL Server Express and don't have any further connection strings defined.

It gives two relationships Nation_NationAllies (FK is "OwnerID") and NationAlly_toNation (FK is "NationID"). All columns are non-nullable. The result in the DB is the following:

这篇关于多重约束违反SQL Server 2008 - CodeFirst的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆