Virtual property is not instantiated in lambda

Asked

Viewed 100 times

0

When I took a code to improve performance I had the following. A method GetAll() this method populated a var. It was made a foreach in this var and was assigning the appropriate values. It happens that within this foreach there was such a command:

if (product.PictureId.HasValue)
    item.PictureFilename = product.Picture.FileName;

This condition is paramount. The problem that this foreach takes at least 4 min to execute and I speak of only 122 records (202 in Getall()). I did a lambda and reduced in Debug to 17s and environment to 6s to 10s. But the problem is in the code above. In lambda I can’t bring it: product.Picture.FileName. Picture is a virtual property of Product(class) and Picture is a class as well. The lambda is not the instance and gives error of NullReferenceException, of the kind:

Undefined object reference for an object instance.

There is some understanding missing in Linq/Lambda for me to complete this topic. This is the complete lambda that I did and the commented lines are the ones that are giving problem. Besides what I posted (original Lambda), I made several other attempts.

var qry = _productRepository.Table.GroupJoin(_categoriesRepository.Table,
            p => p.CategoryId,
            c => c.Id,
            (p, c) => new { Product = p, Categories = c.DefaultIfEmpty() })

            .Where(hdg => hdg.Product.Hidden == false)
            .SelectMany(final => final.Categories,
            (final, c) => new CatalogItemResponse
            {
                ChildrenCategoryId = final.Product.ChildrenCategoryId,

                DolarRate = 0.0m,
                ResellerPriceUSD = 0.0m,
                ResellerPriceBRL = 0.0m,
                BasePriceBRL = 0.0m,
                BasePriceUSD = 0.0m,

                CategoryId = final.Product.CategoryId,
                CategoryName = (c != null ? c.Name : null),
                PictureId = final.Product.PictureId,
                Description = final.Product.Description,
                ShortDescription = final.Product.ShortDescription,
                Name = final.Product.Name,
                NameHtml = string.IsNullOrEmpty(final.Product.NameHtml) ? final.Product.Name : final.Product.NameHtml,
                PartNumber = final.Product.PartNumber,
                Hidden = final.Product.Hidden,
                Order = final.Product.Order,
                HaveMaximumPercentage = final.Product.HaveMaximumPercentage,
                MaximumPercentage = final.Product.MaximumPercentage,
                HaveMinimumPercentage = final.Product.HaveMinimumPercentage,
                MinimumPercentage = final.Product.MinimumPercentage,
                AuthorizeMaximumPercentageAlteration = final.Product.AuthorizeMaximumPercentageAlteration,
                AuthorizeMinimumPercentageAlteration = final.Product.AuthorizeMinimumPercentageAlteration,
                StandardMarkup = final.Product.StandardMarkup,
                DistributionCenterErpId = final.Product.DistributionCenterErpId,
                PictureFilename = final.Product.Picture.FileName

            }).ToList();

            qry.ForEach(q =>
            {
                var product = new Product();

                product.CategoryId = q.CategoryId;
                product.AuthorizeMaximumPercentageAlteration = q.AuthorizeMaximumPercentageAlteration;
                product.AuthorizeMinimumPercentageAlteration = q.AuthorizeMinimumPercentageAlteration;
                product.HaveMaximumPercentage = q.HaveMaximumPercentage;
                product.HaveMinimumPercentage = q.HaveMinimumPercentage;
                product.Hidden = q.Hidden;
                product.ChildrenCategoryId = q.ChildrenCategoryId;
                product.Description = q.Description;
                product.DistributionCenterErpId = q.DistributionCenterErpId;
                product.MaximumPercentage = q.MaximumPercentage;
                product.MinimumPercentage = q.MinimumPercentage;
                product.Name = q.Name;
                product.NameHtml = q.NameHtml;
                product.Order = q.Order;
                product.PartNumber = q.PartNumber;
                product.PictureId = q.PictureId;
                product.ShortDescription = q.ShortDescription;
                product.StandardMarkup = q.StandardMarkup;

                var parentProducts = _productService.GetParentsOf(product.Id).Select(x => x.PartNumber);
                q.Parents.AddRange(parentProducts);

                //if (product.PictureId.HasValue)
                //    q.PictureFilename = product.Picture.FileName;

                var price = _erpPriceService.GetPrice(product, 1, resellerId).Result;

                if (price.BasePriceUSD > 0)
                    q.DolarRate = price.BasePriceBRL / price.BasePriceUSD;

                q.ResellerPriceUSD = price.ResellerPriceUSD;
                q.ResellerPriceBRL = price.ResellerPriceBRL;
                q.BasePriceBRL = price.BasePriceBRL;
                q.BasePriceUSD = price.BasePriceUSD;

            });

            return qry;
  • When I give a new Product(), I already lose references to all the virtual properties in Product, correct? Outside the ForEach() lambda, I really don’t know where to assign the values that are within the if.

  • would have to post a project with only the essential, reproducing the problem, to be able to test on this side?

  • When I do var product = new Product() I’m killing Lazy loading and that leaves Picture out, right? And how do I keep Lazy loading?

1 answer

1

Watching your code I see that variable product is receiving a new instance var product = new Product() but at no time was a value assigned to product.Picture then it is null (unless you have assigned value in the empty constructor of the class Product), in this case if you access any Picture property you will receive Exception: "Object reference not set for an instance of an object". In case you have activated the LazyLoading within the Query you can load the virtual properties automatically, in your case the code I just mentioned is outside the query generation because it ended when you performed the 'Immediate Execution' through the ToList().

  • Anderson Paiva, um .Include(pic => pic.Product.Picture can solve? This include here: var qry = _productRepository.Table.GroupJoin(_categoriesRepository.Table,
 p => p.CategoryId,
 c => c.Id,
 (p, c) => new { Product = p, Categories = c.DefaultIfEmpty() })
 .Include(pic => pic.Product.Picture)
 .Where(hdg => hdg.Product.Hidden == false)

  • Your Catalogitemresponse class needs to have a property to receive Picture so you assign within Selectmany the value 'Picture = final.Product.Picture' with this you could uncomment your code in Foreach because if there is value in Picture it will be filled (if you haven’t turned on the Lazyloading use the include you yourself quoted above)

  • One thing. In Catalogitemresponse I have this: PictureFilename = final.Product.Picture.FileName, this should solve, but not and the if continues giving dick

  • that wouldn’t solve it because PictureFileName must be a string property, and final.Product.Picture.FileName also, the error that the code is generating is in the access to an instantiated object

  • looking at your code again I realized that instead of q.PictureFilename = product.Picture.FileName; you could use q.PictureFilename = product.PictureFileName;

  • I have no product.Picturefilename, but I understand create a new property that gets Picture, I think

  • It’s the only way I can assign: public virtual Product.Picture Picture { get; set; }, but I don’t think that’s it

Show 3 more comments

Browser other questions tagged

You are not signed in. Login or sign up in order to post.