Как заполнить таблицу с аннотацией уникальных данных индекса на основе 2 столбцов с одним нулевым значением

Я хочу создать таблицу, в которой уникальность данных основана на нескольких столбцах (2 или 3), но один из них может быть нулевым. Например:

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, созданный миграцией, заключается в том, что уникальность основана на всех трех столбцах, а не только на последнем, к которому я написал эту аннотацию. Это нормально для меня, это на самом деле то, что я хочу.

У меня все еще есть проблема с заполнением этой таблицы. Я получаю ошибки в методе 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
);

Я что-то пропустил? Спасибо


person Bogac    schedule 09.08.2014    source источник
comment
что такое FormulUnit?   -  person Yuliam Chandra    schedule 09.08.2014
comment
stackoverflow .com/questions/12570634/ ?   -  person Eugene Podskal    schedule 09.08.2014
comment
FormulUnit — это перечисление.   -  person Bogac    schedule 09.08.2014
comment
Что такое первичный ключ Fruit ?   -  person Yuliam Chandra    schedule 09.08.2014
comment
Первичный ключ для Fruit находится в базовом классе, от которого наследуют Fruit и другие объекты.   -  person Bogac    schedule 09.08.2014


Ответы (2)


Я только что отладил 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, это сработает.

person Yuliam Chandra    schedule 09.08.2014
comment
Я думал, что ошибался, пытаясь использовать AddOrUpdate, но позже столкнулся с другой проблемой, не используя AddOrUpdate. Я очень беспокоюсь сейчас. Я просто отменю все аннотации индекса из своей сущности. - person Bogac; 11.08.2014

ОБНОВЛЕНИЕ 2:

Ниже приведено то, что я считал своей ошибкой и дал возможное решение. Но опять же я ошибся. Хотя приведенный ниже код добавляет новые записи в базу данных с индексом на основе 3 критериев, поскольку он создает новые объекты анонимного типа во время процесса, вы теряете отношения внешнего ключа. Если у вас есть родительский класс, назовем его Grocery, который содержит много фруктов, метод seed не будет обновлять эти отношения с помощью приведенного ниже кода.

Снова на работу...


Моя логика неверна.

Если у нас есть уникальный индекс, основанный на нескольких критериях, как определить, какую запись нужно обновить? Запись с одним другим критерием из 3 вполне может быть новой записью или, может быть, старой записью, которую нужно обновить, но это не то, что компилятор может сказать.

Каждую запись нужно добавлять вручную.

Спасибо Юлиам.

ОБНОВЛЕНИЕ: Для тех из вас, кто сталкивается с той же дилеммой, вот как я решил эту ситуацию (с помощью других сообщений Stackoverflow)

Во-первых, вы должны знать, что сущность Fruit имеет базовую сущность с идентификатором, поэтому вы не видите ее здесь. Затем, чтобы лучше понять, необходимо предпринять следующие шаги:

  1. Получите свой DbSet из контекста в список анонимных типов, удалив свойства, которые не относятся к сравнению.
  2. Поместите этот список анонимных типов в строго типизированный список (типа вашего объекта, в этом примере: Fruit)
  3. Создайте новый список, в котором вы выбираете только из исходных объектов, которых нет в базе данных.
  4. Добавьте все эти новые объекты в контекст (а затем context.save() )

    var SeedFruits = новый список {fr1, fr2, fr3};

    var result = from a in context.Fruits select new { a.Name, a.Weight, a.Unit};

    List DBFruitsList = result.AsEnumerable() .Select(o => new Fruit { Name = o.Name, Weight = o.Weight, Unit = o.Unit }).ToList();

    var UniqueFruitsList = from ai в SeedFruits, где !DBFruitsList.Any(x => x.Name == ai.Name && x.Weight == ai.Weight && x.Unit == ai.Unit) select ai;

    foreach (обновление фруктов в UniqueFruitsList) { context.Fruits.Add (обновление фруктов); }

person Bogac    schedule 09.08.2014