LINQ 数据过滤仅在调试期间有效

LINQ filtering on data only works during debug

我正在使用围绕 Entity Framework Code First 和 ASP.NET MVC 5 构建的 N 层解决方案。

我的 Artist Controller 中有以下 ActionResult。

public ActionResult Paintings(string urlfriendly)
{
    var artist = _artistService.GetArtistByUrlFriendly(urlfriendly);
    ArtistPaintingsVM vm = Mapper.Map<ArtistPaintingsVM>(artist);

    return View(vm);
}

该服务最终到达这里:

public Artist GetArtistByUrlFriendly(string urlFriendly)
{
    var artist = _context.Artists.FirstOrDefault(a => a.UrlFriendly == urlFriendly && a.Verified);
    if (artist != null)
    {
        artist.Paintings = _context.Paintings.Where(p => p.ArtistId == artist.Id && p.Verified).ToList();
    }

    return artist;
}

当我单步执行该代码时,我得到了想要的结果。我得到了那位艺术家的两幅经过验证的画作。然而,当我 运行 应用程序没有调试时,我得到了艺术家的所有三幅画,即使其中一幅未经验证!

我上面的代码,应该只return画过验证,在debug下确实如此,但在正常运行ning模式下不行。

这里是数据播种代码:

    public class DXIntializer : DropCreateDatabaseAlways<DXContext>
    {
        protected override void Seed(DXContext context)
        {
            try
            {
                var artists = new List<Artist>
                {
                    new Artist { FName = "Salvador", LName = "Dali", ImgURL = "http://i62.tinypic.com/ss8txxn.jpg", UrlFriendly = "salvador-dali", Verified = true }
                };

                artists.ForEach(a => context.Artists.Add(a));
                context.SaveChanges();

                var paintings = new List<Painting>
                {
                    new Painting { Title = "The Persistence of Memory", ImgUrl = "http://i62.tinypic.com/xx8tssn.jpg", ArtistId = 1, Verified = true },
                    new Painting { Title = "Swans Reflecting Elephants", ImgUrl = "http://i62.tinypic.com/aa9tssn.jpg", ArtistId = 1, Verified = true },
                    new Painting { Title = "Crucifiction", ImgUrl = "http://i62.tinypic.com/qq0tssn.jpg", ArtistId = 1, Verified = false }
                };

                paintings.ForEach(p => context.paintings.Add(p));
                context.SaveChanges();
            }
            catch (DbEntityValidationException ex)
            {
                foreach (var validationErrors in ex.EntityValidationErrors)
                {
                    foreach (var validationError in validationErrors.ValidationErrors)
                    {
                        Trace.TraceInformation("Property: {0} Error: {1}", validationError.PropertyName, validationError.ErrorMessage);
                    }
                }
            }

        }
    }

我做错了什么?

您不应该这样设置艺术家的 属性。由于这是一个 EF 导航 属性。调试时,一旦您的代码命中服务(因为您的调试器读取 属性),它就会被加载,然后被您的 linq 语句的值覆盖。当 运行 没有调试时,更有可能的是 属性 实际上首先在映射器中读取(延迟加载)并且执行的查询是 EF 定义的查询。

如果你只在一个地方需要它,你应该像

一样只写一次
artist.Paintings.Where(p => p.Verified)

如果您在多个地方需要它,您可以为它制作一个 属性。 一个正确的方法是为您的艺术家定义一个额外的 属性。

ICollection<Painting> VerifiedPaintings 
{
    get 
    {
         return this.Paintings.Where(p => p.Verified);
    }
}

当您希望您的服务重新运行一个完全加载的对象时(例如,如果您在离开服务时处置您的 DbContext)。您应该将其加载到单独的列表中。

return new artistDto 
{
    Artist = artist ,
    VerifiedPaintings = artist.Paintings.Where(p => p.Verified)
}

如果 Paintings 属性 的 Artist class 是虚拟的,例如

public virtual List<Painting> Paintings { get; set; }

使其成为非虚拟的 (turn off lazy loading),像这样:

public List<Painting> Paintings { get; set; }

此修复应该可以让您的代码正常运行。如果它已经是非虚拟的,请公开这些实体的源代码以帮助我们理解问题。

解释 + 一些建议

  1. 使用 Entity Framework 构建的 N 层解决方案很可能涉及实体序列化。在您的情况下,用于检索数据的服务(我想是 wcf 或 web api)将序列化 Artist 对象并将其传递到 UI 层。现在,lazy loading and serialization don't mix well. 当序列化程序访问 Paintings 属性 时,由于延迟加载,它最终会加载属于艺术家的所有画作。调试时,试试这个:根本不要检查 \ 悬停在艺术家对象上,也不要将其从手表中删除,在 return artist; 语句上设置断点。按 F5,当执行暂停在 return 语句时,探索艺术家对象,您应该看到所有三幅画。在为 Paintings 属性 赋值之前,请确保没有任何东西会触发 Paintings 属性 的计算。

  2. 像这样声明导航属性是一个很好的做法:public List<Painting> Paintings { get; private set; }这会强制使用推荐的导航属性方式。也就是说,您可以从集合中添加或删除项目,但是,您不能分配新的集合。在默认构造函数中初始化此类属性很重要,如下所示:

    public class Artist
    {    
        public List Paintings { get; private set; }    
        public Artist()
        {
            this.Paintings = new List();
        }
    }
  1. 关闭延迟加载时,必须explicitly load related entities

在您的情况下,与其直接为 Paintings 属性 赋值,不如执行以下操作:

            db.Entry<Artist>(artist)
            .Collection(a => a.Paintings)
            .Query()
            .Where(p => p.Verified)
            .Load();

它只会加载指定艺术家的经过验证的画作(延迟加载应该关闭)。