Transforming into another object using LINQ Grouping - c#

I have a list of exams which has happened on a particular time, and the mark obtained by a student for that Exam contained in an object as below:
class Exam
{
public DateTime? DateTime { get; set; }
public double? Mark { get; set; }
public string Student { get; set; }
public int Age { get; set; }
}
I need to transform this into another format for display purposes using the following structure:
class ExamViewModel
{
public DateTime? DateTime { get; set; }
public double? Mark { get; set; }
List<StudentVieWModel> Students { get; set; }
}
class StudentVieWModel
{
public string Student { get; set; }
public int Age { get; set; }
}
class ResultViewModel
{
List<ExamViewModel> Exams { get; set; }
}
Basically what I want ot display is an exam which has taken place at a particuar date, and the total marks achieved in that by all student (S1 = 10, S2 = 20, Total = 30 at 19/06/2014). The students should be transformed into the Student ViewModel so that I can see who all students have attended the exam. How can I do this using LINQ?
Thanks,
-Mike
Note: Using .NET4
Tried:
exams.OrderBy(x=>x.DateTime)
.GroupBy(x => x.DateTime, x=> x, (d, students) => new{Date= x, Students = students.ToList()} );
But cant get the total of marks..:(

It's cleaner IMHO to use a GroupBy with a subsequent Select instead of using the overload of GroupBy that includes a projection:
var query =
exams.GroupBy(x => x.DateTime)
.Select(g => new {
Date = g.Key,
TotalMarks = g.Sum(s => s.Mark)
}
);

Related

Entity Framework Select top row from each type based on subobject value

I am using EF6 Code First approach. I have a the following related tables Events, Markets, MarketTypes with these models
public class Event
{
public Guid ID { get; set; } = Guid.NewGuid();
public List<Market> Markets { get; set; } = new List<Market>();
}
public class Market
{
public Guid ID { get; set; } = Guid.NewGuid();
public Guid EventID { get; set; }
public Event Event { get; set; }
public Guid TypeID { get; set; }
public MarketType Type { get; set; }
public DateTime InitiateDateTime { get; set; }
public DateTime LastUpdateDateTime { get; set; }
public double Value { get; set; }
}
public class MarketType
{
public Guid ID { get; set; } = Guid.NewGuid();
public string Name { get; set; }
public int Order { get; set; }
}
Each event have many markets and each market has one type. I am using the following statement to get list of events.
List<Event> events = new List<Event>();
var startDate = new DateTime(DateTime.UtcNow.Year, DateTime.UtcNow.Month, DateTime.UtcNow.Day);
var endDate = startDate.AddHours(24);
events = _context.Events
.Include(e => e.Markets)
.Include(e => e.Markets.Select(i => i.Type))
.Where(e => e.DateTime >= startDate)
.Where(e => e.DateTime < endDate)
.OrderBy(e => e.DateTime)
.ToList();
Is it possible to update the above query in order to retrieve one market row of each type which is the last updated one (having largest 'LastUpdateDateTime' value). I am currently doing this through an ugly manual method.
if your want last updated values, I use in this case OrderByDescending, that will be sort a list with last .
like this:
_context.Events
.Inlcude(e=>e.Markets)
.Include(e=>e.Markets.Select(I=>I.Type))
.Where(e=>e.DateTime>= startDate && e.DateTime<endDate)
.OrderBy(e.DateTime)
.OrderByDescending(e.LastUpdateDateTime )
.ToList();
you can't use ThenbBy linq cmd because OrderBy is for ascending and it is in conflict with OrderByDescending.
with this you have a list.
After you need the cmd First() or FirstOrDefault() to get only the largest one.
I am not sure but if you use cmd FirstOrDefault() instead of where cmd it could be work.
is it good for you?

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();

One to Many LINQ Query - compiler error

Please see the LINQ query below:
var test = from s in db.Students
join c in db.Courses on s.courseid equals c.id into d
where s.name.StartsWith("Bert")
select new Student { id=s.id,name=s.name, Course = d.Select(x => x.name) };
A student links to one courses. Therefore the value of Course in the above should be a collection of courses. However, there is a compiler error (System.ArguementNullException). What am I doing wrong?
I am competent using SQL, however I am new to LINQ. Please see the SQL from the database below:
***Student Class***
public partial class Student
{
public int id { get; set; }
public string name { get; set; }
public Nullable<int> age { get; set; }
public Nullable<int> courseid { get; set; }
public virtual Course Course { get; set; }
}
Course Class
public partial class Course
{
public Course()
{
this.Students = new HashSet<Student>();
}
public int id { get; set; }
public string name { get; set; }
public virtual ICollection<Student> Students { get; set; }
}
It seems you are just trying to get your query to also retrieve the Course navigation property for your students. As such, all you need to do is Include it:
var students = db.Students
.Include(s => s.Course)
.Where(s => s.Name.StartsWith("Bert");

Entity Framework include with where clause

I am currently working on a cinema booking system for a school project and have run into a problem.
I have a Movie model which contains a list of show models(date and time for the viewing of the movie). I need to get all the movie objects that are shown on a specific date with a list containing only the Show objects which date is equal to the specific date. I have tried various ways to do it in entity but cant seem to get it to work.
Here is the Movie class:
[DataContract]
public class Movie
{
[Key, Required, DataMember]
public Guid Id { get; set; }
[Required, DataMember]
public string Name { get; set; }
[Required, DataMember]
public string Info { get; set; }
[Required, DataMember]
public DateTime Premiere { get; set; }
[Required, DataMember]
public MovieType Type { get; set; }
[DataMember]
public ICollection<Show> Shows { get; set; }
[DataMember]
public string IMDBID { get; set; }
}
public enum MovieType
{
Movie2D = 0,
Movie3D = 1,
KidsMovie = 2
}
Here is the Show class:
[DataContract]
public class Show
{
[Key, Required, DataMember]
public Guid Id { get; set; }
[Required, DataMember]
public Guid MovieId { get; set; }
[Required, DataMember]
public Guid ScreenId { get; set; }
[Required, DataMember]
public DateTime DateTime { get; set; }
[Required, DataMember]
public ShowType Type { get; set; }
[DataMember]
public Screen Screen { get; set; }
[DataMember]
public Movie Movie { get; set; }
}
public enum ShowType
{
Standard = 0,
Premiere = 1,
}
Here is the GetMovies(DateTime date) method:
public List<Movie> GetMovies(DateTime date)
{
using (EntityContext db = new EntityContext())
{
List<Movie> movieList = db.Movies
.Include("Show")
.Where(x => x.Shows.Where(x => x.DateTime.Date == date.Date)).ToList();
return movieList;
}
}
I know that this function isn't working but hope it would show what I am trying to do.
I think DateTime.Date is not supported in Linq to Entities. You could use DbFunctions.TruncateTime static method:
var justDate= date.Date;
var movieList=db.Movies.Include(x=>x.Shows)
.Where(x => x.Shows.Any(x => DbFunctions.TruncateTime(x.DateTime) == justDate))
.ToList();
Update:
After read #Jonathan's comment I did a little research and it's true using DbFunctions.TruncateTime could affect the performance. You can find a detailed explanation in this post.
Following the same idea of #JonathanAllen and #MattJohnson, you can avoid to use that function if you do a range query instead, truncating first the Time from date parameter:
var startDate= date.Date;
var endDate= startDate.AddDays(1);
var movieList=db.Movies.Include(x=>x.Shows)
.Where(x => x.Shows.Any(x =>x.DateTime >= startDate && x.DateTime < endDate)
.ToList();
You should be able to use Any
public List<Movie> GetMovies(DateTime date)
{
using (EntityContext db = new EntityContext())
{
List<Movie> movieList = db.Movies.Include("Show")
.Where(x => x.Shows.Any(x => x.DateTime.Date == date.Date));
return movieList;
}
}
My request good working with All() method.
model.ReferenceList = db.JournalCardReference.OrderBy(a => a.orderF)
.Include(x => x.JournalCardField)
.Where(x => x.JournalCardField
.All(f => f.deleted == null || f.deleted != true)).ToList();

Trouble populating selectlist in controller code - C# MVC

I have a fairly straight forward requirement - to populate a viewmodel, which has a SelectList as one of its properties - NewOccs is defined on the model as:
public class RatesList
{
[Key]
public long TypeID { get; set; }
public string TypeName { get; set; }
public int TypeCount { get; set; }
public IEnumerable<SelectListItem> NewOccs { get; set; }
}
My controller code to populate it is:
var rooms = dbt.Rooms.Where(r => r.hotel_id == AccID)
.GroupBy(p => p.RoomTypes).Select(g => new RatesList
{
TypeName = g.Key.type_name,
TypeCount = g.Count(),
NewOccs = dbt.Rates.Where(rt => rt.type_id == g.Key.type_id).GroupBy(rt => rt.occ).AsEnumerable()
.Select(proj => new SelectListItem
{
Text = proj.Key,
Value =proj.Key
})
}).ToList();
The Rates table it should be getting its information from is:
public class Rates
{
public int id { get; set; }
public long type_id { get; set; }
public DateTime ratedate { get; set; }
public decimal rate { get; set; }
public string occ { get; set; }
}
How to I access any of the other fields in my Rates table - when I'm populating the SelectList? For example, in VSExpressIDE intellisense only allows me to type proj.Key - the other properties are not there. I want occ to be the key/value and I would like the text to be a concatenation of occ and rate - ie:
Text = proj.occ + ' ' + rate.ToString()
...but rate and occ cannot be found in intellisense.
Thank you, Mark
If you step through your debugger, you'll see that GroupBy() provides a GroupedEnumerable, which contains Keys. The keys are Lookup<string, Rates>, because you used GroupBy on a string.
If you changed your Select to a SelectMany, you'd see all your Rates. But that would defeat the purpose of the GroupBy. I'm not totally sure what you want in the end, but here is a good guide to GroupBy
Like this:
public class Client
{
public int SelectedSexId { get; set; }
public IList<Sex> SexList { get; set; }
public IList<SelectListItem> SexListSelectListItems
{
get
{
SexList=SexList??new List<Sex>();
var list = (from item in SexList
select new SelectListItem()
{
Text = item.Name,
Value = item.Id.ToString(CultureInfo.InvariantCulture)
}).ToList();
return list;
}
set { }
}
}

Categories