Problem with fetching records from database with EntityFramework - c#

I need little help with the three functions below. I expect the functions to take the records daily, monthly and all records of the current year. However, I notice on the daily report the amount of 'scrap' is around 126 meanwhile monthly and year reports are showing 32.
Why 126 'scrap' in the daily report is not included in the others reports? Thank you in advance.
public async Task<List<Scrap>> GetDailyScrap()
{
return await _dbContext.Scrap.Where(x =>
x.Created.Year == DateTime.Now.Year &&
x.Created.Month == DateTime.Now.Month &&
x.Created.Day == DateTime.Now.Day).ToListAsync();
}
public async Task<List<Scrap>> GetMonthlyScrap()
{
return await _dbContext.Scrap.Where(x =>
x.Created.Year == DateTime.Now.Year &&
x.Created.Month == DateTime.Now.Month).ToListAsync();
}
public async Task<List<Scrap>> GetYearScrap()
{
return await _dbContext.Scrap.Where(x =>
x.Created.Year == DateTime.Now.Year).ToListAsync();
}
The amount of scrap for KST-420(daily chart) to reflect with the correct numbers on the monthly and year report.
Scrap Model :
public class Scrap
{
public int Id { get; set; }
public int ScrapLineId { get; set; }
public string Line { get; set; }
public string Type { get; set; }
public string Position { get; set; }
public string Tag { get; set; }
public int Shift { get; set; }
public int ShiftLeaderPersonalId { get; set; }
public int OperatorPersonalId { get; set; }
public int Quantity { get; set; }
public int Week { get; set; }
public DateTime Created { get; set; }
}
endpoint:
//Daily Bar Chart SCRAP
List<Scrap> dailyScrap = await _scrapService.GetDailyScrap();
List<string> xValues = dailyScrap.DistinctBy(x => x.Line).Select(x => x.Line).ToList();
List<int> yValues = dailyScrap.DistinctBy(x => x.Quantity).Select(x => x.Quantity).ToList();
// Monthly Bar Chart SCRAP
List<Scrap> monthlyScrap = await _scrapService.GetMonthlyScrap();
List<string> xValuesMonthly = monthlyScrap.DistinctBy(x => x.Line).Select(x => x.Line).ToList();
List<int> yValuesMonthly = monthlyScrap.DistinctBy(x => x.Quantity).Select(x => x.Quantity).ToList();
// Year Bar Chart SCRAP
List<Scrap> yearScrap = await _scrapService.GetYearScrap();
List<string> xValuesYear = yearScrap.DistinctBy(x => x.Line).Select(x => x.Line).ToList();
List<int> yValuesYear= yearScrap.DistinctBy(x => x.Quantity).Select(x => x.Quantity).ToList();
charts

The way these queries are written, they count individual values, not the count or sum of items per line. For example, 101 and 102 would produce an Y value of 2, while 100 individual 100s would produce 1.
To get totals by line, use GroupBy and Count or Sum, eg :
var dailies=dailyScrap.GroupBy(s=>s.Line)
.Select(g=>new
{
X=g.Key,
Y=g.Sum(s=>s.Quantity)
})
.ToList();
This can be done in EF Core too, retrieving only the totals from the database :
var dateFrom=DateTime.Today;
var dateTo=dateFrom.AddDays(1);
var dailies=_dbContext.Scrap
.Where(s=> s.Created>=dateFrom
&& s.Created <dateTo)
.GroupBy(s=>s.Line)
.Select(g=>new
{
X=g.Key,
Y=g.Sum(s=>s.Quantity)
})
.ToList()
This generates
SELECT Line,SUM(Quantity)
FROM Scrap
WHERE Created >=#d1 && Created < #d2
GROUP BY Line
The condition can be simplified to only Where(s=> s.Created>=DateTime.Today) if there are no future values.
The query can be adapted to cover any period by changing the From and To parameters, eg :
var dateFrom=new DateTime(DateTime.Today.Year,DateTime.Today.Month,1);
var dateTo=dateFrom.AddMonths(1);
or
var dateFrom=new DateTime(DateTime.Today.Year,1,1);
var dateTo=dateFrom.AddYears(1);

Related

How to get Percentage of Sales for the Past 5 Years

In my ASP.NET Core-5 Entity Framework I have this model:
public class Sales
{
public int Id { get; set; }
public string Name { get; set; }
public decimal Amount { get; set; }
public DateTime? SalesDate { get; set; }
}
DTO:
public class YearlyPercentDto
{
public decimal SalesTotal { get; set; }
public int SalesPercent { get; set; }
public string Year { get; set; }
}
public List<YearlyPercentDto> GetYearlySalesSummary()
{
var salesDetail = _context.sales
.GroupBy(o => new
{
Year = o.CreatedDate.Value.Year
})
.Select(u => new YearlyPercentDto
{
SalesPercent = u.Sum(x => x.Amount),
Year = u.Key.Year.ToString()
}).ToList();
return salesDetail;
}
I want to get the total_sales, percentage_sales for each year in the past 5 years as shown below:
Year (Past 5 Years) SalesTotal SalesPercent
2021 200000 18
2020 4300000
2019 1290000
2018 5400000
2017 3322220
How do I achieve this?
I think I'd just pull the totals from the DB and have C# work out the percentage:
public List<YearlyPercentDto> GetYearlySalesSummary()
{
var salesDetail = _context.sales
.Where(s => o.CreatedDate > DateTime.Now.AddYears(-5)
.GroupBy(o => o.CreatedDate.Value.Year)
.Select(u => new YearlyPercentDto
{
SalesTotal = u.Sum(x => x.Amount),
Year = u.Key.ToString() //why is Year a string?
}
).ToList();
//grand total
var tot = salesDetail.Sum(s => s.SalesTotal);
//apply percentage to each element
salesDetail.ForEach(s => s.SalesPercent = (int)(100.0 * s.SalesTotal/tot));
return salesDetail;
}
There seems little point in bullying the DB to provide this info when C# can quickly work it out - the extra hoops to jump through to get the DB to do it don't seem worth it

Get max value from datetimes (c#)

I have code to get all of values from table column and calculate average salary and max difference between salary dates.
Here is model for Salary
public int Id { get; set; }
public DateTime Date { get; set; }
public decimal Amount { get; set; }
public Nullable<int> Employee_Id { get; set; }
public virtual Employee Employee { get; set; }
And here is Viewmodel code
public class SalariesViewModel
{
public string DepartmentName { get; set; }
public decimal AverageSalary { get; set; }
public IEnumerable<DateTime> Dates{ get; set; }
public double MaxDifference { get; set; }
}
Here is code where I calculate average salary
var maxdiff = 0;
List<SalariesViewModel> result = new List<SalariesViewModel>();
var employees = db.Employees.Select(x => new
{
Department = x.Department.Name,
Name = x.Name,
Salary = x.Salaries.OrderByDescending(y => y.Date).FirstOrDefault().Amount,
Date = x.Salaries.OrderByDescending(y => y.Date).FirstOrDefault().Date
});
var data = employees.GroupBy(m => m.Department).Select(x => new SalariesViewModel
{
DepartmentName = x.Key,
AverageSalary = x.Average(y => y.Salary),
Dates = x.Select(y=> y.Date).ToList()
}).ToList();
for (int i = 0; i < data.Count - 1; i++) {
}
return data;
}
Also I need to calculate max difference
I can select only dates and calculate differences between them and find max. And then push to ViewModel.
But I think, it not good experience.
How I can do this in data query or in ViewModel?
Update
This might give some clues to anyway heading down this rabbit hole
In response to Michael Randalls Answer
Yes. But you calculate difference beetween max and min values of date.
I need to calculate other. For example employee has 3 salary pays. I
need to show max time between those pays. So it would be 3 payment -2
payment , and 2 payment - 1 payment. and if for example 1 variant
difference is bigger, I need to show it.
If you need to find only the max difference between adjacent elements, without making the entire list of differences, you can Zip the list with itself after skipping the initial element, and get Max this way:
var maxDiff = dates.Zip(dates.Skip(1), (c, n) => (n-c).TotalSeconds).Max();
var maxdiff = 0;
for (int i = 0; i < dates.Count - 1; i++)
{
var result = (dates[i + 1] - dates[i]).TotalSeconds;
if (maxdiff < result)
{
maxdiff = result;
}
}
I am a gambling man, am i close?
var data = employees.GroupBy(m => m.Department).Select(x => new SalariesViewModel
{
DepartmentName = x.Key,
AverageSalary = x.Average(y => y.Salary),
MaxDifference = (x.Max(y => y.Date) - x.Min(y => y.Date)).TotalDays,
}).ToList()

Group By query in mvc5

Hi i want to write sql Group by query in C# of my MVC5 application.
In the above image I have group by query which i wrote in sql . That I want to write in C# front end.
I tried to write query in front end. But I am getting error which is mentioned in the image. Now I want to write that Group By query in C# and want to display the each employee with count (output same as mentioned in the first image). Can anyone help me to resolve this issue?
My ViewModel(Dashnboard View model)
public class DashboardViewmodel
{
public List<CustomerTypeCountModel> CustomerTypesCountModels { get; set; }
public List<View_VisitorsForm> Visits { get; set; }
public CustomerTypeViewModel CustomerTypeViewModels { get; set; }
public int sizingcount { get; set; }
public int Processingcount { get; set; }
//here i declared two properties
public string EmployeeName { get; set; }
public string EmployeeCount { get; set; }
}
My Controller code
[HttpGet]
public ActionResult SalesVisit()
{
return View();
}
public ActionResult GetDatesFromSalesVisit(DashboardViewmodel dvm)
{
var fromdate = Convert.ToDateTime(dvm.CustomerTypeViewModels.FromDate);
var todate = Convert.ToDateTime(dvm.CustomerTypeViewModels.ToDate);
List<View_VisitorsForm> empcount = new List<View_VisitorsForm>();
if (DepartmentID == new Guid("47D2C992-1CB6-44AA-91CA-6AA3C338447E") &&
(UserTypeID == new Guid("106D02CC-7DC2-42BF-AC6F-D683ADDC1824") ||
(UserTypeID == new Guid("B3728982-0016-4562-BF73-E9B8B99BD501"))))
{
var empcountresult = db.View_VisitorsForm.GroupBy(G => G.Employee)
.Select(e => new
{
employee = e.Key,
count = e.Count()
}).ToList();
empcount = empcountresult ;//this line i am getting error
}
DashboardViewmodel obj = new DashboardViewmodel();
return View("SalesVisit", obj);
}
When you use a GroupBy you get an IEnumerable<IGrouping<Key,YourOriginalType>> so you do not have .Employee and .VisitingID properties.
Change as following:
public class EmployeeCount
{
public string Employee {get; set;}
public int Count {get; set;}
}
List<EmployeeCount> result = db.View_VisitorsForm
.Where(item => item.VisitingDate >= beginDate && item.VisitingDate < endDate)
.GroupBy(G => G.Employee)
.Select(e =>new EmployeeCount
{
employee = e.Key,
count = e.Count()
}).ToList();
//Now add the result to the object you are passing to the View
Also keep in mind that you are not instantiating objects of type View_VisitorsForm but an anonymous object so assigning the result to empcount yet alone with the added FirstOrDefault will not compile
To pass this structure to the View and present it check this question
hope this helps you
var query = db.View_VisitorsForm.Where(o => o.VisitingDate >= new DateTime(2016,10,01) && o.VisitingDate <= new DateTime(2016, 10, 30)).GroupBy(G => G.Employee)
foreach (var item in query)
{
Console.WriteLine($"Employee Id {item.Key} : Count :{item.Count()}");
}

Reduce cannot contain Average() methods in grouping

Just upgraded to v2 and this no longer works; I get a similar error if I try to use Count()
public class Deck_Ratings : AbstractIndexCreationTask<DeckRating, Deck_Ratings.ReduceResult>
{
public class ReduceResult
{
public string DeckId { get; set; }
public int Rating { get; set; }
}
public Deck_Ratings()
{
Map = deckRatings => deckRatings.Select(deckRating => new
{
deckRating.DeckId,
deckRating.Rating
});
Reduce = reduceResults => reduceResults
.GroupBy(reduceResult => reduceResult.DeckId)
.Select(grouping => new
{
DeckId = grouping.Key,
Rating = grouping.Average(reduceResult => reduceResult.Rating)
});
}
}
Aggregates that can be influenced by the size of the reduce batch (such as Count and Average) are prohibited because they will yield the wrong results. You may have been able to use it under 1.0, but your averages were probably wrong unless you had so few items that they all got done in one reduce batch. To understand more about reduce batches, read Map / Reduce - A Visual Explanation
You must count items by summing a 1 for each item. You must average items by taking a sum of the values as a total, a sum of 1's as a count, and then dividing them.
public class Deck_Ratings : AbstractIndexCreationTask<DeckRating, Deck_Ratings.ReduceResult>
{
public class ReduceResult
{
public string DeckId { get; set; }
public int TotalRating { get; set; }
public int CountRating { get; set; }
public double AverageRating { get; set; }
}
public Deck_Ratings()
{
Map = deckRatings => deckRatings.Select(deckRating => new
{
deckRating.DeckId,
TotalRating = deckRating.Rating,
CountRating = 1,
AverageRating = 0
});
Reduce = reduceResults => reduceResults
.GroupBy(reduceResult => reduceResult.DeckId)
.Select(grouping => new
{
DeckId = grouping.Key,
TotalRating = grouping.Sum(reduceResult => reduceResult.TotalRating)
CountRating = grouping.Sum(reduceResult => reduceResult.CountRating)
})
.Select(x => new
{
x.DeckId,
x.TotalRating,
x.CountRating,
AverageRating = x.TotalRating / x.CountRating
});
}
}
This is issue RavenDB-783. This is expected behavior since v2.0.
Not sure what he recommends as an alternative, though.

Efficient way to call .Sum() on multiple properties

I have a function that uses LINQ to get data from the database and then I call that function in another function to sum all the individual properties using .Sum() on each individual property. I was wondering if there is an efficient way to sum all the properties at once rather than calling .Sum() on each individual property. I think the way I am doing as of right now, is very slow (although untested).
public OminitureStats GetAvgOmnitureData(int? fnsId, int dateRange)
{
IQueryable<OminitureStats> query = GetOmnitureDataAsQueryable(fnsId, dateRange);
int pageViews = query.Sum(q => q.PageViews);
int monthlyUniqueVisitors = query.Sum(q => q.MonthlyUniqueVisitors);
int visits = query.Sum(q => q.Visits);
double pagesPerVisit = (double)query.Sum(q => q.PagesPerVisit);
double bounceRate = (double)query.Sum(q => q.BounceRate);
return new OminitureStats(pageViews, monthlyUniqueVisitors, visits, bounceRate, pagesPerVisit);
}
private IQueryable<OminitureStats> GetOmnitureDataAsQueryable(int? fnsId, int dateRange)
{
var yesterday = DateTime.Today.AddDays(-1);
var nDays = yesterday.AddDays(-dateRange);
if (fnsId.HasValue)
{
IQueryable<OminitureStats> query = from o in lhDB.omniture_stats
where o.fns_id == fnsId
&& o.date <= yesterday
&& o.date > nDays
select new OminitureStats (
o.page_views.GetValueOrDefault(),
o.monthly_unique.GetValueOrDefault(),
o.visits.GetValueOrDefault(),
(double)o.bounce_rate.GetValueOrDefault()
);
return query;
}
return null;
}
public class OminitureStats
{
public OminitureStats(int PageViews, int MonthlyUniqueVisitors, int Visits, double BounceRate)
{
this.PageViews = PageViews;
this.MonthlyUniqueVisitors = MonthlyUniqueVisitors;
this.Visits = Visits;
this.BounceRate = BounceRate;
this.PagesPerVisit = Math.Round((double)(PageViews / Visits), 1);
}
public OminitureStats(int PageViews, int MonthlyUniqueVisitors, int Visits, double BounceRate, double PagesPerVisit)
{
this.PageViews = PageViews;
this.MonthlyUniqueVisitors = MonthlyUniqueVisitors;
this.Visits = Visits;
this.BounceRate = BounceRate;
this.PagesPerVisit = PagesPerVisit;
}
public int PageViews { get; set; }
public int MonthlyUniqueVisitors { get; set; }
public int Visits { get; set; }
public double PagesPerVisit { get; set; }
public double BounceRate { get; set; }
}
IIRC you can do all the sums in one go (as long as the query is translated to SQL) with
var sums = query.GroupBy(q => 1)
.Select(g => new
{
PageViews = g.Sum(q => q.PageViews),
Visits = g.Sum(q => q.Visits),
// etc etc
})
.Single();
This will give you one object which contains all the sums as separate properties.
I found out why it was throwing the NotSupportedException. I learned that Linq to Entity does not support constructors with parameters, So deleted the constructors and made changes in my query. I am a novice C# programmer, so let me know if my solution could be improved, but as of right now it is working fine.
public class OminitureStats
{
public int PageViews { get; set; }
public int MonthlyUniqueVisitors { get; set; }
public int Visits { get; set; }
public double PagesPerVisit { get; set; }
public double BounceRate { get; set; }
}
private IQueryable<OminitureStats> GetOmnitureDataAsQueryable(int? fnsId, int dateRange)
{
var yesterday = DateTime.Today.AddDays(-1);
var nDays = yesterday.AddDays(-dateRange);
if (fnsId.HasValue)
{
IQueryable<OminitureStats> query = from o in lhDB.omniture_stats
where o.fns_id == fnsId
&& o.date <= yesterday
&& o.date > nDays
select new OminitureStats() {
o.page_views.GetValueOrDefault(),
o.monthly_unique.GetValueOrDefault(),
o.visits.GetValueOrDefault(),
(double)o.bounce_rate.GetValueOrDefault()
};
return query;
}
return null;
}

Categories

Resources