I am having trouble getting an IQueryable list of a (subsonic) object grouped by Month and Year.
Basic view of the object...
public partial class DatabaseObject
{
[SubSonicPrimaryKey]
public int objectID { get; set; }
public string Description { get; set; }
public decimal Value { get; set; }
public string Category { get; set; }
public DateTime DateOccurred { get; set; }
}
Method to get IQueryable in my Database repository...
public IQueryable GetData(string DataType)
{
return (from t in db.All<DatabaseObject>()
orderby t.DateOccurred descending
select t)
.Where(e => e.Category == DataType);
}
My question is, how can I return the dates grouped by Month? I have tried the below, but this results in compiler warnings regarding anonymous types...
public IQueryable GetData(string DataType)
{
var datalist = (from t in db.All<FinancialTransaction>().Where(e => e.Category == DataType);
let m = new
{
month = t.DateOccurred.Month,
year = t.DateOccurred.Year
}
group t by m into l select new
{
Description = string.Format("{0}/{1}", l.Key.month, l.Key.year),
Value = l.Sum(v => v.Value), // Sum(v => v.Value),
Category = "Grouped"
DateOccurred = l.Last(v => v.DateOccurred)
}
return datalist;
}
Any ideas?
Try this couple issues i found, but you basically need to select a Database object versus anonymous type?
IQueryable<DatabaseObject> datalist = (
from t in db.All<FinancialTransaction>().Where(e => e.Category == DataType)
let m = new
{
month = t.DateOccurred.Month,
year = t.DateOccurred.Year
}
group t by m into l
select new DatabaseObject()
{
Description = string.Format("{0}/{1}", l.Key.month, l.Key.year),
Value = l.Sum(v => v.Value), //Sum(v => v.Value),
Category = "Grouped",
DateOccurred = l.Max(v => v.DateOccurred)
}).AsQueryable();
Let me know if my solution is now what you want. I also noticed you were using Last? The extension you were using I do not have so I replaced it with Max. I don't have subsonic installed so it might come with the libraries.
Any way don't combine LINQ in query syntax and LINQ in extension methods syntax. Use next:
from t in db.All<DatabaseObject>()
where e.Category equals DataType
orderby t.DateOccurred descending
select t;
The issue is apparantly to do with the way Subsonic interprests certain linq statements and is a known bug.
IEnumerable<DatabaseObject> datalist = (
from t in db.All<FinancialTransaction>().Where(e => e.Category == DataType).ToList()
let m = new
{
month = t.DateOccurred.Month,
year = t.DateOccurred.Year
}
group t by m into l
select new DatabaseObject()
{
Description = string.Format("{0}/{1}", l.Key.month, l.Key.year),
Value = l.Sum(v => v.Value), //Sum(v => v.Value),
Category = "Grouped",
DateOccurred = l.Max(v => v.DateOccurred)
}).AsQueryable();
I have fixed this by declaring an list of type IEnumerable and using ToList() to cast the database interaction, finally the query is then recast AsQueryable()
Related
I have an one-to-many relation in my entities:
public class Header
{
public Header()
{
Items = new List<Item>
}
public int Id {get; set;}
public virtual ICollection<Item> Items {get; set;}
// other properties
}
public class Item
{
public int Id {get; set;}
public virtual Header Header { get; set; }
public string Title { get; set; }
public DateTime CreationDate { get; set; }
public int Weight { get; set; }
}
I want to load Header and some of its Items, so I wrote this linq to entity query(EF6):
using(var ctx = new MyContext())
{
var result = ctx.Headers.Where(someConditions)
.AsNoTracking()
.Select(header => new {
HeaderId = header.Id,
//fetch other header properties here
LastItemCreationDate = header.Items.OrderByDescending(item => item.CreationDate)
.Select(item => item.Title)
.FirstOrDefault(),
LastItemTitle = header.Items.OrderByDescending(item => item.CreationDate)
.Select(item => item.CreationDate)
.FirstOrDefault(),
LastItemWeight = header.Items.OrderByDescending(item => item.CreationDate)
.Select(item => item.Weight)
.FirstOrDefault()
}).ToList();
}
This query generate a sql script with 3 times join Header and Item tables, is there any more efficent way to write this query to join Header and Item tables one time?
Since you are using Select, you don't need AsNoTracking since the resulting query will not load any entities. The key performance impacts in your case would be indexes in the Header table suitability for your Where clause, then also whether there is a Descending Index available on the CreationDate in the Items table.
Another improvement would be to alter the projection slightly:
var result = ctx.Headers.Where(someConditions)
.Select(header => new {
HeaderId = header.Id,
LatestItem = header.Items
.OrderByDescending(item => item.CreatedDate)
.Select(item => new
{
Title = item.Title,
CreationDate = item.CreationDate,
Weight = item.Weight
}).FirstOrDefault()
}).ToList();
This will change the resulting anonymous type structure a bit, but should result in a nicer, single join.
You will get a result.HeaderId, then a null-able result.LastestItem containing the latest item's properties. So result.LatestItem?.Title instead of result.Title.
I need a help in LINQ Query for the below.
public interface IBrand
{
int BrandId { get; set; }
IEnumerable<IBuyingAgency> BuyingAgencies { get; set; }
}
public interface IBuyingAgency
{
int BuyingAgencyId { get; set; }
}
public interface IClientGroup
{
IBuyingAgency BuyingAgency { get; set; }
int ClientGroupId { get; set; }
}
1). var brands = LoggedInUserHelper.GetUser().GetBrands(roles); // returns IEnumerable<Tuple<IBrand, string>>
2). var buyingAgencies = LoggedInUserHelper.GetUser().GetBuyingAgencies(roles); //IEnumerable<IBuyingAgency>
3). var clientGroups = LoggedInUserHelper.GetUser().GetClientGroups(roles); //IEnumerable<IClientGroup>
function IEnumerable<IClientGroup> GetClientGroups( List<int> BrandIds)
{
var brands = LoggedInUserHelper.GetUser().GetBrands(roles); // returns IEnumerable<Tuple<IBrand, string>>
var buyingAgencies = LoggedInUserHelper.GetUser().GetBuyingAgencies(roles); //IEnumerable<IBuyingAgency>
var clientGroups = LoggedInUserHelper.GetUser().GetClientGroups(roles); //IEnumerable<IClientGroup>
var lstBrandagencies = brands.Where(brand => BrandIds.Contains(brand.Item1.BrandId) && brand.Item1.BuyingAgencies.Any( ba => buyingAgencies.Contains(ba.BuyingAgencyId))).SelectMany(brand => brand.Item1.BuyingAgencies);
var buyingAgencyIDs = lstBrandagencies.Select(b => b.BuyingAgencyId);
clientGroups = clientGroups.Where(cg => buyingAgencyIDs.Contains(cg.BuyingAgency.BuyingAgencyId));
return Mapper.Map<IEnumerable<IClientGroup>>(clientGroups.ToList());
}
I wrote the above function but not working, it gets all the clientgroups instead of filtering
I wants to write a query to get all ClientGroups whch satisfies the below condition
1. retrieve the brand from brands ( above ) that matches the list of brandId's passing in as parameter
2. Than get all the buyingAgencies under brands (1) above which matches with the id's of (2) above
3. Finally get all clientgroups which matches with the buyingAgency retrieving in step (2)
Please can you help.
you are not filtering from your source 2) in this line
var buyingAgencyIDs = lstBrandagencies.Select(b => b.BuyingAgencyId);
just projecting from the previous query.
If I understood correctly you want to do this.
var lstBrandagencies = (from a in brands
where BrandIds.Contains(a.Item1.BrandId )
select a).SelectMany (b => b.Item1.BuyingAgencies )
.Select (b => b.BuyingAgencyId );
var buyingAgencyIDs = from a in buyingAgencies
where lstBrandagencies.Contains(a.BuyingAgencyId )
select a.BuyingAgencyId;
var clientGroupsResult = clientGroups.Where(cg => buyingAgencyIDs.Contains(cg.BuyingAgency.BuyingAgencyId));
I have written following LINQ query to return list and then iterating list separately to convert time into hours and taking sum of hours for each list item.
I am sure this might not be the right away as it send three database calls.
Can i re-write it in a better way e.g. GroupBy or by some other way to assign data to model, converting IdleTime into hours and than taking sum of IdleTime??
Model Class
public class TestModel
{
public string TaskSummary { get; set; }
public string LocationName { get; set; }
public float IdleTime { get; set; }
public string Description { get; set; }
public float IdleTimeSum { get; set; }
public Guid TaskId { get; set; }
}
LINQ Query
List<TestModel> list = _context.Time
.Include(x => x.Report)
.Include(x => x.Report.Task)
.Include(x => x.Report.Task.Location)
.Where(x => taskIds.Contains(x.Report.TaskId))
.Select(x => new TestModel
{
TaskSummary = x.Report.Task.Summary,
LocationName = x.Report.Task.Location.Name,
IdleTime = x.Duration,
Description = x.Description,
TaskId = x.Report.TaskId,
}).ToList();
How i am converting into hours?
foreach (var item in list)
item.IdleTime = item. IdleTime / 60;
How I am taking sum?
foreach (var item in list)
item.IdleTimeSum = item. IdleTime;
Just add those to your projection:
.Select(x => new TestModel
{
TaskSummary = x.Report.Task.Summary,
LocationName = x.Report.Task.Location.Name,
IdleTime = x.Duration / 60.0f,
Description = x.Description,
TaskId = x.Report.TaskId,
NPTHoursSum = x.Duration / 60.0f,
}).ToList();
Although since you're not showing where you actually sum anything I suspect there's more to the problem than that.
Yeah Select is a projection function, you can just do those operations in your select.
.Select(x => new TestModel
{
TaskSummary = x.Report.Task.Summary,
LocationName = x.Report.Task.Location.Name,
IdleTime = x.Duration /60.0f,
NPTHoursSum = x.Duration / 60.0f,
Description = x.Description,
TaskId = x.Report.TaskId,
}).ToList();
If you're using LINQ to SQL then the division operation will probably occur at the db. If you actually want to do a sum it would have to be in a different query or as a sub query or something.\
I have a query that returns me a list of Categories grouped by Univers and ordered by Univers name. I would like the list of results also being ordered by category name (C.Name), here is my Linq query:
public static IEnumerable<IGrouping<String, String>> GetCatalogUnivCateg(){
var contexte = new AV2_Entities();
var query = from C in contexte.Category
group C.Name by C.Univers.Name into g
orderby g.Key
select g;
return query.ToList();
}
I would like to understand how is applied the group by, when the results has an orderby.
Add ordering when selecting group:
var query = from c in contexte.Category
group c by c.Univers.Name into g
orderby g.Key
select g.OrderBy(x => x.Name) // sort by name
.Select(x => x.Name); // project if you need just names
Unfortunately you can't return sorted results if you are returning IGrouping from method. Also IGrouping implementations are internal, so you can't just return some new grouping object. I suggest you to create DTO class, like:
public class UniversCategories
{
public string UniversName { get; set; }
public IEnumerable<string> Categories { get; set; }
}
And return instances of this class from your method
var query = from c in contexte.Category
group c by c.Univers.Name into g
orderby g.Key
select new UniversCategories {
UniversName = g.Key,
Categories = g.Select(x => x.Name).OrderBy(n => n)
};
I am performing a select query using the following Linq expression:
Table<Tbl_Movement> movements = context.Tbl_Movement;
var query = from m in movements
select new MovementSummary
{
Id = m.DocketId,
Created = m.DateTimeStamp,
CreatedBy = m.Tbl_User.FullName,
DocketNumber = m.DocketNumber,
DocketTypeDescription = m.Ref_DocketType.DocketType,
DocketTypeId = m.DocketTypeId,
Site = new Site()
{
Id = m.Tbl_Site.SiteId,
FirstLine = m.Tbl_Site.FirstLine,
Postcode = m.Tbl_Site.Postcode,
SiteName = m.Tbl_Site.SiteName,
TownCity = m.Tbl_Site.TownCity,
Brewery = new Brewery()
{
Id = m.Tbl_Site.Ref_Brewery.BreweryId,
BreweryName = m.Tbl_Site.Ref_Brewery.BreweryName
},
Region = new Region()
{
Description = m.Tbl_Site.Ref_Region.Description,
Id = m.Tbl_Site.Ref_Region.RegionId
}
}
};
I am also passing in an IFilter class into the method where this select is performed.
public interface IJobFilter
{
int? PersonId { get; set; }
int? RegionId { get; set; }
int? SiteId { get; set; }
int? AssetId { get; set; }
}
How do I add these where parameters into my SQL expression? Preferably I'd like this done in another method as the filtering will be re-used across multiple repositories.
Unfortunately when I do query.Where it has become an IQueryable<MovementSummary>. I'm assuming it has become this as I'm returning an IEnumerable<MovementSummary>. I've only just started learning LINQ, so be gentle.
Answer:
private IQueryable<Tbl_Docket> BuildQuery(IQueryable<Tbl_Docket> movements, IMovementFilter filter)
{
if (filter != null)
{
if (filter.PersonId.HasValue) movements = movements.Where(m => m.UserId == filter.PersonId);
if (filter.SiteId.HasValue) ...
}
return movements;
}
Which is called like follows:
var query = from m in this.BuildQuery(movements, filter)
select new... {}
You have to call the where statement before you fire your select statement, e.g.:
IQueryable<Tbl_Movement> movements = context.Tbl_Movement;
if (filter != null)
{
if (filter.PersonId != null) movements = movements.Where(m => m....PersonId == filter.PersonId);
if (filter.RegionId != null) movements = movements.Where(m => m....RegionId == filter.RegionId);
if (filter.SiteId != null) movements = movements.Where(m => m...SiteId == filter.SiteId);
if (filter.AssetId != null) movements = movements.Where(m => m...AssetId == filter.AssetId);
}
var query = m from movements...
As opposed to using this IFilter class, you might want to consider a Fluent Pipe-based Repository structure, e.g.:
var movements = new MovementsPipe()
.FindSiteId(1)
.FindAssetIds(1, 2, 3)
.FindRegionId(m => m > 10)
.ToMovementSummaryList();
Hope this helps. Let me know if you have any questions.