Excluding child records from entity objects c# - frameworks

I am having an issue with writing a query to exclude records from entity framework child objects.
My query
var response = db.USER_PROFILE.Where(x =>
x.IPAD_SERIAL_NUMBER == id
&& x.ACTIVE_FLAG == 1
&& x.USER_BRAND.Any(y => y.ACTIVE_FLAG == 1)
).FirstOrDefault();
Returned result
One USER_PROFILE object with
Two USER_BRAND objects
USER_BRAND - ACTIVE_FLAG = 1
USER_BRAND - ACTIVE_FLAG = 0
I don't want to return a record with ACTIVE_FLAG = 0 in the collection.
How do I do easily that?
Thanks in advance!

I was able to do it this way
var query = db.USER_PROFILE
.Select(x=> new
{
User = x,
UserBrands = x.USER_BRAND.Where(y=> y.ACTIVE_FLAG == 1)
.Select(a=> new
{
UserBrand = a,
Brand = a.BRAND
}),
});
var filtered = query.Select(x=> x.User);

Related

Group By with Entity Framework

enter image description hereI have a code. And there you need to make a grouping by name.
//<date,<partid,amount>>
Dictionary<string, Dictionary<int, double>> emSpending = new Dictionary<string, Dictionary<int, double>>();
foreach (Orders order in db.Orders.ToList())
{
foreach (OrderItems orderitem in order.OrderItems.ToList())
{
if (!emSpending.ContainsKey(order.Date.ToString("yyyy-MM"))) emSpending.Add(order.Date.ToString("yyyy-MM"), new Dictionary<int, double>());
if (!emSpending[order.Date.ToString("yyyy-MM")].ContainsKey(Convert.ToInt32(orderitem.PartID))) emSpending[order.Date.ToString("yyyy-MM")].Add(Convert.ToInt32(orderitem.PartID), 0);
emSpending[order.Date.ToString("yyyy-MM")][Convert.ToInt32(orderitem.PartID)] += Convert.ToDouble(orderitem.Amount);
}
}
DataGridViewColumn col1 = new DataGridViewColumn();
col1.CellTemplate = new DataGridViewTextBoxCell();
col1.Name = "Department";
col1.AutoSizeMode = DataGridViewAutoSizeColumnMode.Fill;
col1.HeaderText = "Department";
dgvEMSpending.Columns.Add(col1);
foreach (string date in emSpending.Keys)
{
DataGridViewColumn col = new DataGridViewColumn();
col.Name = date;
col.HeaderText = date;
col.AutoSizeMode = DataGridViewAutoSizeColumnMode.Fill;
col.CellTemplate = new DataGridViewTextBoxCell();
dgvEMSpending.Columns.Add(col);
}
List<string> allKey = emSpending.Keys.ToList();
foreach (string date in allKey)
if (date == "Department") continue;
else
{
dgvEMSpending.Rows.Add();
foreach (int partid in emSpending[date].Keys)
{
dgvEMSpending.Rows[dgvEMSpending.Rows.Count - 1].Cells[0].Value = db.Parts.Where(x => x.ID == partid).SingleOrDefault().Name.GroupBy(Name);
for (int i = 1; i < dgvEMSpending.Columns.Count; i++)
{
if (!emSpending.ContainsKey(dgvEMSpending.Columns[i].Name)) emSpending.Add(dgvEMSpending.Columns[i].Name, new Dictionary<int, double>());
if (!emSpending[dgvEMSpending.Columns[i].Name].ContainsKey(partid)) emSpending[dgvEMSpending.Columns[i].Name].Add(partid, 0);
double val = emSpending[dgvEMSpending.Columns[i].Name][partid];
dgvEMSpending.Rows[dgvEMSpending.RowCount - 1].Cells[i].Value = val;
}
}
}
I tried to use group by myself, but something doesn't work. It just outputs the same names, and I want to group them so that there is a grouping. Pls helped to me.
Ok, a few issues to help you out first. This code:
foreach (Orders order in db.Orders.ToList())
{
foreach (OrderItems orderitem in order.OrderItems.ToList())
{
if (!emSpending.ContainsKey(order.Date.ToString("yyyy-MM"))) emSpending.Add(order.Date.ToString("yyyy-MM"), new Dictionary<int, double>());
if (!emSpending[order.Date.ToString("yyyy-MM")].ContainsKey(Convert.ToInt32(orderitem.PartID))) emSpending[order.Date.ToString("yyyy-MM")].Add(Convert.ToInt32(orderitem.PartID), 0);
emSpending[order.Date.ToString("yyyy-MM")][Convert.ToInt32(orderitem.PartID)] += Convert.ToDouble(orderitem.Amount);
}
}
Right off the bat this is going to trip lazy loading on OrderItems. If you have 10 orders 1-10 you're going to be running 11 queries against the database:
SELECT * FROM Orders;
SELECT * FROM OrderItems WHERE OrderId = 1;
SELECT * FROM OrderItems WHERE OrderId = 2;
// ...
SELECT * FROM OrderItems WHERE OrderId = 10;
Now if you have 100 orders or 1000 orders, you should see the problem. At a minimum ensure that if you are touching a collection or reference on entities you are loading, eager load it with Include:
foreach (Orders order in db.Orders.Include(x => x.OrderItems).ToList())
This will run a single query that fetches the Orders and their OrderItems. However, if you have a LOT of rows this is going to take a while and consume a LOT of memory.
The next tip is "only load what you need". You need 1 field from Order and 2 fields from OrderItem. So why load everything from both tables??
var orderItemDetails = db.Orders
.SelectMany(o => o.OrderItems.Select(oi => new { o.Date, oi.PartId, oi.Amount })
.ToList();
This would give us just the Order date, and each Part ID and Amount. Now that this data is in memory we can group it to populate your desired dictionary structure without having to iterate over it row by row.
var emSpending = orderItemDetails.GroupBy(x => x.Date.ToString("yyyy-MM"))
.ToDictionary(g => g.Key,
g => g.GroupBy(y => y.PartId)
.ToDictionary(g2 => g2.Key, g2 => g2.Sum(z => z.Amount)));
Depending on the Types in your entities you may need to insert casts. This first groups the outer dictionary of the yyyy-MM of the order dates, then it groups the remaining data for each date by part ID, and sums the Amount.
Now relating to your question, from your code example I'm guessing the problem area you are facing is this line:
dgvEMSpending.Rows[dgvEMSpending.Rows.Count - 1].Cells[0].Value = db.Parts
.Where(x => x.ID == partid)
.SingleOrDefault().Name.GroupBy(Name);
Now the question would be to explain what exactly you are expecting from this? You are fetching a single Part by ID. How would you expect this to be "grouped"?
If you want to display the Part name instead of the PartId then I believe you would just want to Select the Part Name:
dgvEMSpending.Rows[dgvEMSpending.Rows.Count - 1].Cells[0].Value = db.Parts
.Where(x => x.ID == partid)
.Select(x => x.Name)
.SingleOrDefault();
We can go one better to fetch the Part names for each used product in one hit using our loaded order details:
var partIds = orderItemDetails
.Select(x=> x.PartId)
.Distinct()
.ToList();
var partDetails = db.Parts
.Where(x => partIds.Contains(x.ID))
.ToDictionary(x => x.ID, x => x.Name);
This fetches us a dictionary set indexed by ID for the part names, it would be done outside of the loop after we had loaded the orderItemDetails. Now we don't have to go to the DB with every row:
dgvEMSpending.Rows[dgvEMSpending.Rows.Count - 1].Cells[0].Value = partDetails[partId];

Optional compare in query

Hi i have a problem with my query because i want select that items which brands are called in string subcategory. But if that subcategory is equal "none" i would to select them all i want to do it in query not in linq. Here's my function
string subcategory = HttpContext.Current.Session["subcategory"].ToString() == "none" ? "" : HttpContext.Current.Session["subcategory"].ToString();
List<int> processors = (from x in context.Processors
where (x.Price >= filters.PriceMin && x.Price <= filters.PriceMax)
where x.Brand == subcategory
select x.Product_ID).ToList();
The pattern for this in LINQ and EF is to build up the query differently for the two cases before the query is executed by calling IQueryable.ToList(); eg:
string subcategory = ...;
var q = from x in context.Processors
where (x.Price >= filters.PriceMin && x.Price <= filters.PriceMax)
select x;
if (subcategory != "none")
{
q=q.Where(x => x.Brand == subcategory);
}
var processors = q.Select(x => x.Product_ID).ToList();

Axios: How to get data within axios

I created a search for a unique barcode. Therefore the result will be 0 or 1 because it is unique. If barcode is found, I need to get the ID of that record. How do we do this?
axios.get("api/findpatronbarcode?q=" + query)
.then(({data}) => {
this.loanpatrons = data.data;
//COUNT RECORDS
this.countPatrons = this.loanpatrons.length;
console.log(this.countPatrons);
//THE PROBLEM IS THE CODE BELOW. IT RETURNS "Undefined"
// Get the ID of the record
var getID = this.loanpatrons.id;
console.log(getID)
});
You can try like this:
axios.get("api/findpatronbarcode?q=" + query)
.then(({data}) => {
this.loanpatrons = data.data;
//COUNT RECORDS
this.countPatrons = this.loanpatrons.length;
console.log(this.countPatrons);
// KEEP IN MIND THAT "loanpatrons" is Array
// so first get the first member of the Array
// and only then Get the ID of the record
var getID = (this.loanpatrons[0] || {}).id || '';
console.log(getID)
});

How to do aggregate functions such as count in Entity Framework Core

I have this SQL that I would like to execute in Entity Framework Core 2.1:
Select ItemTypeId, Count(ItemTypeId) as Count from Items i
where i.ParentId = 2
group by ItemTypeId
How do I do that?
This is what I came up with, but it returns zero:
var query = this._context.Items.Where(a => a.ParentId == 2)
.GroupBy(i => new { i.ItemTypeId })
.Select(g => new { g.Key.ItemTypeId, Count = g.Count(i=> i.ItemTypeId == g.Key.ItemTypeId) });
var items = query.ToList();
The only example I could find was here
You don't need Count = g.Count(i=> i.ItemTypeId == g.Key.ItemTypeId), instead use g.Count().

Best way to order by children collection with Entity

I am seeking for the best solution for this simple problem.
Run in C#/Entity the following SQL:
select user.name, userstat.point from user, userstat where userstat.user_id = user.id order by userstat.point desc
There is a User table [Id, Name, ...] and Statistic table [Id, UserId, Point. ...], where it's connected to User by Statistic.UserId. It's a 1:1 relation, so there is (max) 1 Statistic record for each User record.
I want to have a list User+Point, ordered by Point desc, and select a range, let's say 1000-1100.
Currently I have this:
public List<PointItem> Get(int startPos, int count)
{
using (DB.Database db = new DB.Database())
{
var dbList = db.Users.Where(user => .... ).ToList();
List<PointItem> temp = new List<PointItem>(count);
foreach (DB.User user in db.Users)
{
//should be always 1 stat for the user, but just to be sure check it...
if (user.Stats != null && user.Stats.Count > 0)
temp.Add(new PointItem { Name = user.Name, Point = user.Stats.First().Point });
} <--- this foreach takes forever
return temp.OrderByDescending(item => item.Point).Skip(startPos).Take(count).ToList();
}
}
It works fine, but when I have 10000 User (with 10000 UserStat) it runs for 100sec, which is only 1000x slower than I want it to be.
Is there more efficient solution than this?
If I run SQL, it takes 0 sec basically for 10K record.
EDIT
I made it faster, now 100sec -> 1 sec, but still I want it faster (if possible).
var userPoint = db.Users
.Where(u => u.UserStats.Count > 0 && ....)
.Select(up => new
{
User = up,
Point = up.UserStats.FirstOrDefault().Point
})
.OrderByDescending(up => up.Point)
.ToList();
var region = userPoint.Skip(0).Take(100);
Ok, I found the solution, the following code is 0.05 sec. Just need to go from child to parent:
using (DB.Database db = new DB.Database())
{
var userPoint = db.UserStats
.Where(s => s.User.xxx .....)
.Select(userpoint => new
{
User = userpoint.User.Name,
Point = userpoint.Point
})
.OrderByDescending(userpoint => userpoint.Point)
.ToList().Skip(startPos).Take(count);
}