2

我首先使用代码并尝试对 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 序列化字符串,因为如果 FavoriteColors 是字符串,我可以执行 Contains 调用。或者,我可以过分创建一个“颜色”实体,但这是相当重的重量。不幸的是,也不支持枚举。

4

2 回答 2

1

我认为问题不在于集合,而在于对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;
于 2011-02-18T04:28:56.830 回答
0

我决定通过创建一个“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");
            });
    }
}

...一切看起来都与一些测试(尽管很重)完美配合,然后我实现了它的原始目的,在 MVC3 视图中实现了一个多选列表框。由于我不知道的原因,如果为 ListBox 分配了与实体集合属性相同的名称,则不会加载您选择的任何项目。

演示以下内容不起作用: //Razor 查看代码

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 错误,只是在它是实体列表时不喜欢它。

于 2011-02-19T02:47:01.807 回答