简体   繁体   中英

EF Code First DbContext returns encapsulated entity only for first element of collection

Very strange behaviour from EF which has me pulling my hair out.

The problem pertains mainly to the following entity class, and it's encapsulated properties:

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; }
}

As you can see, I have a ContextParamValue class, which has a unidirectional 1 to 1 relationship with ContextParam. Thus, ContextParamValue can access ContextParam but not the other way around.

The piece of code that has me in tatters are as follows:

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;
}

The strange result of this code is that ContextParam is only loaded for the first element returned in rList . All the subsequent elements in rList has a null value for the ContextParam property. The following screenshots shows the element instance values during debugging:

列表中的第一个元素是A-OK First element in the collection... WINNING!

后续元素...巨大失败 Second element in the collection... MASSIVE FAIL!

I have tried multiple alternative implementations for the above method, namely lazy loading, eager loading, even not building up the list from within a loop (I constructed a dictionary of the ParamNames and ParamValues array objects which allowed me to do set-based matching within the LINQ expression). Same result every time.

I also include the relevant snippets from my DbContext class:

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

AND

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);
    }
}
  1. Please check if it is not a data related issue.
  2. Else try replacing your code

      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); 

with another 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>();
  • This might be a context issue as it is either not able to load or not able to execute the linq.Try commenting your code & I am sure you can use SP to get this data (I did the same & it worked)

Humour me, please. Put your whole for loop inside a using block:

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

I would be interested to know if this changes anything.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM