I have this query
[HttpGet]
public List<AttachedPhotosModel> GetReportAttachedPhotos(int reportId)
{
var photos = new ReportsRepository().GetInjuryPhotos(reportId);
return photos.Select(x => new AttachedPhotosModel()
{
Id = x.Id,
Type = x.InjuryType,
Photos = photos.Where(y => y.InjuryType == x.InjuryType).Select(z => z.ServicePhotoUrl).ToList()
}).ToList();
}
I need to GroupBy InjuryType, how to do this?
I added return photos.GroupBy(k => k.InjuryType).Select(x => new AttachedPhotosModel() but how to select model, x have new value key and I don't know how to select my data
This code should work. Assuming photos is collection of objects with InjuryType property and PhotoUrl property and AttachedPhotosModel has an InjuryType and Photos properties like this.
public class AttachedPhotosModel
{
public string InjuryType { set; get; }
public List<string> Photos { set; get; }
}
Code for grouping by InjurType.
var grouped = photos
.GroupBy(s => s.InjuryType,d => d.PhotoUrl, (k, g) => new
AttachedPhotosModel
{
InjuryType = k,
Photos = g.ToList()
}).ToList();
Related
So, I have my Products table in SSMS with these properties:
public class Product
{
public int Id {get; set;}
public string Title { get; set; }
public decimal Price { get; set; }
}
and my Reports table:
public class Report
{
public int Id { get; set; }
public int ProductId { get; set; }
public ReportType ReportType { get; set; }
}
I want to return a List<Product> to my View that is sorted based on how many reports each Product has, but I can't figure out how to do it with LINQ. Any help/tip would be appreciated.
If you put nav props in this would be:
context.Products.Include(p => p.Reports).OrderBy(p => p.Reports.Count(*));
But as you have no nav props, perhaps something like:
context.Products.OrderBy(p => context.Reports.Count(r => r.ProductId == p.Id));
The query ends up looking like this for the latter:
SELECT *
FROM p
ORDER BY (SELECT COUNT(*) FROM r WHERE p.id = r.id)
and similar but with a left join, for the former
You could also do it on the client side
var dict = context.Reports.GroupBy(r => ProductId, (k,g) => new { ProductId, Count = g.Count() } )
.ToDictionary(at => at.ProductId, at => at.Count);
Then:
//or OrderByDescending if you want most reported products
var ret = context.Products.ToList().OrderBy(p => dict[p.ProductId]);
If you have some limited list of products:
var prods = context.Products.Where(...).ToList();
var prodIds = prods.Select(p => p.ProductId).ToArray();
var dict = context.Reports
.Where(r => prods.Contains(r.ProductId))
.GroupBy(r => ProductId, (k,g) => new { ProductId, Count = g.Count() } )
.ToDictionary(at => at.ProductId, at => at.Count)
var ret = prods.OrderBy(p => dict[p.ProductId]);
This code is performing well as expected.
var soldQtyForEachItem = await _context.InvoiceProduct
.Where(x => x.ProductId != 0)
.GroupBy(x => x.ProductId)
.Select(grp => new CompanyProducts
{
CompanyProductId = grp.Key,
CompanyProductSoldQuantity = grp.Sum(item => item.QuantitySold)
}).ToListAsync();
Question :
I now need to join another table called Products and filter by Id against InvoiceProduct table and retrieve
ProductsItemName which is a row from Products table; then all need to go to a custom type "CompanyProducts" below.
Please how do I achieve it?
public class CompanyProducts
{
public int CompanyProductId { get; set; }
public int CompanyProductName { get; set; }
public int CompanyProductSoldQuantity { get; set; }
}
InvoiceProduct table is a many to many, meaning that one InvoiceId may have multiple ProductIds.
Products table has property like ProductId, ProductsItemName.
Simplest solutions is to include ProductItemName into grouping:
var soldQtyForEachItem = await _context.InvoiceProduct
.Where(x => x.ProductId != 0)
.GroupBy(x => new { x.ProductId, x.Product.ProductsItemName } )
.Select(grp => new CompanyProducts
{
CompanyProductId = grp.Key.ProductId,
CompanyProductName = grp.Key.ProductsItemName,
CompanyProductSoldQuantity = grp.Sum(item => item.QuantitySold)
})
.ToListAsync();
Model:
public class Ticket {
public Ticket();
public int Id { get; set; }
public virtual TicketUrgency TicketUrgency { get; set; }
public int UrgencyId { get; set; }
}
public class TicketUrgency {
public TicketUrgency();
[Key]
public int Id { get; set; }
[MaxLength(50)]
[Required]
public string Name { get; set; }
public ICollection<Ticket> Tickets { get; set; }
}
I have the following linq statement:
var model = Entities
.Include(x => x.TicketUrgency)
.GroupBy(x => x.UrgencyId)
.Select(g => new {
id = g.Key,
count = g.Count(),
name = g.FirstOrDefault(u => u.UrgencyId == g.Key).TicketUrgency.Name
});
I want to Group Entities by UrgencyId and then return the Key (UrgencyId), and also count of the items in a single group and show the name of the Urgency.
When I run it, the query just gets hung up without any exceptions.
This should work, doing it the other way around, by retrieving all TicketUrgencies first and grouping it.
Entities.Include(e => e.Tickets)
.GroupBy(t => t.Id)
.Select(g => new {
id = g.Key,
name = g.FirstOrDefault().Name,
count = g.FirstOrDefault().Tickets.Count()
});
Very simple. Just try this :
var model = Entities
.Include(x => x.TicketUrgency)
.GroupBy(x => new {UrgencyId = x.UrgencyId ,
Name = x.TicketUrgency.Name})
.Select(x=> new { UrgencyId = x.Key.UrgencyId,
Name = x.Key.Name,
Count = x.Count()});
Since you are grouping by UrgencyId, you know all members of g have the same id as the Key, so to pick up the name just pull the first one. You also know g isn't empty because that wouldn't make a group:
var model = Entities
.Include(x => x.TicketUrgency)
.GroupBy(x => x.UrgencyId)
.Select(g => new {
id = g.Key,
name = g.First().TicketUrgency.Name
count = g.Count(),
});
You could group by those two properties:
var model = Entities
.Include(x => x.TicketUrgency)
.GroupBy(x => new{ x.UrgencyId, x.TicketUrgency.Name })
.Select(g => new {
id = g.Key.UrgencyId,
count = g.Count(),
name = g.Key.Name
});
Another way could be, as #ASpirin suggested,starting the query from TickerUrgency:
var result= TicketUrgencies.Include(t=>t.Tickets)
.Where(t=>t.Tickets.Any())
.Select(t=> new {id=t.Id,name=t.Name, count= t.Tickets.Count()})
I have the following entities:
public class Parent
{
int Id { get; set; }
string ParentName { get; set; }
List<Child> Children { get; set; }
}
public class Child
{
int Id { get; set; }
string ChildName { get; set; }
}
and the following dto:
public class ParentDTO
{
int Id { get; set; }
List<string> ChildrenNames { get; set; }
}
using QueryOver code below I can get the Parent values
ParentDTO result = null;
Parent parentAlias = null;
Child childAlias = null;
var query = session.QueryOver(() => parentAlias)
.JoinAlias(() => parentAlias.Children, () => childAlias, JoinType.LeftOuterJoin)
.SelectList(list => list.Select(c => c.Id).WithAlias(() => result.Id)
.Select(c => c.ParentName).WithAlias(() => result.Name)
//this part does not work
.Select(c => c.Children .Select(v => v.ChildName)).WithAlias(() => result.ChildrenNames)
//
)
.TransformUsing(Transformers.AliasToBean<ParentDTO>());
return query.List<ParentDTO>();
However I cant seem to be able to project the list of childName values into my ChildrenNames collection.
Any ideas?
As some guys said in comments, you need to do two queries. Using linq, you could try something like this:
// get the parent Ids
var parentIds = session.Query<Parent>().Select(c => c.Id).ToList();
// get the childNames
var childNames = session.Query<Child>()
.Where(x => parentIds.Contains(x.ParentId)) // get on the child from parents query
.Select(x => new {x.Name, x.ParentId}) // get only the properties you need
.ToList(); // list of anon objects
// loop in memory between parentIds filling the corresponding childNames
var result = parentIds.Select(parentId => new ParentDTO()
{
Id = parentId,
ChildrenNames = childNames.Where(x => x.ParentId == parentId).ToList()
}).ToList();
I am not sure if it works, but you could try this in a single query:
var query = from p in session.Query<Parent>()
let names = p.Children.Select(c => c.ChildName).ToList()
select new ParentDTO()
{
Id = o.Id,
ChildrenNames = names
};
return query.Tolist();
Obs: I did not test it.
I've got a method I've been using against IEnumerable no problem. However I want to start using it against IQueryable as well. However, the way I currently have it wont work as its trying to execute my method against the database.
The situation is as follows. I want a property on the object I'm selecting into to be be null if the value selecting from is null or the Id and Name of the property if it exists. For example:
var foos = FooRepository.All().Select(s => new FooBrief()
{
Id = s.Id,
Customer = SimpleData.To(s.Customer, m => m.Id, m => m.Name)
});
where SimpleData.To looks like:
public class SimpleData
{
public int Id { get; set; }
public string Name { get; set; }
public static SimpleData To<T>(T t, Func<T, int> id, Func<T, string> name) where T : class
{
if (t != null)
{
return new SimpleData { Id = id(t), Name = name(t) };
}
return null;
}
}
Is there someway I can get this behaviour whilst allowing it to execute against the database?
NOTE: Because of reasons elsewhere in my code I cannot use .ToList(). I may be adding additional filtering at a later point
The simplest approach is just to perform the selection outside the database, using AsEnumerable:
var foos = FooRepository.All()
.Select(x => new { Id = x.Id,
CustomerId = x.Customer.Id,
CustomerName = x.Name })
.AsEnumerable()
.Select(s => new FooBrief {
Id = s.Id,
Customer = new SimpleData {
Id = s.CustomerId,
Name = s.CustomerName
}
});
The first Select is just to make sure that the database query only pulls out the required fields. If you really still want to use your SimpleData.To method:
// Outdented to avoid scrolling
var foos = FooRepository.All()
.Select(x => new { Id = x.Id,
CustomerId = x.Customer.Id,
CustomerName = x.Name })
.AsEnumerable()
.Select(s => new FooBrief {
Id = s.Id,
Customer = SimpleData.To(s,
s => s.CustomerId,
s => s.CustomerName)
});