How to select a collection nested within another collection in LINQ - linq

Say I have the following:
(--> = 1 to many implemented as a collection in EF code-first)
Message --> UserMessage
Message --> Attachments
When I call the following:
var res = _ctx.DataContext.UserMessage.Where(x => x.UserId)
.Select(m => m.Message).ToList();
EDIT: added classes:
public class Message
{
public int MessageId { get; set; }
public ICollection<Attachment> Attachments { get; set; }
[Required]
public string Text { get; set; }
}
public class Attachment
{
public int AttachmentId { get; set; }
public int MessageId { get; set; }
public virtual Message Message { get; set; }
public string FileServerPath { get; set; }
}
public class UserMessage
{
public int UserMessageId { get; set; }
[Required]
public int MessageId { get; set; }
public Message Message { get; set; }
[Required]
public int UserId { get; set; }
public User User { get; set; }
}
I would expect the res variable to hold all the attachments but it is empty even if there are rows. What am I missing?

Your where condition doesn't make sense, I don't even think it compiles.
You say, you expect res to hold all attachments. Why should it? You don't even use Attachment anywhere in your query.
Without your actual classes, it is a bit hard to suggest the correct way, but I think it would be something like this:
var res = _ctx.DataContext.UserMessage.Where(x => x.UserId == currentUserId)
.SelectMany(m => m.Message.Attachments)
.ToList();
Now, res contains all attachments of all messages of the user with the id currentUserId.
I assumed a class layout like this:
class UserMessage
{
public int UserId {get;set;}
public Message Message {get;set;}
}
class Message
{
public IEnumerable<Attachment> Attachments {get;set;}
// Irrelevant for the query in its current form:
public IEnumerable<UserMessage> UserMessages {get;set;}
}

On the context it needs to be told to acquire the navigation properties with an include such as
_ctx.UserMessage.Include("Attachments")
.SelectMany( ... )
HTH

Related

how to use projection in the include extension method in ef core?

I want to able to select certain entity properties (columns from db) in the include statement of queryable object. My query looks like below but I m getting error Lambda expression used inside Include is not valid
var samuraiWithQuotesQueryable = _context.Samurais.AsQueryable()
.Include(s => s.Quotes.Select(x => new { x.Text }));
// additional filters followed by getting the list
var samuraiList = samuraiWithQuotesQueryable.ToList();
Samurai and Quote entities look like below
public class Samurai
{
public Samurai()
{
Quotes = new List<Quote>();
}
public int Id { get; set; }
public string Name { get; set; }
public List<Quote> Quotes { get; set; }
}
public class Quote
{
public int Id { get; set; }
public string Text { get; set; }
public Samurai Samurai { get; set; }
public int SamuraiId { get; set; }
}
Wondering if this is possible with the IQueryable object?

Trouble mapping to DTOs with LINQ

i've been working with DTOs lately and am unable to determine the issue that this code is having.
I'm mapping Genre names and Movie names to a GenreMovieDto. Visual Studio doesn't show any errors (red lines etc) but when the code is run I get the following:
$exception {"The specified type member 'Movies' is not supported in LINQ to Entities. Only initializers, entity members, and entity navigation properties are supported."} System.NotSupportedException
My code is the following:
public class Genre
{
public int Id { get; set; }
public string Name { get; set; }
public IEnumerable<Movie> Movies { get; set; }
}
public class Movie
{
public int Id { get; set; }
public string Name { get; set; }
public string AgeRating { get; set; }
public int NumberInStock { get; set; }
public Genre Genre { get; set; }
}
public class GenreMovieDto
{
public string GenreName { get; set; }
public IEnumerable<Movie> Movies { get; set; }
}
And my API call:
public IEnumerable<GenreMovieDto> GetGenresWithMovies()
{
var genresWithMovies = _context.Genres
.Include(m => m.Movies)
.Select(x => new GenreMovieDto
{
GenreName = x.Name,
Movies = x.Movies <<<<< CRASHES HERE
})
.ToList();
return genresWithMovies;
}
Any thoughts ? Any and all suggestions / criticism is welcome :P I'm here to learn.
Thanks in advance
You can do like this (EF doesn't support IEnumerable<...> type member):
public class Genre
{
public int Id { get; set; }
public string Name { get; set; }
public virtual List<Movie> Movies { get; set; }
}

AJAX Posting Slickgrid data to MVC

I have a slickgrid and am attempting to save its data back to the server.
When I breakpoint on the server, I can see the data in the Request.Form object, but I can't make it work with my object.
My data looks like...
[
{"id":"0","LineNumber":"","Detail":"MOT cost","Code":" ","Qty":"1","Est":" ","CustomerDamage":false,"Cost":"44.00","Value":"44.00","VAT":true,"SelfBillingLine":"False","DefectStatus":" "},
{"id":"62","LineNumber":"","Detail":"CRACKS IN Chassis","Code":"TLMA02","Qty":"1","Est":"","CustomerDamage":false,"Cost":"35.00","Value":"35.00","VAT":true,"SelfBillingLine":"False","DefectStatus":"Large repair"},
{"id":"63","LineNumber":"","Detail":"TEAR IN N/S CURTAIN","Code":"TLMA02","Qty":"1","Est":"","CustomerDamage":true,"Cost":"10.00","Value":"10.00","VAT":true,"SelfBillingLine":"False","DefectStatus":"Customer"}
]
I am posting with a button onclick...
$("#SBSave").click(function() {
debugger;
var details = JSON.stringify(defectrows);
save('SBDetail/SaveSBItem', details);
});
I have tried a number of things to receive the data, none of them work.
My controller...
[HttpPost]
public void SaveSBItem(SelfBillDetailList details, string Approve = "")
{
// Actions here.
}
My model...
Trying a number of things, neither work...
public class SelfBillDetailList
{
public IEnumerable<SelfBillingIncomingDetail> IncomingDetails { get; set; }
}
public class SelfBillingIncomingDetail
{
public int id { get; set; }
public string Code { get; set; }
public string LineNumber { get; set; }
public string Detail { get; set; }
public string Action { get; set; }
public string Qty { get; set; }
public string Est { get; set; }
public bool VAT { get; set; }
public bool CustomerDamage { get; set; }
public string Cost { get; set; }
public string Value { get; set; }
public DateTime Received { get; set; }
public string DefectStatus { get; set; }
public bool SelfBillingLine { get; set; }
}
So, I have tried an individual SelfBillingIncomingDetail and also a the SelfBillDetailList.
Neither work.
I have even sent an individual row, again, neither work.
I want to send it as a group, so it will be an array of SelfBillingIncomingDetail but nothing works.
Thank you for your help.
I have done it again... eventually found an answer after looking for ages.
Darin Dimitrov's answer in
Post an Array of Objects via JSON to ASP.Net MVC3
let me to the answer.
It seems that when sending the data, I need to give the array of data the same name as the property name in SelfBillDetailList, so...
var details = JSON.stringify({IncomingDetails : defectrows});
fixes it.

How do I write the Linq query to get complex model items?

I'm using EF Code First. Now, I'm having a hard time figuring out how to write the LINQ to retrieve the data into my models in my Controller, to display them in a view. Basically, I am receiving a feed of HolterTest data, and I am trying to create a worklist for the people who do a bunch of specific tasks to process the HolterTest, allowing them to flag the tasks as they are completed, and provide status of where the individual tests are in the process The basic Task class is so they can add or alter steps in the process, with the displayOrder being the order in which tasks are done. A WorkTask is a specific instance of a task, allowing us to mark who completed it, and when. A WorkItem is the complex type that includes the HolterTest, the list of WorkTasks, and status information, including when the WorkTasks were all completed.
Model Classes:
public class HolterTest
{
public Int32 HolterTestID { get; set; }
public string PatientNumber { get; set; }
public string LastName { get; set; }
public DateTime RecordingStartDateTime { get; set; }
public System.Nullable<DateTime> AppointmentDateTime { get; set; }
}
public class Task
{
public Int32 TaskID { get; set; }
public string Name { get; set; }
public string Description { get; set; }
public Int32 DisplayOrder { get; set; }
public bool IsActive { get; set; }
}
public class WorkTask
{
public Int32 WorkTaskID { get; set; }
public Task Task { get; set; }
public bool IsCompleted { get; set; }
public System.Nullable<DateTime> CompletedDateTime { get; set; }
public string CompletedBy { get; set; }
}
public class WorkItem
{
public Int32 WorkItemID { get; set; }
public HolterTest HolterTest { get; set; }
public string Status { get; set; }
public List<WorkTask> WorkTasks { get; set; }
public bool IsStarted { get; set; }
public bool IsCompleted { get; set; }
public System.Nullable<DateTime> CompletedDateTime { get; set; }
}
Currently I have a business logic function that takes the list of HolterTests, finds the ones that don't have a WorkItem, and creates the WorkItems, associates the HolterTests including the WorkTasks, based on the current active list of Tasks.
My problem is how to write the LINQ to get all of the WorkItems (with their child items) for my WorkItemController so I can display the work to do in a View (WorkItems where IsCompleted = false) by PatientNumber, and make it possible to update WorkTasks for a particular WorkItem.
You want to access the related using it's navigation properties. Note that in your example, you haven't setup the navigation properties to be virtual. You should should update your model like this:
public class WorkItem
{
public Int32 WorkItemID { get; set; }
public virtual HolterTest HolterTest { get; set; }
public string Status { get; set; }
public virtual List<WorkTask> WorkTasks { get; set; }
public bool IsStarted { get; set; }
public bool IsCompleted { get; set; }
public System.Nullable<DateTime> CompletedDateTime { get; set; }
}
A simple function for accessing the work items by patient number is this:
IEnumerable<WorkItem> GetWorkIncompleteWorkItemsByPatient(string patientNumber)
{
var db = new YourContext();
return db.WorkItems.Where(wi => wi.IsCompleted == false && wi.HolterTest.PatientNumber == patientNumber);
}
Then to work on the related tasks, you would access it through the task, in this example if you knew the task ID:
var workTask = YourWorkItem.WorkTasks.FirstOrDefault(wt => wt.WorkTaskID == worktaskId);
You could look through all the tasks in the work item like this:
foreach (var workTask in YourWorkItem.WorkTasks)
{
//your logic here...
}
Linq to entities explained
http://msdn.microsoft.com/en-us/library/bb399367.aspx
But starting here may suit better: The EF Main Site http://msdn.microsoft.com/en-us/data/ee712907
if you really want to dive straight intry this video and sample code. http://msdn.microsoft.com/en-us/data/jj193542
then see this http://msdn.microsoft.com/en-us/data/jj573936
Essentially based on the POCO you have you could read per POCO and get the data that way.
However EF does a lot of heavy lifting if the POCOS have navigational properties and foreign keys defined. Worth revisiting the POCO definitions and Code-First patterns.

Create with ViewModel that has two related entities

I have the properties for two entities in a ViewModel. The two entities are both related to one another, so for example, User and Posts. Each User can have multiple Posts, and Many Posts can belong to a single user (one-to-many).
The aim from my ViewModel is to allow the addition of a User and a Post on the same form. So my ViewModel looks something like this:
public class CreateVM
{
[Required, MaxLength(50)]
public string Username { get; set; }
[Required, MaxLength(500), MinLength(50)]
public string PostBody { get; set; }
// etc with some other related properties
}
In my Controller on the Create Method I have something like this:
[HttpPost]
public ActionResult Create(CreateVM vm)
{
if (ModelState.IsValid)
{
User u = new User()
{
Username = vm.Username,
// etc populate properties
};
Post p = new Post()
{
Body = vm.PostBody,
// etc populating properties
};
p.User = u; // Assigning the new user to the post.
XContext.Posts.Add(p);
XContext.SaveChanges();
}
}
It all looks fine when I walk through it through the Debugger, but when I try to view the post, its User relationship is null!
I also tried
u.Posts.Add(p);
UPDATE:
My Post class code is as follows:
public class Post
{
[Key]
public int Id { get; set; }
[Required, MaxLength(500)]
public string Body { get; set; }
public int Likes { get; set; }
[Required]
public bool isApproved { get; set; }
[Required]
public DateTime CreatedOn { get; set; }
[Required]
public User User { get; set; }
}
But that also did not work. What am I doing wrong?
Problem is that EF can not lazy load the User property because you haven't made it virtual.
public class Post
{
[Key]
public int Id { get; set; }
[Required, MaxLength(500)]
public string Body { get; set; }
public int Likes { get; set; }
[Required]
public bool isApproved { get; set; }
[Required]
public DateTime CreatedOn { get; set; }
[Required]
public virtual User User { get; set; }
}
If you know beforehand that you are going to access the User property of the post you should eager load the User related to the post.
context.Posts.Include("User").Where(/* condition*/);

Resources