How to speed up the following Linq Query for Stock - c#

I am write Programming in ASP.NET MVC with SQL Database. I want to get transfer list from database with between Date Range. Also the foreign key relationship with tables like product,Brand,sub-brand.
Here i wrote the code for getting list but it take time to load.The Database have 3000 records per month. how to optimize the code?
var stockTransferDetails = db.StockTransferDetails.Where(s => s.BusinessUnitId == BusinessUnitId && (!string.IsNullOrEmpty(from) ? s.StockTransfer.TransferDate >= FromDate : true) && (!string.IsNullOrEmpty(to) ? s.StockTransfer.TransferDate <= ToDate : true)).ToList();
var stocksummary = from STD in stockTransferDetails.GroupBy(f => f.ProductId).ToList()
select new
{
BrandName = STD.FirstOrDefault().Product.Brand.BrandName + STD.Where(m => !m.Product.SubBrand.BrandName.Equals("DEF")).Select(s => " - " + s.Product.SubBrand.BrandName).FirstOrDefault(),
ProductName = STD.FirstOrDefault().Product.ProductName,
Packsize = STD.FirstOrDefault().Product.Packsize + " " + STD.FirstOrDefault().Product.UOM.PickListValue,
// TotalQty = (double?)STD.Sum(s => s.ReceievedQty)
Cost = STD.FirstOrDefault().Product.Cost,
TotalCost = (STD.Sum(s => s.ReceievedQty) * STD.FirstOrDefault().Product.Cost)
};
OutletSTIn.Value = stocksummary.ToList();

Related

How to apply filtering for specific column?

I have query like below:
var query = from operation in dbContext.Operations
select new OperationsDto
{
Id = o.Id,
ProcessDate = o.ProcessDate,
Amount = o.Credit
//Balance = ...
};
There is no Balance property in Operation entity. I have to calculate it.
Balance => (sum of operations' Amount which operation has happened before current operation ProcessDate) + current Amount). For example:
Id ProcessDate Amount Balance
1 2021.02.01 +100$ 50 + 100 = +150$ (
2 2021.02.03 -200$ 150$ + (-200) = -50$ (this get sum of amount where record's process date before 2021.02.03)
3 2019.01.01 +50$ 0 + 50$ = 50$ (because this is first operation. there is not record before 2019.01.01)
I want this in EF Core.
I know I can do this using foreach after retrieve data like below:
var operations = query.ToList();
foreach (var operation in operations)
{
operation.Balance = operations.Where(x => x.ProcessDate < operation.ProcessDate).Sum(o => o.Debit - o.Credit);
}
But I need to calculate in query
It may be possible to build a balance within the query, but frankly it would likely be quite inefficient as it would need to re-query against the Operations. Something like:
var query = dbContext.Operations
.Select(o => new OperationsDto
{
Id = o.Id,
ProcessDate = o.ProcessDate,
Credit = o.Credit,
Debit = o.Debit,
Balance = dbContext.Operations.Where(x => x.Id != o.Id && x.ProcessDate <= o.ProcessDate).Sum(x => x.Credit - x.Debit)
});
A faster solution would be to build your balance in memory:
var operations = query.OrderBy(o => o.ProcessDate).ToList();
decimal balance = 0;
foreach (var operation in operations)
{
balance += operation.Credit - operation.Debit;
operation.Balance = balance;
}
This only works if you are loading the entire range. If instead you wanted to query data after a particular date, you can fetch the initial balance and start from there:
var operations = query
.Where(o => o.ProcessDate >= startDate)
.OrderBy(o => o.ProcessDate).ToList();
decimal balance = dbContext.Operations
.Where(o => o.ProcessDate < startDate)
.Sum(o => o.Credit - o.Debit); // starting balance.
// I can't check if the above is allowed in EF, may need to do the below:
var sums = dbContext.Operations
.GroupBy(o => true)
.Where(o => o.ProcessDate < startDate)
.Select(o new
{
CreditSum = o.Sum(x => x.Credit),
DebitSum = o.Sum(x => x.Debit)
}).Single();
var balance = sums.CreditSum - sums.DebitSum; // starting balance.
foreach (var operation in operations)
{
balance += operation.Credit - operation.Debit;
operation.Balance = balance;
}

how to convert type 'System.Collections.Generic.IEnumerable<AnonymousType#1>' to 'System.Collections.Generic.IEnumerable<>'.

I need to convert the following linq to a List directly,if I convert it to list I still get the error I explained on the subject,its said I need to convert it to list,but still the error is there,how can I convert it to list of string?without using additional conversion like foreach,...
List<string> eventResult= (from c in DB.Events
where (c.m_turbine_id == turbineid.turbineID) && (c.m_time_stamp >= frmDate && c.m_time_stamp <= toDate)
select new EventLogPartialViewModel
{
Timestamp = c.m_time_stamp,
Description = c.m_event_log_description,
WindSpeed = c.m_wind_speed,
RPM = c.m_rpm,
Power = c.m_power
}).ToList().Select(x => new
{
Timestamp = x.Timestamp.ToString("dd/MM/yyyy H:mm:ss"),
Description = x.Description,
WindSpeed = x.WindSpeed,
RPM = x.RPM,
Power = x.Power
}).ToList().OrderByDescending(m => m.Timestamp);
the first part of the above linq,i get the data and convert to list because I need to change my time stamp format,any help will be appreciated .
As you need to create list of comma separated values with header,
//Add headers as first item
List<string> eventResult = new List<string>(){"Timestamp,Description,WindSpeed,RPM,Power"};
//Add records
eventResult.AddRange(from c in DB.Events
where (c.m_turbine_id == turbineid.turbineID) && (c.m_time_stamp >= frmDate && c.m_time_stamp <= toDate)
select new EventLogPartialViewModel
{
Timestamp = c.m_time_stamp,
Description = c.m_event_log_description,
WindSpeed = c.m_wind_speed,
RPM = c.m_rpm,
Power = c.m_power
}).ToList().Select(x =>
x.Timestamp.ToString("dd/MM/yyyy H:mm:ss")) + "," +
x.Description + "," +
x.WindSpeed + "," +
x.RPM + "," +
x.Power
.ToList());
In the Linq query, you are selecting anonymous complex type so you can't assign it a List<string>. You should use var to keep reference of anonymous type.
var eventResult= (from c in DB.Events
where (c.m_turbine_id == turbineid.turbineID) && (c.m_time_stamp >= frmDate && c.m_time_stamp <= toDate)
select new EventLogPartialViewModel
{
Timestamp = c.m_time_stamp,
Description = c.m_event_log_description,
WindSpeed = c.m_wind_speed,
RPM = c.m_rpm,
Power = c.m_power
}).ToList().Select(x => new
{
Timestamp = x.Timestamp.ToString("dd/MM/yyyy H:mm:ss"),
Description = x.Description,
WindSpeed = x.WindSpeed,
RPM = x.RPM,
Power = x.Power
}).OrderByDescending(m => m.Timestamp).ToList();
Also, the ideal way is creating a class for complex type to handle reference instead of using anonymous type.

Linq Select row Where date in Current month

I need to get data from the current month.Haven't been able to find a solution that works.
This is my code, which gives me the data I need, but I get data from a whole month back, instead of from the current month we are in.
I chose the date "limit" twice with row > DateTime.Today.Addmonths(-1)
Any ideas ?
var userQuery =
from timeEntry in TimeEntries
//this displays a month back, not current month TODO: change to current month.
where timeEntry.DateEntity > DateTime.Today.AddMonths(-1)
select new {
UserName = timeEntry.User.FirstName + " " +timeEntry.User.LastName,
HoursEntered = timeEntry.HoursEntered,
User = timeEntry.User
};
var localrows = userQuery.ToList();
var grouping = localrows.GroupBy(x => x.User);
var userList = grouping.Select(q => new {
UserName = q.FirstOrDefault().UserName,
Hours = q.Sum(hr => hr.HoursEntered), //AbsenceTypeID = holiday(1) // still takes from a whole month, instead of current month.
Holiday = q.FirstOrDefault().User.Absences.Where(a => a.AbsenceTypeID == 1).Where(date => date.DateEntity > DateTime.Today.AddMonths(-1)).Count(),
});
EDIT:
Keyur patel' answer worked, though not for the bottom one.
so there i have to thank Jules for this one:
DateTime.Today.AddDays((DateTime.Today.Day -1) * -1)
Try using this:
DateTime today = DateTime.Today;
var userQuery =
from timeEntry in TimeEntries
where timeEntry.DateEntity.Month == today.Month && timeEntry.DateEntity.Year == today.Year
select new {
UserName = timeEntry.User.FirstName + " " +timeEntry.User.LastName,
HoursEntered = timeEntry.HoursEntered,
User = timeEntry.User
};
Added check for .Year since it may match with months from other years, if you have any.
Edit
As for the holiday part:
var userList = grouping.Select(q => new {
UserName = q.FirstOrDefault().UserName,
Hours = q.Sum(hr => hr.HoursEntered),
Holiday = q.FirstOrDefault().User.Absences.Where(a => a.AbsenceTypeID == 1 && a.DateEntity.Month == today.Month && a.DateEntity.Year == today.Year).Count(),
});
You can simply compare only Month part of the date like this:
timeEntry.DateEntity.Month == DateTime.Now.Month
You can use Month and Year properties of the DateTime class as follows:
var userQuery = TimeEntries
.Where (te => te.DateEntity.Month == DateTime.Today.Month && te.DateEntity.Year == DateTime.Today.Year)
.Select(timeEntry =>
new {
UserName = timeEntry.User.FirstName + " " + timeEntry.User.LastName,
HoursEntered = timeEntry.HoursEntered,
User = timeEntry.User
}
);
You can try this:
.Where(x => x.EntityDate >= new DateTime(DateTime.Now.Year, DateTime.Now.Month, 1));

Searching on multiple strings

I am having some issues with doing a search. I am trying to find a customer on either Name+Lastname, reference or CompanyName. I managed to get it working, however the the ordering is not 100%.
Here is my original object
var q = from row in DataAccess.metadata.db_Customer
where row.accountID == accountID
&& row.isActive
orderby row.Name
select new bl_customerNames
{
customerID = row.customerID,
CustomerName = row.Name + " " + row.LastName,
Company = row.Company,
Reference = row.reference,
Email = row.Email,
CurrencyCode = row.CurrencyCode,
isSuspended = row.isSuspended
};
Then I did the following filter.
if (!string.IsNullOrEmpty(search))
{
q = q.Where(x => (x.CustomerName.Contains(search) || x.Reference.Contains(search) || x.Company.Contains(search)));
}
Although this does a fair job the list does not really make logical sense as its does not favor starts with above Contains.
I then came up with a ranking solution where I would rank Starts with higher than Contains.
Here is my code for that:
var q = from row in DataAccess.metadata.db_Customer
where row.accountID == accountID
&& row.isActive
orderby row.Name
select new bl_customerNames
{
customerID = row.customerID,
CustomerName = row.Name + " " + row.LastName,
Company = row.Company,
Reference = row.reference,
Email = row.Email,
CurrencyCode = row.CurrencyCode,
isSuspended = row.isSuspended,
Rank = ((row.Name + " " + row.LastName).StartsWith(search) || row.reference.StartsWith(search) || row.Company.StartsWith(search)) ? 1 : ((row.Name + " " + row.LastName).Contains(search) || row.reference.Contains(search) || row.Company.Contains(search)) ? 2 : 0
};
I would then filter like this:
q = q.Where(r=>r.Rank > 0).OrderBy(r => r.Rank);
However if I search on reference it does not return anything.
So My question is will my current method work also why does nothing return if I search on reference or Company Name? What would the correct way of doing a StartsWith then Contains and still keeping some kind of alphabetical ordering after StartsWith List.
Please keep in mind I am still a junior developer and any help would be appreciated.
I went back to the basics and this is what I came up with.
//Get customers that starts with
var qSW = from row in DataAccess.metadata.db_Customer
where row.accountID == accountID
&& row.isActive
where (row.Name + " " + row.LastName).StartsWith(search)
|| row.Company.StartsWith(search)
|| row.reference.StartsWith(search)
select new bl_customerNames
{
customerID = row.customerID,
CustomerName = row.Name + " " + row.LastName,
Company = row.Company,
Reference = row.reference,
Email = row.Email,
CurrencyCode = row.CurrencyCode,
isSuspended = row.isSuspended
};
//Order
qSW = qSW.OrderBy(r => r.CustomerName).ThenBy(r => r.Company).ThenBy(r => r.Reference);
//Get customers that contains
var qC = from row in DataAccess.metadata.db_Customer
where row.accountID == accountID
&& row.isActive
where (row.Name + " " + row.LastName).Contains(search)
|| row.Company.Contains(search)
|| row.reference.Contains(search)
select new bl_customerNames
{
customerID = row.customerID,
CustomerName = row.Name + " " + row.LastName,
Company = row.Company,
Reference = row.reference,
Email = row.Email,
CurrencyCode = row.CurrencyCode,
isSuspended = row.isSuspended
};
//If search is less then 3 chars limit to 10 to help with performance
if(search.Count() > 2)
qC = qC.OrderBy(r => r.CustomerName).ThenBy(r => r.Company).ThenBy(r => r.Reference);
else
qC = qC.OrderBy(r => r.CustomerName).ThenBy(r => r.Company).ThenBy(r => r.Reference).Take(10);
//Concat list leaving out duplicates
var SList = qSW.Concat(qC);
// Return List
return SList.ToList();
It seems to be doing the job and performance seems very good.

How to increment column with last column value in Lambda or Linq

I am not expert in Linq or Lambda i really need help with this query.
This is like Statement of Account it has debit, credit and balance. however my linq query is wrong i got this result when im executing this query...
result:
suppose the result is this.
here's my code:
decimal Balance = 0;
var result = from a in entities.Payments
where a.StudentID == ParamStudentID
select new
{
Date = a.DateAdded,
Code = entities.Particulars.Where(p => p.Name == a.PaymentDes).Select(sp => sp.Code).FirstOrDefault(),
Particulars = a.PaymentDes,
Debit = 0,
Credit = a.Amount,
Balance = Balance + a.Amount,
SyTerm = a.SchoolYear + "-" + a.Term.Trim().Substring(0, 5)
};
i know this is an easy question but i don't know how to solve. :D anyone can help me.
Hope it helps:
var result = from a in entities.Payments
where a.StudentID == ParamStudentID
select new
{
Date = a.DateAdded,
Code = entities.Particulars.Where(p => p.Name == a.PaymentDes).Select(sp => sp.Code).FirstOrDefault(),
Particulars = a.PaymentDes,
Debit = 0,
Credit = a.Amount,
Balance = entities.Payments.Where(x => x.StudentID == ParamStudentID).TakeWhile(x => x != a).Sum(x => x.Amount) + a.Amount,
SyTerm = a.SchoolYear + "-" + a.Term.Trim().Substring(0, 5)
};
I apologize if I have any syntax error, but I wrote it in Notepad, but you can get the idea :).
As you are using Entity Framework you are dealing with a more restrictive version of LINQ. LINQ is designed around immutable variables. You can get around it with having your mutable variables outside of your query, but with LINQ to Entities that option goes away.
One way of handling it would be to do your same query then loop through it and set the balances.
var result = (from a in entities.Payments
where a.StudentID == ParamStudentID
orderby a.DateAdded ascending
select new
{
Date = a.DateAdded,
Code = entities.Particulars.Where(p => p.Name == a.PaymentDes).Select(sp => sp.Code).FirstOrDefault(),
Particulars = a.PaymentDes,
Debit = 0,
Credit = a.Amount,
Balance = 0,
SyTerm = a.SchoolYear + "-" + a.Term.Trim().Substring(0, 5)
}).ToList();
double balance = 0;
foreach(var item in result)
{
balance += item.Credit;
item.Balance = balance;
}
There are probably other ways to do it all in the LINQ query but I think they will probably be relatively convoluted or make SQL that is pretty weird.
decimal balance = 0; //Change for clarity
var result = (from a in entities.Payments
where a.StudentID == ParamStudentID
select new
{
Date = a.DateAdded,
Code = entities.Particulars.Where(p => p.Name == a.PaymentDes).Select(sp => sp.Code).FirstOrDefault(),
Particulars = a.PaymentDes,
Debit = 0,
Credit = a.Amount,
SyTerm = a.SchoolYear + "-" + a.Term.Trim().Substring(0, 5)
}).ToList()
.Select(r=>new
{
Rec = r,
Balance = balance += r.Credit
});
NOTE: This only works the first time the query is used. You should probably throw a .ToList() on the end of the query, or not reuse it without reseting the accumulator.

Categories