【问题标题】:EF Code First DbContext returns encapsulated entity only for first element of collectionEF Code First DbContext 仅为集合的第一个元素返回封装的实体
【发布时间】:2014-06-18 08:13:09
【问题描述】:

EF 的非常奇怪的行为让我把头发拔了出来。

问题主要出在以下实体类,是封装的属性:

public class ContextParamValue
{
    public Int64 Id { get; set; }

    public Int64 ContextParamId { get; set; }

    public virtual ContextParam ContextParam { get; set; }

    public virtual ContextInstance ContextInstance { get; set; }

    public Int64 ContextInstanceId { get; set; }

    public string Value { get; set; }
}

如您所见,我有一个 ContextParamValue 类,它与 ContextParam 具有单向的 1 对 1 关系。因此,ContextParamValue 可以访问 ContextParam,但不能反过来。

让我崩溃的代码如下:

public List<ContextParamValue> ParamValuesToList(string[] ParamNames, string[] ParamValues)
{
    if (ParamNames != null && ParamNames.Length != ParamValues.Length)
        throw new System.ArgumentException("ParamNames and ParamValues may not differ in length.");

    List<ContextParamValue> rList = new List<ContextParamValue>();

    for (int i = 0; i < ParamNames.Length; i++)
    {
        string pName = ParamNames[i];
        string pValue = ParamValues[i];

        List<ContextParamValue> lst = db.ContextParamValues
            //.Include(x => x.ContextParam)
            .Where(pv => pv.ContextParam.Name.ToLower().Trim().Equals(pName.ToLower().Trim()))
            .Where(pv => pv.Value.Equals(pValue))
            .ToList<ContextParamValue>();

        rList.AddRange(lst);
    }

    return rList;
}

这段代码的奇怪结果是ContextParam 只为rList 返回的第一个元素加载。 rList 中的所有后续元素的 ContextParam 属性都有一个空值。以下屏幕截图显示了调试期间的元素实例值:

集合中的第一个元素...获胜!

集合中的第二个元素...MASSIVE FAIL!

我已经为上述方法尝试了多种替代实现,即延迟加载、急切加载,甚至不从循环中构建列表(我构建了一个包含 ParamNamesParamValues 数组对象的字典,它允许我在 LINQ 表达式中进行基于集合的匹配)。每次都是一样的结果。

我还包括了我的DbContext 类中的相关sn-ps:

protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
    ModelMapper.InitializeRelationshipMappings(modelBuilder);
    base.Configuration.LazyLoadingEnabled = true;
}

public static class ModelMapper
{
    public static void InitializeRelationshipMappings(DbModelBuilder modelBuilder)
    {
        modelBuilder.Conventions.Remove<OneToManyCascadeDeleteConvention>();
        modelBuilder.Conventions.Remove<ManyToManyCascadeDeleteConvention>();

        modelBuilder.Entity<Document>()
            .HasRequired(d => d.FileItem)
            .WithOptional(fi => fi.Document)
            .WillCascadeOnDelete(true);


        modelBuilder.Entity<Document>()
            .HasMany(d => d.DocumentClasses)
            .WithMany(dc => dc.Documents);

        modelBuilder.Entity<ContextClass>()
            .HasMany(cc => cc.RequiredClasses);

        modelBuilder.Entity<ContextClass>()
           .HasMany(cc => cc.OptionalClasses);

        modelBuilder.Entity<ContextClass>()
            .HasMany(cc => cc.Params)
            .WithRequired(cp => cp.ContextClass)
            .WillCascadeOnDelete(true);

        modelBuilder.Entity<ContextInstance>()
            .HasRequired(ci => ci.ContextClass);

        modelBuilder.Entity<ContextInstance>()
            .HasMany(ci => ci.ContextParamValues)
            .WithRequired(cpv => cpv.ContextInstance)
            .HasForeignKey(cpv => cpv.ContextInstanceId)
            .WillCascadeOnDelete(true);

        modelBuilder.Entity<ContextParamValue>()
            .HasRequired(cpv => cpv.ContextParam);
    }
}

【问题讨论】:

  • 映射中缺少 ContextParam 是否有原因?
  • Ummm... 它没有丢失... 上面最后一个代码 sn-p 中的最后一条语句为:modelBuilder.Entity() .HasRequired(cpv => cpv.ContextParam) ;
  • 谢谢。不幸的是,不是。
  • 您是否调试过“for 循环”并观察循环的第二次迭代期间 ContextParam 属性发生了什么?它是否填充了来自数据库的信息?我很好奇名称和值是否仅与第一项匹配,而与其余项目不匹配。另一个问题:因为您正在转换为一个列表,我们是否可以假设存在多个具有相同名称和值的对象返回的实例?还是为了便于编写代码,只返回一个对象?

标签: c# entity-framework linq-to-entities entity-framework-6


【解决方案1】:
  1. 请检查是否不是数据相关问题。
  2. 或者尝试替换你的代码

       List<ContextParamValue> lst = db.ContextParamValues
        //.Include(x => x.ContextParam)
        .Where(pv => pv.ContextParam.Name.ToLower().Trim().Equals(pName.ToLower().Trim()))
        .Where(pv => pv.Value.Equals(pValue))
        .ToList<ContextParamValue>();
    
    rList.AddRange(lst);
    

使用另一个 LINQ

List<ContextParamValue> lst = from db.ContextParamValues.Where(pv => pv.ContextParam.Name.ToLower() == pName.ToLower().Trim()
                          && db.ContextParamValues.Where(pv => pv.Value == pValue)).ToList<ContextParamValue>();
  • 这可能是上下文问题,因为它要么无法加载,要么无法执行 linq。尝试注释您的代码,我相信您可以使用 SP 来获取这些数据(我做了同样的事情并且它有效)

【讨论】:

  • 感谢您的意见。我可以确认这绝对不是与数据相关的问题,任何引用的表中都没有空引用。 ContextParam.Name 是字符串类型,ContextParamValue.Value 也是如此,因此 Equals 运算符应该可以正常工作。除此之外,您上面编写的查询基本上只是我编写的翻译成查询语法的 LINQ 查询。我找到了一个临时解决方法,但我特别想确定这个问题的根本原因是什么。
  • 我对您关于这可能是与上下文相关的问题的评论感兴趣(您是迄今为止唯一提出此问题的人),我想进一步探讨一下。 “上下文问题”到底是什么意思?
  • 其实Shaul之前也提到过。对不起@Shaul!
  • @Sir Juice,您能否展示一下您的工作,只是想知道您在没有 SQL 注入的情况下是如何完成的......
  • 当然,这里描述了解决方法stackoverflow.com/questions/23703059/…
【解决方案2】:

请幽默一下。将整个 for 循环放入 using 块中:

using (var myDb = new MyDataContext()) 
{
   for (int i = 0; i < ParamNames.Length; i++)
   { 
     //etc., replacing "db" with "myDb" 
   }
}

我很想知道这是否会改变任何事情。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-08-27
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多