LinQ sum group by between using Lambda - linq

Help me convert it to lambda
select
Orders.Order_Date as date, sum(Amount) as amount
from
Orders
inner join
OrderDetails on Orders.Order_ID = OrderDetails.Order_ID
where
Order_Date<= '2017-02-10' and Order_Date<= '2017-02-11'
group by
(Order_Date)
order by
date asc
My model:
public class TotalIncome
{
[DataType(DataType.Date)]
public DateTime fromDate { get; set; }
[DataType(DataType.Date)]
public DateTime toDate { get; set; }
public List<IncomeByDate> ListIncome { get; set; }
}
my controller:
var ba = db.Orders.Include(o => o.OrderDetails)
.Where(o => o.Order_Date >= startDate)
.Where(o => o.Order_Date <= endDate);
i can't sum amount and group by. Help me fix it

Assuming Amount is a member of OrderDetails
var ba = db.Orders
.Include(o => o.OrderDetails)
.Where(o => o.Order_Date >= startDate)
.Where(o => o.Order_Date <= endDate)
.GroupBy(o => o.Order_Date)
.Select(s => new {
date = s.Key,
amount = s.Sum(o => o.OrderDetails.Sum(od => od.Amount)),
});

Related

Cannot compose correct Linq query for a class having collection properties - getting EfCore5 translation errors

I have flat collection of the following data (IQueryable<Article>) which was obtained by querying DB:
ArticleId
LanguageName
ArticleText
ExtraData1
ExtraData2
1
English
EngText1
Something1
Something2
1
English
EngText2
Another1
Another2
1
French
FraText1
Blabla1
2
English
EngText2
Ololo1
Blabla2
2
German
GerText1
Naturlisch2
Now I need to fill the IQueryable<AgregatedArticle>: the idea is grouping by ArticleId and putting repeating data into nested list:
public class AgregatedArticle {
public int ArticleId { get; set; }
public List<Data> ArticleTexts { get; set; }
public class Data {
public string LanguageName { get; set; }
public string ArticleText { get; set; }
}
}
Unfortunately, I cannot make it: I am getting various EfCore5 translation errors and don't know: if it's me or EfCore5 bugs or limitations. I wasted 3 days trying different approaches. Please help - I was unable to find suitable examples in Internet. The problem comes up when I try to fill ArticleTexts property.
Here is the simplified example:
private async Task<IQueryable<LawArticleAggregated>> GetLawArticlesGroupedById(DbSet<LawArticleDetail> dbSet, string userContentLangRestriction = null)
{
var dbContext = await GetDbContextAsync();
var articlesQuery =
(from articleIds in dbSet.Select(x => x.ArticleId).Distinct()
from articlesPerId in dbSet
.Where(x => x.ArticleId == articleIds.ArticleId)
join askedL in dbContext.Langs
.Where(l => l.LanguageCode == userContentLangRestriction)
on
articlesPerId.LanguageCode
equals
askedL.StringValue
into askedLanguages
from askedLawLanguage in askedLanguages.DefaultIfEmpty()
join fallbackL in dbContext.Langs
.Where(l => l.LanguageCode == CoreConstants.LanguageCodes.English)
on
articlesPerId.LanguageCode
equals
fallbackL.StringValue
into fallbackLanguages
from fallbackLanguage in fallbackLanguages.DefaultIfEmpty()
select new
{
ArticleId = articleIds.ArticleId,
ArticleText = articlesPerId.ArticleText,
LanguageName = askedLawLanguage.ShortName ?? fallbackLanguage.ShortName
})
.OrderBy(x => x.ArticleId).ThenBy(x => x.LanguageName).ThenBy(x => x.ArticleText);
await articlesQuery.LoadAsync();
var aggregatedArticleData = articlesQuery.Select(x => new
{
ArticleId = x.ArticleId,
ArticleText = x.ArticleText,
LanguageName = x.LanguageName
});
var aggregatedArticles = articlesQuery.Select(x => x.ArticleId).Distinct().Select(x => new ArticleAggregated
{
ArticleId = x.ArticleId,
ArticleTexts = aggregatedArticleData.Where(a => a.ArticleId == x.ArticleId)
.Select(x => new LawArticleAggregated.Data
{
ArticleText = x.ArticleText,
LanguageName = x.LanguageName
}).ToList()
});
return aggregatedArticles;
}
For this specific code the exception is as follows:
Unable to translate collection subquery in projection since the parent
query doesn't project key columns of all of it's tables which are
required to generate results on client side. This can happen when
trying to correlate on keyless entity or when using 'Distinct' or
'GroupBy' operations without projecting all of the key columns.
I think I have reverse engineered your query. Big difference that we cannot return IQueryable from this funcrtio, but prepared IEnumerable. So if you have pagination later, better to pass page info into function parameters.
private async Task<IEnumerable<LawArticleAggregated>> GetLawArticlesGroupedById(DbSet<LawArticleDetail> dbSet, string userContentLangRestriction = null)
{
var dbContext = await GetDbContextAsync();
var articlesQuery =
from article in dbSet
from askedLawLanguage in dbContext.Langs
.Where(askedLawLanguage => askedLawLanguage.LanguageCode == userContentLangRestriction && article.LanguageCode == askedLawLanguage.StringValue)
.DefaultIfEmpty()
from fallbackLanguage in dbContext.Langs
.Where(fallbackLanguage => fallbackLanguage.LanguageCode == CoreConstants.LanguageCodes.English && article.LanguageCode == fallbackLanguage.StringValue)
.DefaultIfEmpty()
select new
{
ArticleId = article.ArticleId,
ArticleText = article.ArticleText,
LanguageName = askedLawLanguage.ShortName ?? fallbackLanguage.ShortName
};
articlesQuery = articlesQuery
.OrderBy(x => x.ArticleId)
.ThenBy(x => x.LanguageName)
.ThenBy(x => x.ArticleText);
var loaded = await articlesQuery.ToListAsync();
// group on the client side
var aggregatedArticles = loaded.GroupBy(x => x.ArticleId)
.Select(g => new ArticleAggregated
{
ArticleId = g.Key,
ArticleTexts = g.Select(x => new LawArticleAggregated.Data
{
ArticleText = x.ArticleText,
LanguageName = x.LanguageName
}).ToList()
});
return aggregatedArticles;
}
I ended up with the following implementation (I show it "as is", without simplification from the first message to demonstrate the approach, slightly modified from the initial variant to use proper paging):
private async Task<IEnumerable<LawArticleAggregated>> GetLawArticlesGroupedByIdListAsync(
DbSet<LawArticleDetail> dbSet,
Expression<Func<IQueryable<LawArticleDetail>, IQueryable<LawArticleDetail>>> filterFunc,
int skipCount,
int maxResultCount,
string userContentLangRestriction = null,
CancellationToken cancellationToken = default
)
{
var dbContext = await GetDbContextAsync();
var articlesQuery =
(from articleIds in filterFunc.Compile().Invoke(dbSet).Select(x => new { x.TenantId, x.LawArticleId })
.Distinct().OrderBy(x => x.TenantId).OrderByDescending(x => x.LawArticleId).Skip(skipCount).Take(maxResultCount)
from articlesPerId in dbSet
.Where(x => x.TenantId == articleIds.TenantId && x.LawArticleId == articleIds.LawArticleId)
join askedL in dbContext.FixCodeValues
.Where(l =>
l.DomainId == CoreConstants.Domains.CENTRAL_TOOLS
&& l.CodeName == CoreConstants.FieldTypes.LANGUAGE
&& l.LanguageCode == userContentLangRestriction)
on
articlesPerId.LanguageCode
equals
askedL.StringValue
into askedLanguages
from askedLawLanguage in askedLanguages.DefaultIfEmpty()
join fallbackL in dbContext.FixCodeValues
.Where(l =>
l.DomainId == CoreConstants.Domains.CENTRAL_TOOLS
&& l.CodeName == CoreConstants.FieldTypes.LANGUAGE
&& l.LanguageCode == CoreConstants.LanguageCodes.English)
on
articlesPerId.LanguageCode
equals
fallbackL.StringValue
into fallbackLanguages
from fallbackLanguage in fallbackLanguages.DefaultIfEmpty()
select new
{
TenantId = articleIds.TenantId,
LawArticleId = articleIds.LawArticleId,
Shortcut = articlesPerId.Shortcut,
ArticleText = articlesPerId.ArticleText,
LanguageName = askedLawLanguage.ShortName ?? fallbackLanguage.ShortName
})
.OrderBy(x => x.TenantId).ThenByDescending(x => x.LawArticleId).ThenBy(x => x.Shortcut).ThenBy(x => x.LanguageName).ThenBy(x => x.ArticleText);
var articleList = await articlesQuery.ToListAsync(cancellationToken);
var aggregatedArticles = articleList.GroupBy(x => new { x.TenantId, x.LawArticleId })
.Select(g => new LawArticleAggregated
{
TenantId = g.Key.TenantId,
LawArticleId = g.Key.LawArticleId,
ArticleTexts = g.Select(x => new LawArticleAggregated.Data
{
Shortcut = x.Shortcut,
ArticleText = x.ArticleText,
LanguageName = x.LanguageName
}).ToList()
});
return aggregatedArticles;
}
private async Task<long> GetLawArticlesGroupedByIdCountAsync(
DbSet<LawArticleDetail> dbSet,
Expression<Func<IQueryable<LawArticleDetail>, IQueryable<LawArticleDetail>>> filterFunc,
CancellationToken cancellationToken = default
)
{
return await filterFunc.Compile().Invoke(dbSet).GroupBy(x => new { x.TenantId, x.LawArticleId }).LongCountAsync(cancellationToken);
}

How to bind the TreeList to a MVC model class using MVC wrappers

I am finding it difficult to understand how the hierarchy is binded in the TreeList. We are trying to achieve InCell editing and I am unable to do it using the HTML MVC wrappers.
Please help me with the working solution as the one on the How to website does not give an insight on how the DB schema and am unable to debug it to understand how to structure the Model.
https://docs.telerik.com/aspnet-mvc/helpers/treelist/editing
Code snippets: I hope this helps:
Html Markup:
#(Html.Kendo().TreeList<ABC.Areas.COManager.ViewModels.MaterialViewModel>()
.Name("treelist")
.Toolbar(toolbar =>
{
//toolbar.Create().Name("Add Item to order");
toolbar.Save();
toolbar.Cancel();
})
.Columns(columns =>
{
columns.Add().Field(e => e.OrderItemId).Title("Item id").Expandable(true);//.Width(220);
columns.Add().Field(e => e.OrderSubItemId).Title("Sub item id");//.Width(220);
columns.Add().Field(e => e.WBS).Title("WBS");//.Width(220);
columns.Add().Field(e => e.Rate).Title("Rate");//.Width(100);
columns.Add().Field(e => e.Hours);
columns.Add().Field(e => e.CostAmount).Title("Cost Amount").Format("{0:C2}");
columns.Add().Command(c =>
{
c.CreateChild().Text("Add Item detail");
c.Destroy();
}
);//.Width(240);
})
.Events(ev => ev.DataBound("onDataBound"))
.Editable(e => e.Mode(TreeListEditMode.InCell))
.DataSource(dataSource => dataSource
.Batch(true)
.Read(read => read.Action("All_InCell", "OrderInputs"))
.Create(create => create.Action("Create_InCell", "OrderInputs").Type(HttpVerbs.Post))
.Update(update => update.Action("Update_InCell", "OrderInputs").Type(HttpVerbs.Post))
.Destroy(delete => delete.Action("Destroy_InCell", "OrderInputs").Type(HttpVerbs.Post))
.Model(m =>
{
m.Id(f => f.OrderSubItemId);
m.ParentId(f => f.OrderItemId);
//m.Expanded(true);
m.Field(f => f.OrderId);
m.Field(f => f.OrderItemId);
m.Field(f => f.OrderSubItemId);
m.Field(f => f.WBS);
m.Field(f => f.Rate);
m.Field(f => f.Hours);
m.Field(f => f.CostAmount).DefaultValue(0);
})
)
//.Height(540)
)
Controller action for read:
public JsonResult All_InCell([DataSourceRequest] DataSourceRequest request)
{
var result = GetDirectory().ToTreeDataSourceResult(request,
e => e.OrderSubItemId,
e => e.OrderItemId,
e => new MaterialViewModel
{
OrderItemId = e.OrderItemId,
OrderId = e.OrderId,
OrderSubItemId = e.OrderSubItemId,
OrderDate = e.OrderDate,
hasChildren = false
}
);
return Json(result, JsonRequestBehavior.AllowGet);
}
Gets the Data:
private IEnumerable<MaterialViewModel> GetDirectory()
{
return employeeDirectory.GetAll();
}
Returns the data (Dummy/Static): Could this be the problem? I am unsure.
internal IEnumerable<MaterialViewModel> GetAll()
{
var returnData = new List<MaterialViewModel>();
for (var i = 0; i <= 10; i++)
{
returnData.Add(new MaterialViewModel { OrderId = i + 1, OrderItemId = (10 * (i + 1)) + (i + 1), OrderSubItemId = (100 * (i + 1)) + (i + 1), OrderDate = DateTime.Now, WBS = "ABC" + (i + 1).ToString(), Description = "Description " + (i + 1).ToString(), });
}
return returnData;
}
Model:
MaterialViewModel
public class MaterialViewModel: OrderInputsViewModel
{
public string WBS { get; set; }
public string Description { get; set; }
public double Rate { get; set; }
public int Hours { get; set; }
public double CostAmount { get; set; }
}
Model:
OrderInputsViewModel
public class OrderInputsViewModel
{
//[ScaffoldColumn(false)]
public int? OrderId { get; set; }
//[ScaffoldColumn(false)]
public int OrderItemId { get; set; }
public int OrderSubItemId { get; set; }
public DateTime OrderDate { get; internal set; }
//[ScaffoldColumn(false)]
public bool hasChildren { get; set; }
}
Expected is to have a TreeList populated by the Model class and be able to create children in the loaded items.
Answered by the Progress Telerik support team. Sharing the resolution here in anticipation that it could help any future readers.
Ans - Perhaps the issue I was experiencing is that there is no data in the TreeList. The cause is that the ParentId is not nullable and that there are no root items - items with a ParentId which is null.
Making the following two changes, I am able to see the TreeList rendered correctly:
Model which is bonded to the TreeList:
public class OrderInputsViewModel
{
//Parent Id
**public int? OrderItemId { get; set; }**
Method that returns the data (Dummy/Static):
returnData.Add(new MaterialViewModel {
OrderId =i + 1 ,
OrderItemId = i % 2 == 0 ? (10 * (i + 1)) + (i + 1)
**: (int?)null**,
OrderSubItemId = (100 * (i + 1)) + (i + 1),
OrderDate = DateTime.Now,
WBS = "ABC" + (i + 1).ToString(),
Description = "Description " + (i + 1).ToString()
});

Not understanding the behavior of _id on ES

I have documents in a Mongo database, they use the _id field as an index.
I use Monstache to sync ES with Mongo's op log, so the documents in ES have the same _id field.
When searching a specific document, Kibana shows:
Tags:
tag1 testtag CreatedOn:
October 26th 2018, 14:25:57.053
_id:
FRaqDPIzWcVI2dl-oA9uUFHLVFQk8qIqqhySWSkM7Ds
_type:
testobject
_index:
test.object
_score:
0
but then a query with Nest, returns this in the Documents array:
_id = 0d5aa177-3066-4c6a-aaf5-9b887ae7297f
and when I look in the Hits array, I see:
Id = FRaqDPIzWcVI2dl-oA9uUFHLVFQk8qIqqhySWSkM7Ds
So in Documents, the _id is now an unrelated guid, but in Hits the _id is called Id and it has the right value.
Why is that, and is there a way to get the proper value for _id in Documents?
Edit: more info
This is the object; since it's shared by MongoDB and ES, it has attributes for both.
[Nest.ElasticsearchType, BsonIgnoreExtraElements]
public class TestObject
{
public string _id { get; set; }
public string OwnerId { get; set; }
public Flags Flags { get; set; }
[Nest.Text, BsonIgnoreIfDefault] public string Title { get; set; }
[Nest.Text] public string Tags { get; set; }
[Nest.Ignore] public string Hash { get; set; }
[Nest.Ignore, BsonIgnoreIfDefault] public string Link { get; set; }
}
This is the code creating the index:
private static void InitializeElasticSearch(string ConnectionString)
{
var Settings = new ConnectionSettings(new Uri(ConnectionString))
.DefaultIndex(_IndexName)
.DefaultFieldNameInferrer(_ => _)
.DefaultMappingFor<TestObject>(_ => _.Ignore(I => I._id));
_ElasticClient = new ElasticClient(Settings);
if (!_ElasticClient.IndexExists(_IndexName).Exists)
{
// create the index
var CreateIndexResponse = _ElasticClient.CreateIndex(_IndexName, C => C
.Settings(S => S
.Analysis(A => A
.CharFilters(Cf => Cf
.Mapping("expressions", E => E
.Mappings(TextLists.Expressions)
)
)
.TokenFilters(Tf => Tf
.Synonym("synonyms", Sy => Sy
.Synonyms(TextLists.Synonyms)
.Tokenizer("whitespace")
)
)
.Analyzers(An => An
.Custom("index", Ca => Ca
.CharFilters("expressions")
.Tokenizer("standard")
.Filters("standard", "synonyms", "stop")
)
)
)
)
.Mappings(M => M
.Map<TestObject>(Mm => Mm
.AutoMap()
.Properties(P => P
.Text(T => T
.Name(N => N.Title)
.Analyzer("index")
)
.Text(T => T
.Name(N => N.Tags)
.Analyzer("index")
)
)
)
)
);
Then, the query code:
var R = await _ElasticClient.SearchAsync<TestObject>(Sr => Sr
.Query(Q =>
{
// do we query 'all' ?
if (Terms == "*") return Q.MatchAll();
// or do we have a general query
return Q
.MultiMatch(Fu => Fu
.Fields(F => F
.Field(Ff => Ff.Tags)
.Field(Ff => Ff.Title)
)
.Query(Terms)
.Fuzziness(Fuzziness.EditDistance(2))
);
})
.Take(_MaxObjectReturned)
);

Assign/Compare DateTime value from Database with DateTime value from C# in Linq query

I have following class:
public class Top10Date
{
[Key]
public int DateId { get; set; }
public DateTime Date { get; set; }
}
Which is associated with Top10Date table in database.
I am trying to do a comparison and assign values to latestDate in controller class:
public ActionResult Index(string date, DateTime? latestDate)
{
var topDate = db.Top10Dates;
if (!latestDate.HasValue)
latestDate = (DateTime?)(topDate.OrderByDescending(d => d.Date).FirstOrDefault());
if (date.Value == "PreviousDate")
{
latestDate = (DateTime?)(topDate.Where(d => d.Date < latestDate.Date).OrderByDescending(d => d.Date).FirstOrDefault());
}
if (date.Value == "NextDate")
{
latestDate = (DateTime?)(topDate.Where(d => d.Date > latestDate.Date).OrderBy(d => d.Date).FirstOrDefault());
}
....
ViewBag.latestDate = latestDate
return View();
}
But it is giving me all sort of errors. I have tried other things too but none are working:
latestDate = topDate.OrderByDescending(d => d.Date).FirstOrDefault();
latestDate.Value = topDate.OrderByDescending(d => d.Date).FirstOrDefault();
latestDate = topDate.Where(d => d.Date > latestDate.Value.Date).OrderBy(d => d.Date).FirstOrDefault();
Also how can i get DateId from the Database? I tried this it is now working.
var dateId = (DateTime?)top10Date.Where(d => d.Date == latestDate.Value.Date).Select(x => x.DateId).FirstOrDefault();
try
if (!latestDate.HasValue)
latestDate = (DateTime?)(topDate.OrderByDescending(d => d.Date).Select( x => x.Date).FirstOrDefault());
if (date == "PreviousDate")
{
latestDate = (DateTime?)(topDate.Where(d => d.Date < latestDate.Date).OrderByDescending(d => d.Date).Select(x => x.Date).FirstOrDefault());
}
if (date == "NextDate")
{
latestDate = (DateTime?)(topDate.Where(d => d.Date > latestDate.Date).OrderBy(d => d.Date).Select(x => x.Date).FirstOrDefault());
}
Your query on topDate is returning an instance of Top10Date which cannot be automatically converted to DateTime?. You can use the linq method Select() to have your query return something different - in this case the Date property in Top10Date
Here is the working code if someone else had similar problem. It took me whole day to figure this out. And i cant thank StanK enough for showing me the right path:
if (!latestDate.HasValue)
{
latestDate = (DateTime?)(topDate.OrderByDescending(d => d.Date).Select(x => x.Date).FirstOrDefault());
}
if ( date == "PreviousDate")
{
latestDate = (DateTime?)(topDate.Where(d => d.Date < latestDate).OrderByDescending(d => d.Date).Select(x => x.Date).FirstOrDefault());
}
if (date == "NextDate")
{
latestDate = (DateTime?)(topDate.Where(d => d.Date > latestDate).OrderBy(d => d.Date).Select(x => x.Date).FirstOrDefault());
}
Also here is how to get the DateId of the latestDate:
int dateId = topDate.Where(d => d.Date == latestDate).Select(x => x.DateId).FirstOrDefault();

LINQ RowNumber, Aggregate (Sum) and GroupBy

I have an SQL code like;
Select GroupName, sum(LineAmount) as Total, WeekNumber,
ROW_NUMBER() over (partition by WeekNumber order by sum(LineAmount) desc) as RowNum
from
Invoices
where
month(InvoiceDate)=month(getdate())
group by
GroupName,WeekNumber
I would like to convert this to LINQ, but no luck. I am using LINQ to Object. Any help would be appreciated.
TIA
EDIT : Here is some sample data, and the expected result.
public class Invoice
{
public string GroupName { get; set; }
public int LineAmount { get; set; }
public int WeekNum { get; set; }
}
List<Invoice> theData = new List<Invoice>();
theData.Add(new Invoice { GroupName = "A", LineAmount = 1, WeekNum = 1});
theData.Add(new Invoice { GroupName = "A", LineAmount = 2, WeekNum = 1 });
theData.Add(new Invoice { GroupName = "A", LineAmount = 3, WeekNum = 1 });
theData.Add(new Invoice { GroupName = "A", LineAmount = 2, WeekNum = 2 });
theData.Add(new Invoice { GroupName = "A", LineAmount = 3, WeekNum = 2 });
theData.Add(new Invoice { GroupName = "A", LineAmount = 4, WeekNum = 2 });
theData.Add(new Invoice { GroupName = "B", LineAmount = 4, WeekNum = 1 });
theData.Add(new Invoice { GroupName = "B", LineAmount = 3, WeekNum = 1 });
theData.Add(new Invoice { GroupName = "B", LineAmount = 7, WeekNum = 2 });
theData.Add(new Invoice { GroupName = "B", LineAmount = 6, WeekNum = 2 });
theData.Add(new Invoice { GroupName = "B", LineAmount = 5, WeekNum = 2 });
I have removed "where" from my first query as its not a problem at the moment.
theData
.GroupBy(g => new {g.GroupName, g.WeekNum}, (key, gg) => new {key.GroupName, key.WeekNum, Total = gg.Sum(g => g.LineAmount)})
.GroupBy(g => g.WeekNum, (weekNum, gg) => gg.OrderByDescending(g => g.Total).Select((g,i) => new {g.GroupName, g.Total, g.WeekNum, RowNum = i}))
.SelectMany(g => g)
You have not specified the language you need it in. Here is the code in C#
int index = 0;
var filteredInvoices = (from i in invoices
where i.InvoiceDate.Month == DateTime.Now().Month
group i by new { i.GroupName, i.WeekNumber }
into ig
select new {i.GroupName, Total = ig.Sum(i => i.LineAmount), i.WeekNumber, RowNum = ++index}).OrderByDescending(n => n.Total);
filteredInvoices should have the result that you want. Also I am assuming that the i.InvoiceDate is of type DateTime.
Serg Rogovtsev answer gives me expected result. And the below code is what I have done. Don't know which performs better, but results are same.
(theData.GroupBy(f => new { f.GroupName, f.WeekNum})
.Select(r => new {r.Key.WeekNum, r.Key.GroupName, Total = r.Sum(f => f.LineAmount)}))
.GroupBy(r => new {r.WeekNum}).SelectMany(
g =>
g.OrderByDescending(f => f.Total).Select(
(f, index) => new { f.GroupName, f.Total, f.WeekNum, Ix = index + 1 }))

Resources