I have the MySql tables schema below (resumed):
I need to Select only the category data in a Query using EFCore:
List<CategoryViewModel> viewModel = await _context.Category
.Join(_context.Product_Category, c => c.CategoryId, pc => pc.CategoryId, (c, pc) => new { c, pc })
.Join(_context.Product, cpc => cpc.pc.ProductId, p => p.ProductId, (cpc, p) => new { cpc, p })
.Where(cpcp => cpcp.p.EstablishmentId == paramEstablishmentId) //paramEstablishmentId comes via parameter
.Select(vm => new CategoryViewModel()
{
Id = vm.cpc.pc.category.CategortId,
Name = vm.cpc.pc.category.Name,
Image = vm.cpc.pc.category.ImagePath,
Description = vm.cpc.pc.category.Description
})
.ToListAsync();
But this query always result a list with zero models inside. I guarantee there are values in the database to be returned.
Any Ideia what i'm doing wrong?
Many Thanks!
You should use Include()function instead of join. For eg :
var blogs = context.Blogs
.Include(blog => blog.Posts)
.ToList();
Based on #Flyzzx Answer (many thanks, friend), i've modify my query to:
List<CategoryViewModel> viewModel = await _context.Product_Category
.Where(pc => pc.Product.EstablishmentId == EstablishmentId)
.Include(pc => pc.Product)
.Include(pc => pc.Category)
.Select(c => new CategoryViewModel()
{
Id = c.Category.Id,
Name = c.Category.Name,
Image = c.Category.ImagePath,
Description = c.Category.Description
}).Distinct()
.ToListAsync();
Basically, instead of select Categories, now i select Product_Category and use Include to add Products and Categories, making possible to use the Where Clause.
Related
I have a result set of users in a particular position.
Now if I want to exclude users from another list that contains a list of users, this will work:
var toExclude = dc.Users.Where(p => p.SomeProperty == true);
positionUsers = positionUsers.Where(p => !toExclude.Select(x => x.UserID).Contains(p.UserID));
But now my exclusion list contains a pair of users + positions. How do I exclude them now?
If I simply do an && it will exlude all users and all positions in the exclusion list, and not the combination. In other words in needs to do an outer join on 2 fields.
var toExclude = dc.UserPositions.Where(p => p.SomeProperty == true).Select(p => new { p.User, p.Position});
positionUsers = positionUsers
.Where(p => !toExclude
.Select(x => x.UserID + Position)
.Contains(p.UserID + Position)); //Obviously this is wrong, but will hopefully explain the requirement.
This works:
var toExclude = dc.UserPositions
.Where(p => p.SomeProperty == true)
.Select(p => new { p.User, p.Position});
positionUsers = positionUsers
.Where(p => !toExclude
.Select(x => new { x.UserID, Position })
.Contains(new { p.UserID, Position }));
I have a query table like this, people let me ask how can I get the Positions data of the current user when I get the userid to query with the Document table.
var claimsIdentity = _httpContextAccessor.HttpContext.User.Identity as ClaimsIdentity;
var userId = claimsIdentity.FindFirst(ClaimTypes.NameIdentifier)?.Value.ToString();
var query = from c in _context.Documents
join u in _context.Users on c.UserID equals u.Id
join p in _context.Positions on u.Id equals p.UserID
where c.UserID.ToString() == userId
select new { c, u, p };
The data you query is almost enough, but it contains duplicate entries of Document and Position. If you want the final query to be put in a single object like this:
{
User = ...,
Documents = ...,
Positions = ...
}
You just need to project it using Linq-to-object (because all the data is loaded and ready for projection on the client):
var result = (from document in _context.Documents
join user in _context.Users on document.UserID equals user.Id
join position in _context.Positions on user.Id equals position.UserID
where document.UserID.ToString() == userId
select new { document, user, position }).AsEnumerable()
.GroupBy(e => e.user.Id)
.Select(g => new {
User = g.First().user,
Documents = g.GroupBy(e => e.document.Id)
.Select(e => e.First().document),
Positions = g.GroupBy(e => e.position.Id)
.Select(e => e.First().position)
}).FirstOrDefault();
If you don't want to fetch the user info, you don't need to join that DbSet but instead join the two Document and Position directly like this:
var result = (from document in _context.Documents
join position in _context.Positions on document.UserID equals position.UserID
where document.UserID.ToString() == userId
select new { document, position }).AsEnumerable()
.GroupBy(e => e.document.UserID)
.Select(g => new {
Documents = g.GroupBy(e => e.document.Id)
.Select(e => e.First().document),
Positions = g.GroupBy(e => e.position.Id)
.Select(e => e.First().position)
}).FirstOrDefault();
Note that I suppose your Document and Position both have its own primary key property of Id (adjust that to your actual design).
Finally, usually if your User entity type exposes navigation collection properties to the Document and Position. We can have a better (but equal) query like this:
var user = _context.Users
.Include(e => e.Documents)
.Include(e => e.Positions)
.FirstOrDefault(e => e.Id.ToString() == userId);
It's much simpler because all the joining internally translated by the EFCore. The magic is embedded right into the design of navigation collection properties.
I would like to talk about the important note of the condition UserID.ToString() == userId or Id.ToString() == userId. You should avoid that because it would be translated into a query that breaks the using of index for filtering. Instead try parsing for an int userId first (looks like it's a string in your case) and use that parsed int directly for comparison in the query, like this:
if(!int.TryParse(userId, out var intUserId)){
//return or throw exception
}
//here we have an user id of int, use it directly in your query
var user = _context.Users
.Include(e => e.Documents)
.Include(e => e.Positions)
.FirstOrDefault(e => e.Id == intUserId);
That applies similarly to other queries as well.
SELECT StepID, count() as nb FROM Question GROUP BY StepID ORDER by nb;
You should probably go through the basics of LINQ. Microsoft Docs has a whole section dedicated to LINQ: https://learn.microsoft.com/en-us/dotnet/csharp/programming-guide/concepts/linq/
If you have your data in a List called as questions of type, List<Question> then you should be able to convert your Query like this:
var ret = from q in questions
group q by q.StepId into grouped
let count = grouped.Count()
orderby count
select new { StepId = grouped.Key, nb = count };
Query comprehension syntax:
from q in questions
group q by q.StepId into g
select new { StepId = g.Key, Count = g.Count() } into stepCount
orderby stepCount.Count
select stepCount;
Exact same in method syntax (which I prefer, since it can all query syntax can plus more and also often is more compact):
questions
.GroupBy(q => q.StepId)
.Select(g => new { StepId = g.Key, Count = g.Count() })
.OrderBy(stepCount => stepCount.Count)
Variant using another GroupBy overload:
questions
.GroupBy(q => q.StepId, (key, values) => new { StepId = key, Count = values.Count() })
.OrderBy(stepCount => stepCount.Count);
i used this query:
$brands = TblBrand::find(array("id In (Select p.brand_id From EShop\\Models\\TblProduct as p Where p.id In (Select cp.product_id From EShop\\Models\\TblProductCategory as cp Where cp.group_id_1='$id'))", "order" => "title_fa asc"));
if($brands != null and count($brands) > 0)
{
foreach($brands as $brand)
{
$brandInProductCategory[$id][] = array
(
"id" => $brand->getId(),
"title_fa" => $brand->getTitleFa(),
"title_en" => $brand->getTitleEn()
);
}
}
TblBrand => 110 records
TblProduct => 2000 records
TblProductCategory => 2500 records
when i used this code, my site donot show and loading page very long time ...
but when i remove this code, my site show.
how to solve this problem?
The issue is your query. You are using the IN statement in a nested format, and that is always going to be slower than anything else. MySQL will need to first evaluate what is in the IN statement, return that and then do it all over again for the next level of records.
Try simplifying your query. Something like this:
SELECT *
FROM Brands
INNER JOIN Products ON Brand.id = Products.brand_id
INNER JOIN ProductCategory ON ProductCategory.product_id = Products.id
WHERE ProductCategory.group_id_1 = $id
To achieve the above, you can either use the Query Builder and get the results that way
https://docs.phalconphp.com/en/latest/api/Phalcon_Mvc_Model_Query_Builder.html
or if you have set up relationships in your models between brands, products and product categories, you can use that.
https://docs.phalconphp.com/en/latest/reference/model-relationships.html
example:
$Brands = Brands::query()
->innerJoin("Products", "Products.brand_id = Brand.id")
->innerJoin("ProductCategory", "ProductCategory.product_id = Products.id")
->where("ProductCategory.group_id_1 = :group_id:")
->bind(["group_id" => $id])
->cache(["key" => __METHOD__.$id] // if defined modelCache as DI service
->execute();
$brandInProductCategory[$id] = [];
foreach($Brands AS $Brand) {
array_push($brandInProductCategory[$id], [
"id" => $Brand->getId(),
"title_fa" => $Brand->getTitleFa(),
"title_en" => $Brand->getTitleEn()
]);
}
I am trying to use two left outer joins with Zend Framework 2's SQL classes but for some reason it is not returning one result but the other one is working fine. I've ran the actual SQL in MySQL Workbench and it returns just like I want but it is not doing it with Zend Framework. Here is my code:
Pure SQL:
SELECT groups.group_name, members.username, groups.id FROM groups
LEFT OUTER JOIN group_admins ON groups.id = group_admins.group_id
LEFT OUTER JOIN members ON group_admins.user_id = members.id
WHERE group_admins.user_id = " . parent::getUserId()['id']
This returns the result I wish, (which can be seen here: http://imgur.com/8ydmn4f)
Now, here is the Zend Framework 2 code I have in place:
$select_admins = new Select();
$select_admins->from(array(
'g' => 'groups',
))
->join(array(
'ga' => 'group_admins'
), 'g.id = ga.group_id')
->join(array(
'm' => 'members'
), 'ga.user_id = m.id', array('username'))
->where(array('ga.user_id' => parent::getUserId()['id']));
$query_group_admin = parent::$sql->getAdapter()->query(parent::$sql->buildSqlString($select_admins), Adapter::QUERY_MODE_EXECUTE);
$group_admins = array();
foreach ($query_group_admin as $group_admin) {
$group_admins[] = $group_admin;
}
// get the group members
$select = new Select();
$select->from(array(
'g' => 'group_members'
))
->join(array(
'm' => 'members'
), 'g.member_id = m.id')
->join(array(
'grp' => 'groups'
), 'g.group_id = grp.id')
->where(array(
'g.group_id' => $group_id
));
$query = parent::$sql->getAdapter()->query(parent::$sql->buildSqlString($select), Adapter::QUERY_MODE_EXECUTE);
$member_username = array();
foreach ($query as $member) {
$member_username[] = $member['username'];
}
// get the rest of the group info
$fetch = $this->gateway->select(array(
'id' => $group_id
));
$row = $fetch->current();
if (!$row) {
return false;
}
return array(
'admins' => implode(", ", $group_admins),
'members' => implode(", ", $member_username),
'info' => $row
);
Controller:
public function grouphomeAction()
{
$id = $this->params()->fromRoute('id', 0);
if (0 === $id) {
return $this->redirect()->toRoute('members/groups', array('action' => 'index'));
}
if (!$this->getGroupsService()->getGroupInformation($id)) {
return $this->redirect()->toRoute('members/groups', array('action' => 'index'));
}
return new ViewModel(array('group_info' => $this->getGroupsService()->getGroupInformation($id)));
}
However, this only shows the group name, group creator and group members but leave the group admins field empty.
Here is the print_r result of the array returned:
Array ( [admins] => [members] => jimmysole, fooboy [info] => ArrayObject Object ( [storage:ArrayObject:private] => Array ( [id] => 2 [group_name] => Tim's Group [group_creator] => timlinden [group_created_date] => 2017-01-16 17:39:56 ) ) )
If it helps, here is a screenshot as well of the page - http://imgur.com/xUQMaUu
Any help would be appreciated!
Thanks.
Basically your joins are INNER JOINS...I know....you must hate Zend right now :p . By default they are INNER JOINS so i assume that is what is wrong. SO try to specify the type of join and you should be fine. You can find more examples here: examples
$select12->from('foo')->join('zac', 'm = n', array('bar', 'baz'), Select::JOIN_OUTER);