C# Entity Framework, Simplify Select Child Property That Using Same Condition - c#

I have 2 ef model, let's say Task() and Progress()
public class Task()
{
public int Id { get; set; }
public string Title { get; set; }
}
and then
public class Progress()
{
public int Id { get; set; }
public int Id_Task { get; set; }
public string Status { get; set; }
public DateTime Update { get; set; }
}
I want to get data for each task and it's last progress, So i make a ViewModel
public class ViewTask()
{
public int Id { get; set; }
public string Title { get; set; }
public string Last_Status { get; set; }
public DateTime Last_Update { get; set; }
}
Right now, this is what i did:
var data = _context.Task
.Select(x => new ViewTask
{
Id = x.Id,
Title = x.Title,
Last_Status = x.Progress.OrderByDescending(y => y.Update)
.Select(y => y.Status).FirstOrDefault(),
Last_Update = x.Progress.OrderByDescending(y => y.Update)
.Select(y => y.Update).FirstOrDefault()
}).ToList();
Is there any better ways so I don't need to repeat x.Progress... part? The only code i can think of is something like this:
var data = _context.Task
.Select(x => new
{
Id = x.Id,
Title = x.Title,
Progress = x.Progress.OrderByDescending(y => y.Update).FirstOrDefault()
})
.Select(x => new ViewTask
{
Id = x.Id,
Title = x.Title,
Last_Status = x.Progress.Status
Last_Update = x.Progress.Update
}).ToList();
I appriciete any kind of suggestion. Thanks :)

You can use MaxBy and MinBy methods from moreLINQ library.
var data = _context.Task
.Select(x => new ViewTask
{
Id = x.Id,
Title = x.Title,
Last_Status = x.Progress.MaxBy(y => y.Update).Status,
Last_Update = x.Progress.MaxBy(y => y.Update).Update
}).ToList();
Hope it helps!!

Related

Convert DataTable to a nested object using LINQ

I have a stored proc returning a datatable using a stored procedure. I am able to convert the it to an object using the following code
outgoingPaymentData.AsEnumerable().Select(x => new OutgoingPaymentApprovalDetails() { });
Here is my OutgoingPaymentApprovalDetails class
public class OutgoingPaymentApprovalDetails
{
public int OriginatorId { get; set; }
public string Name { get; set; }
public string DocumentId { get; set; }
public string DebtorName { get; set; }
public string Currency { get; set; }
public double Amount { get; set; }
public string Description { get; set; }
public string DebitAccountNo { get; set; }
public string CreditAccountNo { get; set; }
}
Now, instead of a flat list, I need to add heirarchy, to select this one object to 3 objects.
Classes as under:
public class OriginatorDetails
{
public int OriginatorId { get; set; }
public string Name { get; set; }
public List<DocumentDetails> DocumentDetails { get; set; }
}
public class DocumentDetails
{
public string DocumentId { get; set; }
public List<TransactionDetails> TransactionDetails { get; set; }
}
public class TransactionDetails
{
public string Amount { get; set; }
public string DebitAccountNo { get; set; }
public string CreditAccountNo { get; set; }
}
Basically, All Documents of a particular Originator have to be in the list of DocumentDetails and all TransactionDetails of a particular document have to be in that list.
One way is to create a dictionary and add stuff in it and finally create an object. I was wondering if there was a more abbreviated and efficient way to do something like this.
TIA
You can do the grouping of retrieved records of OutgoingPaymentApprovalDetails using Linq to create the nested object of OriginatorDetails collection.
see below code
var originalDetails = inputs.GroupBy(g => g.OriginatorId)
.Select(g => new OriginatorDetails()
{
OriginatorId = g.Key,
Name = g.First().Name,
DocumentDetails = g.GroupBy(d => d.DocumentId)
.Select(d => new DocumentDetails()
{
DocumentId = d.Key,
TransactionDetails = d.Select(t => new TransactionDetails()
{
DebitAccountNo = t.DebitAccountNo,
CreditAccountNo = t.CreditAccountNo,
Amount = t.Amount.ToString()
}).ToList()
})
.ToList()
});
Check the created https://dotnetfiddle.net/FCA7Qc to demostrate your scenario.
Try this code:
Basically you need to group 2 times, first time by OriginatorId and Name and then by DocumentId like this:
var result = list.GroupBy(c => new {c.OriginatorId, c.Name})
.Select(g => new OriginatorDetails()
{
Name = g.Key.Name,
OriginatorId = g.Key.OriginatorId,
DocumentDetails = g
.GroupBy(dd => dd.DocumentId)
.Select(dd => new DocumentDetails()
{
DocumentId = dd.Key,
TransactionDetails = dd.ToList()
.Select(td => new TransactionDetails()
{
Amount = td.Amount.ToString(),
CreditAccountNo = td.CreditAccountNo,
DebitAccountNo = td.DebitAccountNo
}).ToList()
}).ToList()
}).ToList();

GroupBy and then Sum

I am trying to take the items from sortedItems and then create a GroupBy MaterialCode and MaterialThickness. Then I want to find the sum of NbrOfSheets where MaterialCode and MaterialThickness are the the same.
Here is my code.
var sortedItems =
db.Jobs
.Where(x => x.JobStatus == "O")
.OrderBy(x => x.Material.MaterialCode).ThenBy(x => x.MaterialThickness)
.ThenBy(x => x.IdealSheetSize).ThenByDescending(x => x.DueDate)
.Select(x => new
{
x.JobNum,
x.Material.MaterialCode,
x.MaterialThickness,
x.Part.CustPartNum,
x.Part.CustName,
x.IdealSheetSize,
x.NbrOfSheets,
x.DueDate,
x.ShipmentNotes,
JobRoutings = x.JobRoutings.Select(e => new
{
e.ProcessID,
e.CrewSize,
e.PiecesPerHour,
e.OnHand,
e.PC,
e.LastScannedDate,
e.OpSeq
})
}).AsNoTracking().ToList();
Group by an anonymous type with these properties, then use Sum on the groups:
var result = sortedItems
.GroupBy(x => new { x.MaterialCode, x.MaterialThickness })
.Select(g => new {
g.Key.MaterialCode, g.Key.MaterialThickness,
NbrOfSheetsSum = g.Sum(x => x.NbrOfSheets)
});
I modeled your database with classes to get syntax correct. See answer below :
class Program
{
static void Main(string[] args)
{
DataBase db = new DataBase();
var sortedItems =
db.Jobs
.Where(x => x.JobStatus == "O")
.OrderBy(x => x.Material.MaterialCode).ThenBy(x => x.MaterialThickness).ThenBy(x => x.IdealSheetSize).ThenByDescending(x => x.DueDate)
.Select(x => new
{
x.JobNum,
x.Material.MaterialCode,
x.MaterialThickness,
x.Part.CustPartNum,
x.Part.CustName,
x.IdealSheetSize,
x.NbrOfSheets,
x.DueDate,
x.ShipmentNotes,
JobRoutings = x.JobRoutings.Select(e => new
{
e.ProcessID,
e.CrewSize,
e.PiecesPerHour,
e.OnHand,
e.PC,
e.LastScannedDate,
e.OpSeq
})
}).ToList();
var groups = sortedItems.GroupBy(x => new { materialCode = x.MaterialCode, materialThickness = x.MaterialThickness })
.Select(x => new { job = x, count = x.Sum(y => y.NbrOfSheets) }).ToList();
}
}
public class DataBase
{
public List<Job> Jobs { get; set; }
}
public class Job
{
public string JobStatus { get; set; }
public Material Material { get; set; }
public string MaterialThickness { get; set; }
public string IdealSheetSize { get; set; }
public DateTime DueDate { get; set; }
public int JobNum { get; set; }
public Part Part { get; set; }
public int NbrOfSheets { get; set; }
public string ShipmentNotes { get; set; }
public List<JobRoutings> JobRoutings { get; set; }
}
public class Material
{
public string MaterialCode { get; set; }
}
public class Part
{
public string CustPartNum { get; set; }
public string CustName { get; set; }
}
public class JobRoutings
{
public string ProcessID {get;set;}
public string CrewSize {get;set;}
public string PiecesPerHour {get;set;}
public string OnHand {get;set;}
public string PC {get;set;}
public string LastScannedDate {get;set;}
public string OpSeq { get; set; }
}

Populate multiple collections on same type

I need to populate a Product object which contains two collections.
The current code works fine and populates the Product.GraphicItems collection, but I also need to populate the Product.InfoItems collection, but I can't figure out the syntax for multiple collections.
Current select:
var result = await this.Context.ShopCategorys
.Include(cat => cat.InfoItems)
.Include(cat => cat.Products)
.ThenInclude(prd => prd.GraphicItems)
.ThenInclude(itm => itm.Graphic)
.ThenInclude(gfx => gfx.Items)
.SingleAsync(cat => cat.Id.Equals(id));
Product.cs:
[Table("ShopProduct")]
public class Product : BaseShop
{
public bool Active { get; set; } = true;
public int CategoryId { get; set; }
public int CultureId { get; set; } = -1;
public List<ProductInfo> InfoItems { get; set; } = new List<ProductInfo>();
public List<ProductGraphic> GraphicItems { get; set; } = new List<ProductGraphic>();
}
ProductInfo.cs:
[Table("ShopProductInfo")]
public class ProductInfo : BaseShop, IInfoItem
{
public int? ProductId { get; set; }
public int CultureId { get; set; }
public Culture Culture { get; set; }
public string Name { get; set; }
public string Description { get; set; }
}
Solution:
var result = await this.Context.ShopCategorys
.Include(cat => cat.InfoItems)
.Include(cat => cat.Products)
.ThenInclude(prd => prd.InfoItems)
.Include(cat => cat.Products)
.ThenInclude(prd => prd.GraphicItems)
.ThenInclude(itm => itm.Graphic)
.ThenInclude(gfx => gfx.Items)
.SingleAsync(cat => cat.Id.Equals(id));

Pick the first item in a Linq to Entities query

I have the following code:
context.Posts
.SelectMany(x => x.Packs
.SelectMany(y => y.Files, (y, z) => new {
File = new { Key = z.Key }
})
.Select(y => new PostModel {
Id = x.Id,
File = y.File.Key,
Types = x.Types
})
).ToList();
This is working but one Post has Many PostLocalized.
I would like to, in my query, pick the PostLocalized which .Culture == culture.
And I need to use its data to create the PostModel. something like:
context.Posts
// PICK the first PostLocalized which .Culture property equals culture
.SelectMany(x => x.Packs
.SelectMany(y => y.Files, (y, z) => new {
File = new { Key = z.Key }
})
.Select(y => new PostModel {
Id = x.Id,
File = y.File.Key,
Types = x.Types,
//Title = PostLocalized.Title,
//Body = PostLocalized.Body
})
).ToList();
How can I do this?
NOTE:
The Post and PostLocalized entities are the following:
public class Post {
public Int32 Id { get; set; }
public Boolean Active { get; set; }
public PostTypes Types { get; set; }
public virtual ICollection<PostLocalized> PostsLocalized { get; set; }
} // Post
public class PostLocalized {
public Int32 Id { get; set; }
public String Culture { get; set; }
public String Body { get; set; }
public String Title { get; set; }
public virtual Post Post { get; set; }
public virtual ICollection<Pack> Packs { get; set; }
} // PostLocalized
public class Pack {
public Int32 Id { get; set; }
public Boolean Active { get; set; }
public DataType Type { get; set; }
public DateTime Updated { get; set; }
public virtual ICollection<File> Files { get; set; }
public virtual ICollection<PostLocalized> PostsLocalized { get; set; }
} // Pack
public class File {
public Int32 Id { get; set; }
public Byte[] Data { get; set; }
public Guid Key { get; set; }
public String Mime { get; set; }
public virtual Pack Pack { get; set; }
} // File
Thank You,
Miguel
This is not exactly beautiful or efficient on its own but it should at least work and the query optimizer will hopefully make it fast.
context.Posts
.SelectMany(post => post.Packs
.SelectMany(pack => pack.Files
.Select(file => new PostModel
{
Id = post.Id,
File = file.Key,
Types = post.Types,
Title = post.PostsLocalized.First(pl => pl.Culture == culture).Title,
Body = post.PostsLocalized.First(pl => pl.Culture == culture).Body
})))
.ToList();

Retrieving data from a second table if it has a record with the Id from another table

I have 2 context tables and I would like to select data from them if the ids are equal.
I would like to do this using LINQ's fluent API.
I want to check if the Id in Movie equals the MovieId in MovieReview, and if so present the ReviewerName from MovieReview, and Name from Movie
The tables are:
public class MovieReview
{
public int Id { get; set; }
[Range(1,10)]
[Required]
public int Rating { get; set; }
[Required]
[StringLength(1024)]
public string Body { get; set; }
[Display(Name="User Name")]
[DisplayFormat(NullDisplayText="anonymous")]
public string ReviewerName { get; set; }
public int MovieId { get; set; }
public class Movie
{
public int Id { get; set; }
public string Name { get; set; }
public string Director { get; set; }
public DateTime ReleaseDate { get; set; }
public string Genre { get; set; }
public virtual ICollection<MovieReview> Reviews { get; set; }
}
Any help is highly appreciated.
So far I made.. not even sure if im close:
var model =
_db.Movies
.Join(_db.Reviews, g => g.Id, u => u.MovieId, (g,u) => new { MovieReview = u, Movie = g})
.Where( (g => g.Movie
select (r => new AdminReviewListViewModels
{
Id = g.Id,
Name = g.Name,
Director = g.Director,
ReleaseDate = g.ReleaseDate,
Genre = g.Genre,
CountOfReviews = g.Reviews.Count(),
UserName = u.ReviewerName
});
If you just need an anonymous enumerable that contains MovieName and ReviewerName properties, this should work.
using (var _db = new YourDbContext())
{
var movies = _db.Movies;
var movieReviews = _db.MovieReviews;
var results = movies.Join(movieReviews,
m => m.Id,
mr => mr.MovieId,
(m, mr) => new { MovieName = m.Name, ReviewerName = mr.ReviewerName }).ToList();
}
You can use:
context.Set<MovieReview>().Select(c=>new{c.ReviewerName, c.Movie.Name})

Categories

Resources