Iterate over Entity Framework object properties - c#

I have a table "StaffMembers" that have columns indicating the number of days worked in a month, the properties in the model are as follows:
public class StaffMember
{
public int Id { get; set; }
public string Name { get; set; }
public string Gender { get; set; }
public int Phone { get; set; }
public string Email { get; set; }
public string BirthDate { get; set; }
public int OctDays { get; set; }
public int NovDays { get; set; }
public int DecDays { get; set; }
public int JanDays { get; set; }
public int FebDays { get; set; }
public int MarDays { get; set; }
public int AprDays { get; set; }
}
now I retrieve the specific staffMember using linq:
var staffMember = (from b in db.StaffMembers
where b.Id == Id
select b).FirstOrDefault();
what I want to do is to loop over the months properties in staffMember and add all the worked days together to get total working days in the year.
for example if he worked 10 days in oct and 20 days in dec and 30 days in jan, I want a way to iterate over the months and sum the days.

You can do it by iterating over object properties and apply your condition on it.
static void Main(string[] args)
{
var staffA = new StaffMember();
int totalWorkDays = 0;
staffA.AprDays = 5;
staffA.FebDays = 7;
foreach (var item in staffA.GetType().GetProperties())
{
if (item.Name.EndsWith("Days"))
{
totalWorkDays += (int)item.GetValue(staffA)!;
}
}
Console.WriteLine(totalWorkDays);
}
this snippet prints ( 5 + 7 ) => 12

You can use reflection to iterate over the properties, but I do not recommend this because you have to point anyway which properties you want to take into consideration. That's because you have multiple integer properties like Id and Phone so you cannot indicate in the loop that you want to sum integer properties, you have to show explicitly that you want to sum OctDays etc. or write some algorithms that indicates that the current property is responsible for the month. So the best way (and in my opinion simplier than reflection way) would be just to get each of the month explicit and sum like this:
var sum = staffMember.OctDays + staffMember.NovDays + staffMember.DecDays + staffMember.JanDays + staffMember.FebDays + staffMember.MarDays + staffMember.AprDays

Related

Merging data from two tables into one view

I am having some trouble getting the data from two different tables into one view. If you know how to do this please let me know. This is what I'm working with:
I have four tables:
public class CoinAllocation
{
public int CoinAllocationID { get; set; }
public int? StoreID { get; set; }
public int? TimeFrameID { get; set; }
public virtual Store Store { get; set; }
public virtual TimeFrame TimeFrame { get; set; }
public virtual List<CoinAllocationItem> CoinAllocationItems { get; set; }
}
public class CoinAllocationItem
{
public int CoinAllocationItemID { get; set; }
public int? CoinID { get; set; }
public int? StoreID { get; set; }
public int? CoinAllocationID { get; set; }
public int QuantityAllocated { get; set; }
public virtual Coin Coin { get; set; }
}
public class CoinUsed
{
public int CoinUsedID { get; set; }
public int? TimeFrameID { get; set; }
public int? StoreID { get; set; }
public virtual Store Store { get; set; }
public virtual TimeFrame TimeFrame { get; set; }
public virtual List<CoinUsedItem> CoinUsedItems { get; set; }
}
public class CoinUsedItem
{
public int CoinUsedItemID { get; set; }
public int? CoinUsedID { get; set; }
public int? CoinID { get; set; }
public int? QuantityUsed { get; set; }
public virtual Coin Coin { get; set; }
public int? StoreID { get; set; }
}
Now, I need iterate through these tables to find coins that are from the same store and the same time frame. Then, I need to combine coins with the same ID, total their allocation amount, and then total the amount that they have used. Last, I need to get them into one view that is set up like this:
Coin Name | Amount Allocated | Amount Used | Remaining
silver coin 10 1 9
gold coin 15 5 10
and so on...
So, if there are two silver coins from the same store during the same time frame, they show up in the table in just one line, with the totals.
The problem I am having is getting the allocated from one table and getting the used from the other table.
Anyone out there who can help will be amazing.
Generally, you have to consider the following steps:
Filter your result by desired TimeFrame and Shop (LINQ Where)
Select the properties that you are interested in or that are needed for further computations (LINQ Select and SelectMany)
Group the results and compute sums (LINQ GroupBy)
Join different sub-results, select final properties (LINQ Join and GroupJoin)
There's always more than one way. I imagine that using GroupJoin at some point might be more efficient than what I currently came up with and if you start with Coin instead of separately handling CoinAllocation and CoinUsed, you might get a better structured code depending on the available navigation properties...
The following is what I came up with, which might or might not satisfy your needs - there are some uncertainties in your presented model and criteria.
// whatever you search for... this assumes you want coins for one store in one timeframe
int desiredStoreID = 0, desiredTimeFrameID = 0;
var coinUsedSelection = db.CoinUsed
.Where(x => x.StoreID == desiredStoreID && x.TimeFrameID == desiredTimeFrameID)
.SelectMany(x => x.CoinUsedItems)
.GroupBy(x => x.CoinID, x => x.QuantityUsed, (k, v) => new { CoinID = k, QuantityUsedSum = v.Sum() });
var coinAllocationSelection = db.CoinAllocations
.Where(x => x.StoreID == desiredStoreID && x.TimeFrameID == desiredTimeFrameID)
.SelectMany(x => x.CoinAllocationItems)
.GroupBy(x => new { x.CoinID, x.Coin.CoinName }, x => x.QuantityAllocated, (k, v) => new { k.CoinID, k.CoinName, QuantityAllocatedSum = v.Sum() });
var result = coinAllocationSelection.Join(coinUsedSelection, ca => ca.CoinID, cu => cu.CoinID, (ca, cu) => new
{
CoinName = ca.CoinName,
AmountAllocated = ca.QuantityAllocatedSum,
AmountUsed = cu.QuantityUsedSum,
Remaining = ca.QuantityAllocatedSum - cu.QuantityUsedSum
})
.ToList();

Understanding MongoDb .NET driver indexing

I'm currently working on an application where MongoDb is used for quite a large amount of data.
The objects I'm storing in MongoDb looks like this:
public class PowerPlantDataReading
{
[BsonId]
public int ID { get; set; }
[BsonElement("EDIEL")]
public string EDIEL { get; set; }
[BsonElement("EndDate")]
public DateTime EndDate { get; set; }
[BsonElement("Created")]
public DateTime Created { get; set; }
[BsonElement("DataReading")]
public DataReading DataReading { get; set; }
}
public class DataReading
{
[BsonElement("Version")]
public int Version { get; set; }
[BsonElement("OriginalId")]
public int OriginalId { get; set; }
[BsonElement("Unit")]
public string Unit { get; set; }
[BsonRepresentation(MongoDB.Bson.BsonType.Double)]
[BsonElement("Quantity")]
public decimal Quantity { get; set; }
[BsonElement("Quality")]
public string Quality { get; set; }
[BsonElement("StartDate")]
public DateTime StartDate { get; set; }
}
And the query I'm running against MongoDb looks like this:
DateTime startDateUtc = DateTime.UtcNow.AddDays(-5);
DateTime endDateUtc = DateTime.UtcNow;
var queryBuilder = Builders<PowerPlantDataReading>.Filter;
var filter = queryBuilder.Where(x => x.EndDate >= startDateUtc && x.EndDate < endDateUtc);
var query = collection.Find(filter).ToListAsync();
return query.Result;
The query returns around 825.000 objects, but takes well over 4 minutes to run.
I then tried to create an index like this:
IMongoCollection<PowerPlantDataReading> collection = GetCollection();
collection.Indexes.CreateOne(Builders<PowerPlantDataReading>.IndexKeys.Descending(x => x.EndDate));
Then ran the query again, but to my surprise, it didn't make a difference at all.
I'm not sure if I'm creating the index correctly? If not, how should I create my index to get the best possible performance for the query?
Thanks in advance.

Week and day entities

I am learning C# and MVC with ASP.NET. I am making an application that takes daily data and splits it up into weeks.
My question is: How should I go about making the entities? I want it so that you have a WeekNo and a DayNo and you use them to display, edit and create data and tables. So for example, WeekNo = 1 and DayNo = 1 would be Monday in week 1. WeekNo = 1 and DayNo = 2 would be Tuesday and so on.
I'm new to this and not very good at information management. Do I need foreign keys?
I was thinking:
public class Day
{
public int WeekNo { get; set; }
public int DayID { get; set; }
public int DayNo { get; set; }
public string DayofWeek { get; set; }
//then other declarations, not important
[ForeignKey("DayNo")]
}
public class Week
{
public int ID { get; set; }
public int WeekNo { get; set; }
public int DayofWeek { get; set; }
}
Does the foreign key go in Day or Week or do I even need it?
On the assumption that you meant Objects when saying entities.
Here is how you can structure an Object for use per day:
public class myDailyObject
{
public myDailyObject()
{ }
public int ObjectID { get; set; }
public int WeekNo { get; set; }
public int DayNo { get; set; }
public string DayofWeek { get; set; }
//Other variables can go here.
}
Otherwise, you can use the following structure for use per week:
public class myWeeklyObject
{
public myWeeklyObject()
{
DataByDay = new Dictionary<int, myDataObject>();
}
public int ObjectID { get; set; }
public int WeekNo { get; set; }
public Dictionary<int, myDataObject> DataByDay { get; set; }
}
public class myDataObject
{
//Other variables can go here.
}
In here, you can use the Dictionary<int, myDataObject> called DataByDay to contain an instance of the myDataObject Object for each day of the week.
Example of usage:
The Weekly object:
//Declare the weekly object:
myWeeklyObject thisWeek = new myWeeklyObject();
//Populate variables of thisWeek here:
thisWeek.ObjectID = thisWeek.WeekNo = CultureInfo.InvariantCulture.Calendar.GetWeekOfYear(DateTime.Now, CalendarWeekRule.FirstFourDayWeek, DayOfWeek.Monday);
//The line above gets the current weekNo for the year.
You could also fetch this object from somewhere if one already exists for this week.
So now, the daily object:
//Get the day of the week as an int:
int dayOfWeek = (int)DateTime.Now.DayOfWeek + 1; //DayOfWeek is 0 based
//Daclare object for a specific day:
myDataObject newObject = new myDataObject();
//Populate variables of newObject here.
//Then add it with the int dayOfWeek as the Key to the DataByDay Dictionary.
thisWeek.DataByDay.Add(dayOfWeek, newObject);
I hope this answers your question (and future questions such as how to get the WeekNo), or at least is of some kind of help.
Good luck.

How to do nested group by in RavenDB multi map index

I have two different document collections in my RavenDB database - Teams and Matches. The documents look like this:
public class Team {
public string Id { get; set; }
public string Name { get; set; }
public int LeaguePosition { get; set; }
}
public class Match {
public string Id { get; set; }
public string HomeTeamName { get; set; }
public string AwayTeamName { get; set; }
public DateTime StartTime { get; set; }
}
So basically I have teams and matches between these teams. However, for certain operations I need to get an entity which look something like the following from the database:
public class MatchWithExtraData {
public string Id { get; set; } // Id from the match document.
public string HomeTeamId { get; set; }
public string HomeTeamName { get; set; }
public int HomeTeamPosition { get; set; }
public string AwayTeamId { get; set; }
public string AwayTeamName { get; set; }
public int AwayTeamPosition { get; set; }
public DateTime? StartTime { get; set; }
}
What I want is really the match document but with extra fields for the home and away teams' ids and league positions. Basically join the match document on home and away team name with two team documents, one for the home team and one for the away team. I figured that a multi map/reduce index should do the trick so I have started with the following index:
public class MatchWithExtraDataIndex: AbstractMultiMapIndexCreationTask<MatchWithExtraData> {
public MatchWithExtraData() {
AddMap<Team>(
teams => from team in teams
select new {
Id = (string)null,
HomeTeamId = team.Id,
HomeTeamName = team.Name,
HomeTeamPosition = team.LeaguePosition,
AwayTeamId = team.Id,
AwayTeamName = team.Name,
AwayTeamPosition = team.LeaguePosition,
StartTime = (DateTime?)null
}
);
AddMap<Match>(
matches => from match in matches
select new {
Id = match.Id,
HomeTeamId = (string)null,
HomeTeamName = match.HomeTeamName,
HomeTeamPosition = 0,
AwayTeamId = (string)null,
AwayTeamName = match.AwayTeamName,
AwayTeamPosition = 0,
StartTime = match.StartTime
}
);
Reduce = results => from result in results
// NOW WHAT?
}
}
The reduce part is the one I can't figure out since there are two teams in each match. I think I need to do a nested group by, first on the HomeTeamName, and then on the AwayTeamName but I can't figure out how to do that.
Maybe this is more a LINQ problem than a RavenDB problem. But how would such a nested group by statement look? Or could it be done in another way?
You are better off using Transform Results for that, or includes.
See the docs here: http://ravendb.net/docs/client-api/querying/handling-document-relationships

searching a date list

I have a collection list which contains the following fields
Date1
Xvalue
Yvalue
I want to seach within this list against another date list. For every date in the second list I want to get records starting between this minimum date and the next date is which (30 minutes to the minium date).
foreach (var item in selectedDates.Where(x => x.Checked))
{
// item.minDate is my starting date
// I want all records between item.minDate and 30 minutes added to it)
var t = lf.ReplicateBlocks.FindAll(o=> o.minimumCompletionDate >= item.
}
**UPDATE**
public class ReplicateBlock
{
public int ReplicateId { get; set; }
public string AssayNumber { get; set; }
public DateTime InitiationDate { get; set; }
public DateTime InitiationTime { get; set; }
public DateTime minimumCompletionDate { get; set; }
public DateTime minimumCompletionTime { get; set; }
public string correctedCount { get; set; }
public string moduleName { get; set; }
public string exception { get; set; }
}
public class RunLogEntryDatesDisplay
{
public DateTime runDate { get; set; }
public String DateRange { get; set; }
public bool Checked { get; set; }
public string MinimumReplicateId { get; set; }
}
The final output I am looking for is a revised Replicate Block list. RunLogEntryDatesDisplay is a checkbox list posted from the view. In this list I look at the checked date which is runDate and starting from the first selection I add 30 minutes to it and find all records in ReplicateBlock List in between and the edges. I will do the same for every selected date in the checbox list and in the end will have a final/filtered ReplicateBlockLisr based on users selections(checked item).
You could loop through the dates and populate a result list:
List<ReplicateBlock> blocks = new List<ReplicateBlock>();
foreach (var item in selectedDates.Where(x => x.Checked))
{
var t = lf.ReplicateBlocks.Where(o=>
o.minimumCompletionDate >= item.minDate &&
o.minimumCompletionDate <= item.minDate.AddMinutes(30));
blocks.AddRange(t);
}
You could also do it in one query:
var query = from d in selectedDates
from o in lf.ReplicateBlocks
where d.Checked &&
o.minimumCompletionDate >= d.minDate &&
o.minimumCompletionDate <= d.minDate.AddMinutes(30))
select o;

Categories