如何使用索引唯一数据注释对基于2列和一个可空的列进行种子设定

时间:2014-08-09 09:19:30

标签: c# .net sql-server entity-framework entity-framework-6.1

我想创建一个表,其中数据唯一性基于多个列(2或3),但其中一列可以为null。 例如:

FRUIT   WEIGHT  UNIT
Apple
Apple   1       Kg
Apple   2       Kg
Orange
Orange  1       Kg
Orange  2       Kg

将全部视为唯一条目。 可以使用EF 6.1数据注释完成吗? 我相信我实现了这样:

[Required]
[Index("UniqueIndx", 1)]
public string Name { get; set; }
[Index("UniqueIndx", 2)]
public float? Weight { get; set; }
[Index("UniqueIndx", 3, IsUnique = true)]
public FormulUnit? Unit { get; set; }

产生:

public override void Up()
{
    AlterColumn("MyDb.Fruits", "Weight", c => c.Single());
    AlterColumn("MyDb.Fruits", "Unit", c => c.Int());
    CreateIndex("MyDb.Fruits", new[] { "Name", "Weight", "Unit" }, 
                 unique: true, name: "UniqueIndx");
}

根据我对迁移创建的Up方法的理解,唯一性基于所有3列,而不仅仅是我编写该注释的最后一列。这对我来说没问题,这实际上就是我想要的。

我仍然有播种该表的问题。我在AddOrUpdate方法上遇到错误,如:

System.InvalidOperationException: The binary operator Equal is not defined for the types 'System.Nullable`1[System.Single]' and 'System.Single'.

有:

context.Fruits.AddOrUpdate(
    p => new {p.Name, p.Weight, p.Unit}, fr1, fr2, fr3
);

我错过了什么吗? 感谢

2 个答案:

答案 0 :(得分:1)

我刚刚调试了EF source code,抛出错误的代码位于AddOrUpdate内。

以下是导致错误的代码摘录的一部分。

var matchExpression
    = identifyingProperties.Select(
        pi => Expression.Equal(
            Expression.Property(parameter, pi.Single()),
            Expression.Constant(pi.Last().GetValue(entity, null))))

或者你可以通过这样做来复制错误。

var fr1 = new Fruit { Weight = 1 };
var epe = Expression.Property(Expression.Constant(fr1), "Weight");
var ec = Expression.Constant(fr1.Weight);
var ee = Expression.Equal(epe, ec);

我不确定为什么Float?不被Expression.Equal接受,也许其他人可以解释。

但是,如果您可以使用Add或手动检查是否添加或更新,而不是AddOrUpdate,那将会有效。

答案 1 :(得分:0)

更新2:

以下是我认为我的错误,并给出了可能的解决方案。但后来我又错了。虽然下面的代码使用基于3个条件的索引向数据库添加新记录,因为它在进程期间创建了匿名类型的新对象,但是松散了外键关系。 如果你有一个父类,我们称之为拥有许多Fruit的Grocery,种子方法不会用以下给定代码更新这些关系。

回到工作岗位......


我的逻辑错了。

如果我们有基于多个条件的唯一索引,那么如何判断要更新的记录?一个具有三个不同标准的记录可能是一个很好的新记录,或者它可能是一个旧的更新,但这不是编译器可以告诉的。

每条记录都需要手动添加。

谢谢Yuliam。

<强>更新 对于那些陷入同样困境的人来说,这就是我如何解决这种情况(借助其他Stackoverflow帖子)

首先,你必须知道Fruit实体有一个带ID的基本实体,这就是你在这里看不到它的原因。 然后,为了更好地理解这里需要采取的步骤:

  1. 从上下文中获取您的DbSet到一个匿名类型列表,被剥离 来自不需要比较的属性。
  2. 将该匿名类型列表放入强类型列表中 实体类型,在本例中:Fruit)
  3. 创建一个新列表,您只能从数据库中不存在的种子对象中进行选择。
  4. 将所有这些新对象添加到上下文(然后是context.save())

    var SeedFruits = new List {fr1,fr2,fr3};

    var result = from in context.Fruits              选择新的{a.Name,a.Weight,a.Unit};

    列出DBFruitsList = result.AsEnumerable()               。选择(o =&gt;新水果               {                   名称= o.Name,                   重量= o。重量,                   单位= o.Unit               })ToList();

    var UniqueFruitsList =      来自SeedFruits的ai      where!DBFruitsList.Any(x =&gt; x.Name == ai.Name&amp;&amp; x.Weight == ai.Weight&amp;&amp; x.Unit == ai.Unit)      选择ai;

    foreach(UniqueFruitsList中的Fruit fruittoupdate) {     context.Fruits.Add(fruittoupdate); }