Join and get Distinct on Foreign Objects/Keys Linq - c#

I have a Model like this
public class Product
{
public int Id { get;set;}
...
public virtual ICollection<Supplier> Suppliers {get;set;}
}
public class Suppliers
{
public int Id {get;set;}
...
public string Name {get;set;}
}
My Linq query is constructed like this to get Products which are unique
var suppliers = _context.Products.Where(condition).Select(u => u.Suppliers).ToList(); //
The result of this query returns a List<Iqueryable<Supplier>> How do I get a list of Supplier Names from this list??

Thanks to Camilo Terevinto this query works.
var suppliers = _context.Products.Where(condition).SelectMany(u => u.Suppliers).GroupBy(u => u.Name).Select(u => u.First()).ToList()

You can try this
var supplier_names = _context.Products.Where(condition)
.SelectMany(u => u.Suppliers).Select(a => a.Name).ToList();

Related

Filtering a collection against a list of combinations in mongodb

We have a collection of users with the following class definition.
public class User
{
public Guid UserId { get;set; }
public Guid CompanyId { get;set; }
public Guid AddressTypeId { get;set; }
public Guid LocationId { get;set; }
public Guid DepartmentId { get;set; }
}
We're trying to query a list of users with a companyId, that matches a list of LocationFilter, with the following class definition.
public class LocationFilter
{
public Guid AddressTypeId { get;set; }
public ICollection<Guid> LocationIds { get;set; }
public ICollection<Guid> DepartmentIds { get;set; }
}
The querying API definition will look like this:
Task<List<User>> GetUserLocationList(
Guid companyId,
List<LocationFilter> locationFilters);
I have the following Builder queries if we're querying against a single LocationFilter.
var companyIdFilter = Builders<User>.Filter
.Eq(user => user.CompanyId, companyId);
var addressTypeIdFilter = Builders<User>.Filter
.Eq(user => user.AddressTypeId, addressTypeId);
var locationIdFilter = Builders<User>.Filter
.In(user => user.LocationId, locationIds);
var departmentIdFilter = Builders<User>.Filter
.In(user => user.DepartmentId, departmentIds);
var andFilter = Builders<User>.Filter
.And(
companyIdFilter,
addressTypeIdFilter,
locationIdFilter,
departmentIdFilter);
var result = await collection.Find(andFilter).ToListAsync();
But how do we filter against a list of locationfilter?
Each locationfilter is it's own, it cannot intersect.
For example, all users checked against location filter A, then against location filter B etc.
How can we achieve that using builder queries?
Is this the right approach?
You can use an Or to combine the location filters to find any users that matches (at least) one of the filters, e.g. (assuming that all users should be from the same company):
public FilterDefinition<User> BuildLocationFilter(LocationFilter locFilter)
{
var addressTypeIdFilter = Builders<User>.Filter
.Eq(user => user.AddressTypeId, locFilter.AddressTypeId);
var locationIdFilter = Builders<User>.Filter
.In(user => user.LocationId, locFilter.LocationIds);
var departmentIdFilter = Builders<User>.Filter
.In(user => user.DepartmentId, locFilter.DepartmentIds);
var andFilter = Builders<User>.Filter
.And(
companyIdFilter,
addressTypeIdFilter,
locationIdFilter,
departmentIdFilter);
return andFilter;
}
public FilterDefinition<User> BuildCompanyAndLocationFilters(
Guid companyId,
IEnumerable<LocationFilter> locFilters)
{
var locFilterDefs =
Builders<User>.Filter.Or(locFilters.Select(x => BuildLocationFilter(x)));
return Builders<User>.Filter.Eq(x => CompanyId, companyId)
& locFilterDefs;
}
This retrieves all the users of the company that fulfill at least one company filter condition.

Left outer join using LINQ Query Syntax EF Core C#

I have a question in regards with the below,
Left outer join of two tables who are not connected through Foreign Key.
Order by the results matched in second table.
I would like this to be done in LINQ Query method syntax as I am adding lots of conditions depending on the input provided along with skip and limit.
If we have below Product and Favorite tables
So the output that I would like to have is:
meaning with the favorites as part of first set and which are not favorites should be behind them. Below are the tries that I did.
I am able to join the tables get the output but not sure how I can make sure that in the first page I get all the favs.
This answer was very near to what I thought but it gets the result and then does the ordering which will not be possible in my case as I am doing pagination and using IQueryable to get less data.
Group Join and Orderby while maintaining previous query
Open to any solutions to achieve the same.
[Table("Product")]
public class ProductModel
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public Guid ProductId { get; set; }
public string ProductName {get; set;}
public bool IsFavorite { get; set; }
}
[Table("UserFavorite")]
public class UserFavoriteModel
{
[Required]
public Guid UserId { get; set; }
[Required]
public Guid Identifier { get; set; }
[Required]
public FavoriteType Type { get; set; }
}
// Gets products
private async Task<List<ProductModel>> GetProductsAsync(
Guid categoryId,
Guid subCategoryId,
int from,
int limit)
{
var query = _context.Products.AsQueryable();
if (!string.IsNullOrEmpty(categoryId))
query = query.Where(product => product.CategoryId == categoryId);
if (!string.IsNullOrEmpty(subCategoryId))
query = query.Where(product => product.SubCategoryId == subCategoryId);
query = query.Skip(from).Take(limit);
var products = await query.ToListAsync();
query = query.GroupJoin(
_context.Favorites.AsNoTracking()
.Where(favorite => favorite.Type == FavoriteType.FASHION)
// This user Id will come from context just adding for overall picture.
.Where(favorite => favorite.UserId == userId),
//This orderby if I add will not make any difference.
//.OrderByDescending(favorite => favorite.Identifier),
v => v.ProductId,
f => f.Identifier,
(product, fav) => new { product, fav }).
SelectMany(x => x.Fav.DefaultIfEmpty(),
(x, y) => SetFavorite(x.Project, y));
}
private static ProductModel SetFavorite(ProductModel v, UserFavoriteModel si)
{
v.IsFavorite = (si != null);
return v;
}
I would do something like this:
var query =
_context.Products.AsQueryable().Select(p => new ProductModel {
ProductId = p.ProductId,
ProductName = p.ProductName,
IsFavorite =
_context.Favorites.Any(f =>
f.Identifier = p.ProductId &&
f.Type == FavoriteType.FASHION &&
f.UserId == userId
)
}).OrderByDescending(favorite => favorite.Identifier);

GetAll groupjoin that populates list

I have a GetAll statement that grabs every person and populates the collection of tickets they have. My classes are abbreviated as follows:
public class Person
{
public int Id {get;set;}
public string Name {get;set;}
public ICollection<Ticket> Tickets {get;set;}
}
public class Ticket
{
public int Id {get;set;}
public int PersonId {get;set;}
public string MovieName {get;set;}
}
I'm using entity framework to generate the classes with linq from the dbcontext to populate the request.
public async Task<List<Person>> GetPersonsAsync()
{
return await _context.Person.GroupJoin(_context.Ticket,
p => p.Id,
c => c.PersonId,
(per, tix) => PopulatePerson(per,tix) )
.ToListAsync();
}
private Person PopulatePerson(Person per, IEnumerable<Ticket> tix)
{
per.Tickets= tix.ToList();
return per;
}
This works but I had to create the seperate private PopulatePerson method in order to accomplish this. How can I avoid creating a private method and populate the list of tickets in the same GroupJoin statement?
You can use Include() and let EntityFramework do the Joins for you:
public async Task<List<Person>> GetPersonsAsync()
{
return await _context.Person
.Include(p => p.Tickets)
.ToListAsync();
}

LINQ Query for getting count in ASP.Net MVC 5 Application

Need help in writing LINQ Query to get count of employee based on department.
Department Model
public class Department
{
public int Id { get; set; }
public string Name { get; set; }
public List<Employee> Employees { get; set; }
}
Employee Model
public class Employee
{
public int Id { get; set; }
public string Name { get; set; }
public int DepartmentId { get; set; }
public Department Department { get; set; }
}
MyViewModel
public class MyViewModel
{
public string Department { get; set; }
public int count { get; set; }
}
LINQ Query
public ActionResult shows()
{
//one department can have many employees and one employee can only be parrt of one department
// below LINQ query fetches the count of employees belonging to each department
var x = _context.Employees.Include("Department").GroupBy(e => e.DepartmentId)
.Select(y=> new MyViewModel{
Department= y.Key, // ERROR HERE AS Cannot type cast string to integer
count = y.Count()
}).ToList();
// code removed for brevity
return Content("x");
}
Output Expected
Department Count (or employees)
Human Resource 10
Information Tech 5
Question
How to write a LINQ Query to get the output as above. Please guide me.
You have 2 options for modifying your query. I am assuming your Department model contains a property string Name which you want to assign to the Department property of your view model.
Get the first Department in the grouped collection and assign its
Name property to your view models Department property. Note that
you need to add .ToList() before .GroupBy() so that the
first part of the query is executed before grouping
var x = _context.Employees
.Include("Department")
.ToList()
.GroupBy(e => e.DepartmentId)
.Select(y => new MyViewModel
{
Department = y.First().Department.Name,
count = y.Count()
}).ToList();
Change the .GroupBy() expression to group by the Name property
of the Department property
var x = _context.Employees
.Include("Department")
.GroupBy(e => e.Department.Name)
.Select(y=> new MyViewModel
{
Department = y.Key,
count = y.Count()
}).ToList();
public ActionResult shows()
{
//one department can have many employees and one employee can only be parrt of one department
// below LINQ query fetches the count of employees belonging to each department
var x = _context.Employees.Include("Department").GroupBy(e => new { e.DepartmentId, e.Department.Name})
.Select(y=> new MyViewModel{
Department= y.Key.Name
count = y.Count()
}).ToList();
// code removed for brevity
return Content("x");
}
the key here is to group by DepartmentId AND Name
Try it like this:
public ActionResult shows()
{
//one department can have many employees and one employee can only be parrt of one department
// below LINQ query fetches the count of employees belonging to each department
var x = _context.Employees.Include("Department").GroupBy(e => e.DepartmentId)
.Select(y=> new MyViewModel{
Department= y.Key.DepartmentName, // or whatever property you have for storing the name
count = y.Count()
}).ToList();
// code removed for brevity
return Content("x");
}
you can use this code :
using System.Linq;
using System.Web.Mvc;
using MvcApplication1.Models;
namespace MvcApplication1.Controllers
{
     
     public class HomeController : Controller
     {
          public ActionResult Index()
          {
               var dataContext = new MovieDataContext();
int count = (from row in dataContext.Movies
                    where row.IsEnquiry == true
select row).Count();
ViewBag.ItemCount = count;
               return View();
          }
     }
}

linq select items from child collection

Below are my classes. I have a product that contains list of days. Each day has a city property.
I need to create a linq query that will give me the distinct cities that are used on all my products in the system.
I tried something like this but it does not work:
var cities = from product in NHibernateSession.Linq<Product>() select new { city = product.Days.Where(d => d.City != null).Distinct() }; //This returns the day items but i need distinct cities
public class Product : EntityBase
{
public virtual string Name { get; set; }
public virtual IList<ProductDayDefinition> Days { get; set; }
}
public class ProductDayDefinition : EntityBase
{
public virtual Product Product { get; set; }
public virtual City City { get; set; }
}
You need to call the SelectMany function, which takes a single item and lets you get multiple items from it.
For example:
var cities = NHibernateSession.Linq<Product>()
.SelectMany(p => p.Days)
.Select(p => p.City)
.Where(c => c != null)
.Distinct();
Note that if the City class doesn't implement Equals and GetHashCode correctly, this will return duplicates.
You can do this using query comprehension syntax like this: (Untested)
var cities = (from product in NHibernateSession.Linq<Product>()
from day in product.Days
where day.City != null
select day).Distinct();

Categories

Resources