Linq-to-SQL gridview add sum column - c#

I have this linq query in GridView
var table = from ll in dbo.Workers
join p in dbo.WorkDays on ll.Id equals p.Id
orderby p.Enter
select new
{
ll.Id,
ll.Name,
ll.Salary,
p.Enter,
p.ExitT,
p.Place,
WorkTime = Math.Round(getWorkTime(p.Enter, p.ExitT), 2),
Earned = Math.Round(getEarned(p.Enter, p.ExitT, ll.Salary), 2),
};
How can I add column of sum to the table?

Create class which will hold data and provide calculations based on that data:
public class WorkerInfo
{
// I don't know exact type of fields, but its enough for you to get the idea
public int Id { get; set; }
public string Name { get; set; }
public decimal Salary { get; set; }
public DateTime Enter { get; set; }
public DateTime ExitT { get; set; }
public string Place { get; set; }
public int WorkTime
{
get { return (ExitT - Enter).TotalDays; }
}
public decimal Earned
{
get { return WorkTime * Salary; }
}
}
Fill that these entities with data:
var workers = from ll in dbo.Workers
join p in dbo.WorkDays on ll.Id equals p.Id
orderby p.Enter
select new WorkerInfo
{
Id = ll.Id,
Name = ll.Name,
Salary = ll.Salary,
Enter = p.Enter,
ExitT = p.ExitT,
Place = p.Place
};
UPDATE Sample for returning aggregated data for all days:
var query = from w in dbo.Workers
join d in dbo.WorkDays on w.Id equals d.Id into days
let WorkTime = days.Sum(d => d.ExitT - d.Enter)
select new
{
w.Id,
w.Name,
w.Salary,
WorkTime,
Earned = WorkTime * w.Salary
};

Related

Groupby, select EF Ccore 3.1

I have three tables like this:
public partial class PriceSite
{
public DateTime ValidFromGmtDtm { get; set; }
public long PriceSiteId { get; set; }
public virtual ICollection<PriceSiteProduct> PriceSiteProduct { get; set; }
}
public partial class PriceSiteProduct
{
public long PriceSiteId { get; set; }
public long ProductId { get; set; }
public virtual Product Product { get; set; }
}
public partial class Product
{
public string ProductCd { get; set; }
public string ProductNm { get; set; }
public long ProductId { get; set; }
public virtual ICollection<PriceSiteProduct> PriceSiteProduct { get; set; }
}
We need to group the data by Productname and choose the product with highest ValidFromGmtDtm (Pricesite).
I tried this query
using (var dbContext = _dbContextProvider.DbContext)
{
var data = from t1 in dbContext.PriceSite
join t2 in dbContext.PriceSiteProduct
on t1.PriceSiteId equals t2.PriceSiteId
join t3 in dbContext.Product
on t2.ProductId equals t3.ProductId
where t1.ValidFromGmtDtm > shellSiteNotification.ValidFromDtm
select new
{
PriceSiteId = t1.PriceSiteId,
ValidFrom = t1.ValidFromGmtDtm,
ProductId = t3.ProductId,
ProductNm = t3.ProductNm,
ProductValues = t3.PumpPriceProduct
};
var res1 = data.ToList();
var data2 = from element in res1
group element by element.ProductNm into groups
select groups.OrderByDescending(p => p.ValidFrom).FirstOrDefault();
}
I am getting timeout error by running this query.
I also tried this query:
var grouped = from priceSite in _dbContextProvider.DbContext.PriceSite
where priceSite.ValidFromGmtDtm < shellSiteNotification.ValidFromDtm
from priceSiteProduct in priceSite.PriceSiteProduct
group priceSiteProduct by priceSiteProduct.Product.ProductNm into produtGroup
select new
{
ProductNamKey = produtGroup.Key,
produtGroup = produtGroup.OrderByDescending(x => x.PriceSite.ValidFromGmtDtm)
};
This code throws a runtime error as we cannot access the navigation property in select in EF Core.
I also tried another query - but this also results in a timeout
var result = from s in _dbContextProvider.FreshReadOnlyPricingDbContext.PriceSite
from r in s.PriceSiteProduct
let p = r.Product
select new
{
PriceSiteId = s.PriceSiteId,
ValidFrom = s.ValidFromGmtDtm,
ProductId = p.ProductId,
ProductNm = p.ProductNm,
ProductValues = p.PumpPriceProduct
};
var list = result.ToList();
It is known EF and SQL limitation, you cannot get items after grouping. Only Key and aggregation result is supported. So basically EF proposes to put ToList() before grouping and it means that you will load several million records to the client.
Without third party extensions which can do that effectively, you can use the following workaround:
using (var dbContext = _dbContextProvider.DbContext)
{
var dataQuery =
from t1 in dbContext.PriceSite
join t2 in dbContext.PriceSiteProduct
on t1.PriceSiteId equals t2.PriceSiteId
join t3 in dbContext.Product
on t2.ProductId equals t3.ProductId
where t1.ValidFromGmtDtm > shellSiteNotification.ValidFromDtm
select new
{
PriceSiteId = t1.PriceSiteId,
ValidFrom = t1.ValidFromGmtDtm,
ProductId = t3.ProductId,
ProductNm = t3.ProductNm,
ProductValues = t3.PumpPriceProduct
};
var query =
from key in dataQuery.Select(d => new { d.ProductNm }).Distinct()
from d in dataQuery.Where(d => d.ProductNm == key.ProductNm)
.OrderByDescending(d => d.ValidFrom)
.Take(1)
select d;
var result = query.ToList();
}

How to caculate the working hours for employees when i have the time when the got "in" and "out"

So i have a database that is connected with a device at the door that employees check when they come in and go out now i can get the history of statuses but i wanted to know how can I calculate the working hours for a day a week and a month for each employee. I got a this class for employee .
public class Employee : BaseEntity
{
public Employee()
{
this.HistoryOfStatuses = new List<Checkinout>();
this.TodayCheckedStatus = new List<Checkinout>();
}
public string Name { get; set; }
public string Department { get; set; }
public string CardNumber { get; set; }
public string Status { get; set; }
public byte[] Picture { get; set; }
public Checkinout ActualCheckinStatuse { get; set; }
public List<Checkinout> HistoryOfStatuses { get; set; }
public List<Checkinout> TodayCheckedStatus { get; set; }
public int UserId { get; internal set; }
And this is the checking status class
public class Checkinout : BaseEntity
{
public Checkinout()
{
}
public int EmployeeId { get; set; }
public string CheckStatus { get; set; }
public DateTime CheckTime { get; set; }
public Employee EmployeeObject { get; set; }
}
My controller looks something like this
public IActionResult Index()
{
using (var context = new RSAT.Api.Data.Proxy.ATT2018NOVUSContext())
{
var baseViewModel = base.GetLayoutViewModel();
var viewModel = new HomeViewModel()
{
User = baseViewModel.User,
RoleCollection = baseViewModel.RoleCollection,
TableCollection = baseViewModel.TableCollection,
//Olap = baseViewModel.Olap,
//Localization = baseViewModel.Localization,
EmployeeCollection = (from userinfo in context.Userinfo
join department in context.Dept on userinfo.Deptid equals department.Deptid
select new Employee()
{
Id = userinfo.Userid,
Name = userinfo.Name,
Picture = userinfo.Picture,
Department = department.DeptName,
CardNumber = userinfo.CardNum,
Status = userinfo.UserFlag.ToString(),
ActualCheckinStatuse = (from checkinout in context.Checkinout
join status in context.Status on checkinout.CheckType equals status.Statusid
where checkinout.Userid == userinfo.Userid
orderby checkinout.CheckTime descending
select new Checkinout
{
CheckStatus = status.StatusText,
CheckTime = checkinout.CheckTime
}).FirstOrDefault()
}).ToList()
};
return View(viewModel);
}
}
public IActionResult WorkingHours()
{
var inTime = "10:00";
var outTime = DateTime.Now.TimeOfDay;
var totalHours = Convert.ToDateTime(inTime).TimeOfDay.Subtract(outTime);
}
I wanted someones help to clear me out how to do this and how can i connect the last code in my controller .
Does your model have a property somewhere determining whether an entry is an in or out check? You will need this.
What you can do now is create a method that takes two dates that form a range. You can return the total work hours inside this range. You can then easily create a method for a week, month, year, where you specify the date ranges to this method.
Here's some pseudocode to get you started.
decimal GetRangeWorkHours(Employee employee, DateTime startOfRange, DateTime endOfRange){
//1. Ask the database for all the entries from this employee within the range of dates.
//2. Sort them by date, figuring out what entry the start/end is. You might get some edgecases where employees clock out midday and then clock back in later on the day. You can work this out using TimeSpans.
//3. Calculate the difference between all the now paired in/out entries.
//4. Sum the results of the previous step, return the result.
}
Consuming for week/month/year becomes easy
Employee someEmployee = new Employee(/*wetherever you use to identify employees*/);
//Replace these 2 values with whatever you need to provide your user with.
DateTime startOfRange = new DateTime(2019, 1, 1);
DateTime endOfRange = startOfRange.AddDays(7);
decimal workHours = GetRangeWorkHours(someEmployee, startOfRange, endOfRange);

Linq query to return list within a list

Hi I have following two model classes
public class c1
{
public int id { get; set; }
public int ptId { get; set; }
public int bId { get; set; }
public int rId { get; set; }
public IEnumerable<styles> newStruct { get; set; }
}
public class styles
{
public int id { get; set; }
public int bId { get; set; }
public string desc { get; set; }
}
I am trying to write a linq query
var records = (from y in db.main
join c in db.secondary on y.bId equals c.bId
where c.id == id
select new c1
{
pId= c.pId,
id = c.id,
newStruct = new List<styles>
{
new styles
{
id=y.room_id,
desc=y.desc,
}
}
});
return records.ToList();
Problem I am having is that in newStruct is suppose to be List of all the styles but it just returns one style at a time rather than one list.
Please let me know how can it return record where inside it contains list of styles
Thanks
If you want to get a sublist by main list, you should use group by,
You can try this, but I'm not sure it worked. Becasue I couldn't compile it.
var records = (from y in db.main
join c in db.secondary on y.bId equals c.bId
where c.id == id
group c by new
{
c.pId,
c.id
} into gcs
select new c1
{
pId = c.Key.pId,
id = c.Key.id,
newStruct = from g in gcs select new styles { id=g.room_id, desc=g.desc}
});
Is this LINQ to Entities? If so, and the mappings are correct in the edmx, you can try:
var records =
from c in db.secondary
where c.id == id
select new c1
{
pId = c.pId,
id = c.id,
newStruct = c.main.Select(m => new styles { id = m.room_id, desc = m.desc })
};
return records.ToList();

LINQ: Fill objects from left join

I'm new to linq. How do I load my objects using LINQ from a left join database query (PostGIS).
This is my database query:
SELECT
dt.id,
dt.type,
dta.id as "AttId",
dta.label,
dtav.id as "AttValueId",
dtav.value
FROM public."dataTypes" dt,
public."dataTypeAttributes" dta
LEFT JOIN public."dataTypeAttributeValues" dtav
ON dta.id = "dataTypeAttributeId"
WHERE dt.id = dta."dataTypeId"
ORDER BY dt.type, dta.label, dtav.value
And here is example output:
I have 3 entities:
public class TypeData
{
public int ID { get; set; }
public string Type { get; set; }
public TypeDataAttribute[] Attributes { get; set; }
}
public class TypeDataAttribute
{
public int ID { get; set; }
public string Label { get; set; }
public TypeDataAttributeValue[] Values { get; set; }
}
public class TypeDataAttributeValue
{
public int ID { get; set; }
public string Value { get; set; }
}
Update
Here is where I get stuck:
...
using (NpgsqlDataReader reader = command.ExecuteReader())
{
if (reader.HasRows)
{
IEnumerable<TypeData> typeData = reader.Cast<System.Data.Common.DbDataRecord>()
.GroupJoin( //<--stuck here.
}
...
SOLUTION:
using AmyB's answer, this is what filled my objects:
using (NpgsqlDataReader reader = command.ExecuteReader())
{
if (reader.HasRows)
{
var groups = reader.Cast<System.Data.Common.DbDataRecord>()
.GroupBy(dr => new { ID = (int)dr["id"], AttID = (int)dr["AttId"] })
.GroupBy(g => g.Key.ID);
typeDataList = (
from typeGroup in groups
let typeRow = typeGroup.First().First()
select new TypeData()
{
ID = (int) typeRow["id"],
Type = (string) typeRow["type"],
Attributes =
(
from attGroup in typeGroup
let attRow = attGroup.First()
select new TypeDataAttribute()
{
ID = (int)attRow["AttId"],
Label = (string)attRow["label"],
PossibleValues =
(
from row in attGroup
where !DBNull.Value.Equals(attRow["AttValueId"])
select new TypeDataAttributeValue() { ID = (int)row["AttValueId"], Value = (string)row["value"] }
).ToArray()
}
).ToArray()
}
);
}
}
So - if I understand right - you have a database query that you are happy with, but you want to take the row-column shaped result and project it into a hierarchically shaped result.
Suppose the results are in a List<Row>
public class Row
{
public int id {get;set;}
public string type {get;set;}
public int attid {get;set;}
public string label {get;set;}
public int? attvalueid {get;set;}
public string value {get;set;}
}
Then you would group twice, and turn each top-level group into a Type, each child-level group into an Attribute and each row into a Value (if the row is not an empty value).
List<Row> queryResult = GetQueryResult();
//make our hierarchies.
var groups = queryResult
.GroupBy(row => new {row.id, row.attid})
.GroupBy(g => g.Key.id);
//now shape each level
List<Type> answer =
(
from typeGroup in groups
let typeRow = typeGroup.First().First()
select new Type()
{
id = typeRow.id,
type = typeRow.type,
Attributes =
(
from attGroup in typeGroup
let attRow = attGroup.First()
select new Attribute()
{
id = attRow.attid,
label = attRow.label
Values =
(
from row in attRow
where row.attvalueid.HasValue //if no values, then we get an empty array
select new Value() {id = row.attvalueid, value = row.value }
).ToArray()
}
).ToArray()
}
).ToList();
var q = (from dt in public."dataTypes"
// Join the second table to the first, using the IDs of the two tables <-- You may join on different columns from the two tables
join dta in public."dataTypeAttributes" on
new { ID = dt.id } equals
new { ID = dta.id }
// Join the third table to the first, using the IDs of the two tables
join dtav in public."dataTypeAttributeId" on
new { ID = dt.id } equals
new { ID = dtav.id }
// Conditional statement
where dt.id == dta."dataTypeId"
// Order the results
orderby dt.type, dta.label, dtav.value
// Select the values into a new object (datObj is a created class with the below variables)
select new datObj() {
ID = dt.id,
Type = dt.type,
AttID = dta.id,
Label = dta.label,
AttValueID = dtav.id,
AttValue = dtav.value
}).ToList()
This will return a List that match your where statement, in the order specified by the orderby statement.
Not exactly what you need, but should give you an example of what you should be doing.
Try GroupJoin sytnax
There is also Join syntax for regular inner join
This link has an example of group join
Yopu should check this: http://codingsense.wordpress.com/2009/03/08/left-join-right-join-using-linq/

Get Linq Subquery into a User Defined Type

I have a linq query, which is further having a subquery, I want to store the result of that query into a user defined type, my query is
var val = (from emp in Employees
join dept in Departments
on emp.EmployeeID equals dept.EmployeeID
select new Hello
{
EmployeeID = emp.EmployeeID
Spaces = (from order in Orders
join space in SpaceTypes
on order.OrderSpaceTypeID equals space.OrderSpaceTypeID
where order.EmployeeID == emp.EmployeeID group new { order, space } by new { order.OrderSpaceTypeID, space.SpaceTypeCode } into g
select new
{
ID = g.Key.SpaceTypeID,
Code = g.Key.SpaceTypeCode,
Count = g.Count()
})
}).ToList();
Definition for my Hello class is
public class Hello
{
public IEnumerable<World> Spaces { get; set; }
public int PassengerTripID { get; set; }
}
Definition for my World class is
public class World
{
public int ID { get; set; }
public string Code { get; set; }
public int Count { get; set; }
}
You are creating anonymous object but you need to specify type name World
select new World
{
ID = g.Key.SpaceTypeID,
Code = g.Key.SpaceTypeCode,
Count = g.Count()
})

Categories