EF Core query not including empty results - c#

I'm fairly new to EF Core, so please forgive me if I'm missing something easy here or went about this the wrong way. I have the following models (I have removed irrelevant relationships / data for brevity):
public class SaleOrder
{
public int SaleOrderId { get; set; }
public ICollection<SaleOrderRule> Rules { get; set; }
public ICollection<Carton> ScannedCartons { get; set; }
}
public class SaleOrderRule
{
public int SaleOrderRuleId { get; set; }
public int SaleOrderId { get; set; }
public SaleOrder SaleOrder { get; set; }
public int ProductCodeId { get; set; }
public ProductCode ProductCode { get; set; }
public int Quantity { get; set; }
}
public class ProductCode
{
public int ProductCodeId { get; set; }
public string Value { get; set; }
}
public class Carton
{
public int CartonId { get; set; }
public string BoxID { get; set; }
public int ProductCodeId { get; set; }
public ProductCode ProductCode { get; set; }
public int? SaleOrderId { get; set; }
public SaleOrder? SaleOrder { get; set; }
}
A Product Code and Quantity make up a rule, and a Sale Order can have many Rules and Cartons, Cartons can only be added to the Sales Order if the Product Code matches one of the rules, I am trying to query a breakdown of the data for a single Sale Order, including:
The SaleOrderRuleId
The Product Code
The Total Cartons scanned for this Rule
The Total Quantity required for this Rule And
A list of the Scanned Cartons for the rule (just the BoxID and CartonId)
I came up with the following query:
var saleOrderCartonCountBreakdown = await
(from c in _context.Cartons
where c.SaleOrderId == id
group c by new
{
c.ProductCodeId,
} into g
select new
{
ProductCodeId = g.Key.ProductCodeId,
CartonInfo = g.Select(x =>
new SaleOrderCartonBreakdownCarton {
CartonId = x.CartonId,
BoxID = x.BoxID
}).ToList(),
Count = g.Count()
} into gv
join pc in _context.ProductCodes on gv.ProductCodeId equals pc.ProductCodeId
join sor in _context.SaleOrderRules on gv.ProductCodeId equals sor.ProductCodeId
select new SaleOrderCartonBreakdownModel
{
SaleOrderRuleId = sor.SaleOrderRuleId,
ProductCode = pc.Value,
TotalScanned = gv.Count,
TotalRequired = sor.Quantity,
CartonList = gv.CartonInfo.ToList()
}).ToListAsync();
This works, but it only includes data if there is atleast one scanned carton for a product code, the intention is to also include the product codes for rules which do not have any cartons scanned in yet.
I'm fairly certain this is because I am starting on the Cartons table, however my attempts to rewrite the query to start on the SaleOrderRules and output the same result have been unsuccessful.
Any help is much appreciated :)

Related

Sequence contains no elements on query

Query:
var result = await this.Context.ShopProducts
.Include(prd => prd.Category)
.ThenInclude(cat => cat.Culture)
.Include(prd => prd.InfoItems)
.SingleOrDefaultAsync(prd => prd.Id.Equals(id) && prd.CategoryId.Equals(culture));
Edit: Updated the entities and query to reflect the new design and added a sql query
Entities:
Product:
[Table("ShopProduct")]
public class Product : ShopBase
{
public bool Active { get; set; } = true;
public int CategoryId { get; set; }
public virtual Category Category { get; set; }
public ICollection<ProductInfo> InfoItems { get; set; } = new HashSet<ProductInfo>();
}
ProductInfo:
[Table("ShopProductInfo")]
public class ProductInfo : ShopBase
{
public int ProductId { get; set; }
public int CultureId { get; set; }
public string Name { get; set; }
public string Description { get; set; }
public decimal Sum { get; set; }
public ICollection<GraphicItem> GraphicItems { get; set; }
}
What I want is to only select the ProductInfo objects with CultureId that equals the Category CultureId property. When selecting I provide the product Id and Category Id.
I want to replicate something like this sql query:
DECLARE #prdId INT,
#catId INT
SET #prdId = 1
SET #catId = 1
SELECT prd.*,
info.*,
cat.*
FROM ShopProduct prd,
ShopProductInfo info,
ShopCategory cat
WHERE prd.Id = #prdId
AND prd.CategoryId = cat.Id
AND cat.Id = #catId
AND cat.CultureId = info.CultureId
this error mean, linq query return some value otherwise give exception error

Linq query for complicated model

I can't seem to find out the query for a complicated model I'm using. The model looks like this:
public class Stash
{
public int StashId { get; set; }
public int LocationId { get; set; }
public Location Location { get; set; }
public int ProductId { get; set; }
public Product Product { get; set; }
public int Amount { get; set; }
}
A product can have several locations and now I want to get all the locations that are connected to a product.
var stashes = new List<Stash>();
int productID = [your id];
var result = stashes.Where(stash => stash.ProductId == productID).Select(stash => stash.Location);
If you need the result to be a List add .ToList() at the end.
you can use
Include to load reference of other entity
var result=context.entity.Include(c=>c.product)
.where(c=>c.product.productId==productid)

How to get specific columns with one to many relationship

I have 3 tables , one to many relationship.
I need to get only specific columns with SelectMany method.
I need to get only Categories.CategoryName and Comments.CommentDate of the selected News object.
Here is my code
News news = db.News.Include(w => w.Categories)
.Include(w => w.Comments).SingleOrDefault(n => n.NewsId == Id);
Here are my Entities:
News Entity:
public partial class News
{
public News()
{
this.Categories = new HashSet<Category>();
this.Comments = new HashSet<Comment>();
}
public int NewsId { get; set; }
public string NewsTitle { get; set; }
public string NewsBody { get; set; }
public System.DateTime NewsDate { get; set; }
public string NewsImagePath { get; set; }
public virtual ICollection<Category> Categories { get; set; }
public virtual ICollection<Comment> Comments { get; set; }
}
Category Entity:
public partial class Category
{
public Category()
{
this.News = new HashSet<News>();
}
public int CategoryId { get; set; }
public string CategoryName { get; set; }
public virtual ICollection<News> News { get; set; }
}
Comment Entity:
public partial class Comment
{
public Comment()
{
this.News = new HashSet<News>();
}
public int CommentId { get; set; }
public string CommentBody { get; set; }
public Nullable<System.DateTime> CommentDate { get; set; }
public virtual ICollection<News> News { get; set; }
}
This LINQ query should take care of it:
var query =
from news in db.News
where news.Id == Id
let categoryNames =
from category in news.Categories
select category.Name
let commentDates =
from comment in news.Comments
select comment.CommentDate
select new {
CategoryNames = categoryNames.ToList(),
CommentDates = commentDates.ToList()
};
That query is not using SelectMany, but that wouldn't help you, since then you wouldn't be able to group your categories and comments by news items. Since categories and comments are not directly connected, you'd need two SelectManys and then you'd need to cross join the results. That would obviously not be what you want.
Maybe try using the following?
var categoryNames = news.Categories.Select(c=>c.CategoryName);
var commentDates = news.Comments.Select(c=>c.CommentDate);
Note that SelectMany is used to flatten lists.For example, lets say you have collection of news matching certain search criteria, and then you use SelectMany to collect all the Categories/Comments of these news set, in a flat list.

Grouping 4 Tables using Linq

I have the following requirements:
One rating can have zero or many RatingPictures
One Rating can have zero or many Comments
One Comment belongs to one User
This is what I have so far:
from rating in Ratings
where rating.LocationID == 1007
join ratingpicture in RatingPictures
on rating.ID equals ratingpicture.RatingID into j3
from ratingpicture in j3.DefaultIfEmpty()
join comment in Comments
on rating.ID equals comment.RatingID into j1
from comment in j1.DefaultIfEmpty()
join user in Users
on comment.UserID equals user.ID into j2
from user in j2.DefaultIfEmpty()
group new { ratingpicture, comment, user } by rating into g
select new { rating = g.Key, ratingpicture= g.Key, comment = g.ToList() }
If you model your entity classes like this:
public class Comment
{
public int CommentId { get; set; }
public int RatingId { get; set; }
public virtual Rating Rating { get; set; }
public int UserId { get; set; }
public virtual User User { get; set; }
}
public class User
{
public int UserId { get; set; }
}
public class Rating
{
public Rating()
{
RatingPictures = new HashSet<RatingPicture>();
Comments = new HashSet<Comment>();
}
public int RatingId { get; set; }
public virtual ICollection<RatingPicture> RatingPictures { get; set; }
public virtual ICollection<Comment> Comments { get; set; }
public int LocationID { get; set; }
}
public class RatingPicture
{
public int RatingPictureId { get; set; }
public int RatingId { get; set; }
public virtual Rating Rating { get; set; }
}
Then your query would be as simple as this:
var query = context.Ratings.Where(r => r.LocationID == 1007)
.Include(r => r.RatingPictures)
.Include(r => r.Comments.Select(c => c.User));
var result = query.ToList();
Under the hood, this query will be translated into a join query. But this is the beauty of ORMs, we get to work at a more abstract level.
Take a look at this reference for more information about relationships and navigation properties in Entity Framework.

EF4.1 - Attribute Evaluating to null at runtime

I'm using EF4.1 code first to create a simple database app with SQL CE 4 backend. I have a Product class and a CallItem class defined as so:
class CallItem
{
public int id { get; set; }
public float discount { get; set; }
public virtual Product Product { get; set; }
}
class Product
{
public int id { get; set; }
public decimal BaseCost { get; set; }
public int UnitSize { get; set; }
public bool isWasteOil { get; set; }
public string Code { get; set; }
public string Name { get; set; }
public string Description { get; set; }
public string Ingredients { get; set; }
}
edit - When I am creating a collection of CallItems using a LINQ query, I cannot access the attributes of the Product attached to each CallItem, eg
var callItems = from ci in context.CallItems select ci;
foreach(CallItem callItem in callItems)
{
RunSheet nrs = new RunSheet();
nrs.prodCode = callitem.Product.Code;
}
Interrogating the database shows that Productid in CallItems is being populated. However, the following line generates a NullReferenceException during run time:
nrs.prodCode = callitem.Product.Code;
Because callitem.Product is evaluating to null. Is this something to do with lazy loading and if so how can I resolve the issue?
RunSheet is another class, nrs is an instance whose attribute 'prodCode' I want to populate with the CallItem's Product's code.
Thanks!
From that code what you've showed it should work. Have you tried explicit loading?
var callItems = from ci in context.CallItems.Include(c => c.Product) select ci;
foreach(CallItem callItem in callItems)
{
RunSheet nrs = new RunSheet();
nrs.prodCode = callitem.Product.Code;
}
public class CallItem
{
public int Id { get; set; }
public float Discount { get; set; }
public virtual Product Product { get; set; }
}
public class Product
{
public int Id { get; set; }
public decimal BaseCost { get; set; }
public int UnitSize { get; set; }
public bool IsWasteOil { get; set; }
public string Code { get; set; }
public string Name { get; set; }
public string Description { get; set; }
public string Ingredients { get; set; }
}
using (var context = new StackOverFlowContext())
{
var p = new Product
{
Id = 1,
BaseCost = 200,
Code = "Hola",
Description = "Soe description",
Ingredients = "Some ingredients",
IsWasteOil = true,
Name = "My Product",
UnitSize = 10
};
var item = new CallItem
{
Id = 101,
Discount = 10,
Product = p
};
context.CallItems.Add(item);
context.SaveChanges();
var result = from temp in context.CallItems
select temp;
Console.WriteLine("CallItem Id"+result.First().Id);
Console.WriteLine("ProductId"+result.First().Product.Id);
}
I wrote the above code with the following output
CallItemId 1
ProductId 1
The sql Profiler showed this
SELECT TOP (1)
[c].[Id] AS [Id],
[c].[Discount] AS [Discount],
[c].[Product_Id] AS [Product_Id]
FROM [dbo].[CallItems] AS [c]
It was too long for a comment ,so i put it here .

Categories

Resources