EF 4: ссылка на нескалярные переменные не поддерживается - PullRequest
2 голосов
/ 17 февраля 2011

Сначала я использую код и пытаюсь выполнить простой запрос к свойству List, чтобы проверить, содержит ли оно строку в списке фильтрации.Однако я сталкиваюсь с проблемами.Для простоты предположим следующее.

public class Person
{
   public List<string> FavoriteColors { get; set; }
}

//Now some code. Create and add to DbContext
var person = new Person{ FavoriteColors = new List<string>{ "Green", "Blue"} };
dbContext.Persons.Add(person);
myDataBaseContext.SaveChanges();

//Build 
var filterBy = new List<string>{ "Purple", "Green" };
var matches = dbContext.Persons.AsQueryable();
matches = from p in matches
          from color in p.FavoriteColors 
          where filterBy.Contains(color)
          select p;

Опция, которую я рассматриваю, заключается в преобразовании ее в сериализованную строку json, поскольку я могу выполнить вызов Contains, если FavoriteColors - строка.В качестве альтернативы, я могу пойти за борт и создать «цветную» сущность, но это довольно тяжелый вес.К сожалению, перечисления также не поддерживаются.

Ответы [ 2 ]

1 голос
/ 18 февраля 2011

Я думаю, что проблема не в коллекции, а в ссылке на matches.

var matches = dbContext.Persons.AsQueryable();
matches = from p in matches
          from color in p.FavoriteColors 
          where filterBy.Contains(color)
          select p;

Если вы посмотрите Известные проблемы и замечания для EF4 , это больше илименее точно упомянутый случай.

Ссылка на нескалярные переменные, такие как сущность, в запросе не поддерживается.Когда такой запрос выполняется, генерируется исключение NotSupportedException с сообщением, в котором говорится «Невозможно создать постоянное значение типа EntityType.

Также обратите внимание, что в нем конкретно говорится, что он ссылается на набор скалярных переменных поддерживается (это ново в EF 4 imo).

Сказав, что должно работать следующее (не могу попробовать прямо сейчас):

matches = from p in dbContext.Persons
          from color in p.FavoriteColors 
          where filterBy.Contains(color)
          select p;
0 голосов
/ 19 февраля 2011

Я решил поэкспериментировать, создав класс «StringEntity», чтобы преодолеть это ограничение, и использовал неявные операторы для удобного преобразования строк и в строки.См. Ниже решение:

public class MyClass
{
    [Key, DatabaseGenerated(DatabaseGenerationOption.Identity)]
    public Guid Id { get; set; }
    public List<StringEntity> Animals { get; set; }
    public MyClass()
    {
        List<StringEntity> Animals = List<StringEntity>();
    }
}
public class StringEntity
{
    [Key, DatabaseGenerated(DatabaseGenerationOption.Identity)]
    public Guid Id { get; set; }
    public string Value { get; set; }

    public StringEntity(string value) { Value = value; }
    public static implicit operator string(StringEntity se) { return se.Value; }
    public static implicit operator StringEntity(string value) { return new StringEntity(value);  }
}
public class MyDbContext : DbContext
{           
    public DbSet<MyClass> MyClasses { get; set; }       

    protected override void OnModelCreating(ModelBuilder modelBuilder)
    {
        modelBuilder.Entity<MyClass>()
            .HasMany(x => x.Animals)
            .WithMany()
            .Map(x =>
            {
                x.MapLeftKey(l => l.Id, "MyClassId");
                x.MapRightKey(r => r.Id, "StringEntityId");
            });
    }
}

... Все выглядело так, как будто оно отлично работало с некоторым тестированием (хотя и тяжелым), а затем я реализовал для своей первоначальной цели Multiselect ListBox в виде MVC3.По неизвестным мне причинам, если для ListBox назначено то же имя, что и для свойства Entity Collection, ни один из выбранных вами элементов не будет загружен.

Чтобы продемонстрировать следующее НЕ работает: // Razor View Code

string[] animalOptions = new string[] {"Dog", "Cat", "Goat"};
string[] animalSelections = new string[] {"Dog", "Cat"};
Html.ListBox("Animals", Multiselect(animalOptions, animalSelections));

Чтобы обойти это ограничение, мне нужно было сделать четыре вещи:

//#1 Unpluralize the ListBox name so that is doesn't match the name Model.Animals
var animalOptions = new string[] {"Dog", "Cat", "Goat"};
@Html.ListBox("Animal", new MultiSelectList(animalOptions, Model.Animals.Select(x => x.Value)))

//#2 Use JQuery to replace the id and name attribute, so that binding can occur on the form post
<script type="text/javascript">
   jQuery(function ($) {
     $("select#Animal").attr("name", "Animals").attr("id", "Animals");
    });
</script>

//#3 Create a model binder class to handle List<StringEntity> objects 
public class StringEntityListBinder : IModelBinder
{
  public object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
  {
     var stringArray = controllerContext.HttpContext.Request.Params.GetValues(bindingContext.ModelName);
     return stringArray.Select(x => new StringEntity(x)).ToList();
  }
}

//#4 Initialize the binder in your Global.asax setup.
ModelBinders.Binders.Add(typeof(List<StringEntity>), new StringEntityListBinder ());

Обратите внимание, что ошибка Listbox НЕ возникала, когда свойство представляло собой список строк, ему просто не нравилось, когда оно представляло собой список объектов.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...