EF Code First and linq extension methods - foreign keys, am i doing it wrong? - linq

I have something like this: (pseudocode)
public class Author
{
int id;
public List<Thread> Threads;
public List<ThreadPoints> ThreadPointses;
}
public class Thread
{
int id;
public List<ThreadPoints> ThreadPointses;
}
public class ThreadPoints
{
int id;
int Points;
}
And i am not sure if above is correct, but now i would want to obtain number of points' that specified Author have in specified Thread.
I cannot directly call ThreadPoints.Thread_id, because it's not accessible, even if it physically is in the database.
So do i need to change my model, or am i unaware of some useful methods?
So basically, my model looks like that:
public class Account
{
[Key]
public Guid AccountId { get; set; }
public string UserName { get; set; }
public List<Post> Posts { get; set; }
public List<Post> ModifiedPosts { get; set; }
public List<Thread> Threads { get; set; }
public List<ThreadPoints> ThreadPointses { get; set; }
public List<Thread> LastReplied { get; set; }
public int Points { get; set; }
}
public class Thread
{
[Key]
public int Id { get; set; }
public List<ThreadPoints> ThreadPointses { get; set; }
public List<Post> Posts { get; set; }
public int CurrentValue { get; set; }
public int NumberOfPosts { get; set; }
public int TotalValue { get; set; }
public int Views { get; set; }
public string Title { get; set; }
public DateTime DateCreated { get; set; }
}
public class ThreadPoints
{
[Key]
public int Id { get; set; }
public int Points { get; set; }
}
And what i need, is, when user creates a thread, he gives some amount of points into it. In the next action, i want to take that amount of points (from database), and increase it. But i only have thread id as input information.
Your answer might be good, (as far i am trying to implement it), but anyways, i am not sure about this model. Maybe i should manually add foreign keys into my model? It surely would be simpler, but then i would have two foreign keys in my database...

Since you're not explicitly mapping your FK's, entity framework is generating them and hiding them away, so to get to the Id's of the properties, you'll need to follow the navigation collections.
I'm not sure about your question, but are you wanting the number of Points, inside of a specific Threadpoint for a given author? Your model doesn't seem to support this very well, but you could do something like this-
public int GetPoints(Author author, Thread thread)
{
int points = author.Threads.FirstOrDefault(t => t.id == thread.id).ThreadPointses.Sum(tp => tp.Points);
}
This would return the sum of all the points contained in the list of threadpoints, which are contained in the list of threads with the same id as the thread you passed in, for the specified author.
If this doesn't work for you - can you post your actual model?

Related

Can't Include navigation property (it is null) [duplicate]

This question already has answers here:
How to stop self-referencing loop in .Net Core Web API?
(4 answers)
Closed 4 years ago.
I am trying to get the students from the database and also include the course entities corresponding to them, but it seems that I'm doing something wrong.
this is student class:
public class Student
{
public int StudentID { get; set; }
public string Name { get; set; }
public int CourseID { get; set; }
public virtual Course Course { get; set; }
}
this is course class:
public class Course
{
public int CourseID { get; set; }
public string Name { get; set; }
public virtual ICollection<Student> Students { get; set; }
}
and I retrive the collection of student entities as shown below:
context.Students.Include(i => i.Course).ToList();
If I remove the include method, then I get data, but course property of student object is null.
P.S I am testing it with Postman and with "Include" I am not able to get anything.
If I comment this
public virtual ICollection<Student> Students { get; set; }
everything works fine.
I put the full code on github:
https://github.com/AlexDev5/Problem
You have to configure the serializer to ignore circular references in your project.
So for this you have to add following line of code inside ConfigureServices method in Startup.cs
Like
public void ConfigureServices(IServiceCollection services)
{
services.AddMvc()
.AddJsonOptions(options => {
options.SerializerSettings.ReferenceLoopHandling = ReferenceLoopHandling.Ignore;
});
}
Depending on the sample found here https://github.com/aspnet/EntityFramework.Docs/tree/master/samples/core/Querying/Querying
I wrote one solution, but I don't know if it is good approach for this problem:
I created StudentCourse class and defined it as shown below:
public class StudentCourse
{
public int StudentCourseID { get; set; }
public int StudentID { get; set; }
public Student Student{ get; set; }
public int CourseID { get; set; }
public Course Course { get; set; }
}
And I modified the Course class as shown below:
public class Course
{
public int CourseID { get; set; }
public string Name { get; set; }
public virtual ICollection<StudentCourse> Students { get; set; }
}

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.

Entity Framework/LINQ: Selecting columns from multiple tables?

Models:
public class User
{
[Key]
public int UserId { get; set; }
public string UserName { get; set; }
}
public class Resource
{
[Key]
public int ResourceId { get; set; }
public string ResourceName { get; set; }
public string ResourceDescription { get; set; }
}
public class UserResource
{
[Key, Column(Order=0)]
public int UserId { get; set; }
[Key, Column(Order=1)]
public int ResourceId { get; set; }
public int ResourceQuantity { get; set; }
}
I want to select "ResourceName" from Resource model and "ResourceQuantity" from UserResource model for a given "UserId". Also, once selected, do I need a brand new model to carry only those two specified columns?
Also note that UserResource model has a composite key so I am confused as to how to make the join... Is this right?
var userResources =
from r in imDB.Resources
join ur in imDB.UserResources
on r.ResourceId equals ur.ResourceId
select new { r.ResourceName, ur.ResourceQuantity };
Hence you're using Code first you can create your models are as below by using EF conventions.
public class User {
public int Id { get; set; }
public string UserName { get; set; }
public virtual ICollection<Resource> Resources { get; set; }
}
public class Resource {
public int Id { get; set; }
public string ResourceName { get; set; }
public int ResourceQuantity { get; set; }
public virtual ICollection<User> Users {get;set;}
}
Then EF will generate your junction table is as UsersResources.You don't need to create additional model as you did.EF will look after that.
When using POCOs with EF, if you mark your navigation properties as
virtual you can use additional EF supports like Lazy Loading. So in
general use a virtual keyword in navigation properties considered to
be a good practice.
UPDATE
You may try something like below:
Method 1 : Method based syntax
imDB.Resources.Where(r => r.Users.Any(u => u.UserId == userId))
Method 2 : Query based syntax
from r in imDB.Resources
from u in r.Users
where u.UserId == userId
select r;
I hope this will help to you.

Entity Framework 4.1 - Segregating an entity into multiple tables (Code-First)

I'm trying to find out how I can re-use a simple 'Comment' entity type for multiple scenarios where something is 'commentable' in my application.
At the moment, I have a couple of entities that a user is able to post comments to. Examples include Blogs, Profiles and Photos - these can all be 'commented' on.
I'd like to be able to use the same 'Comment' class for each of these scenarios, but I don't want to end up with one HUGE table full of comments for everything. I figure it would be much more efficient to at least store a table of BlogComments, PhotoComments, and ProfileComments. At the moment, my Comment class looks like this:
public class Comment
{
[Key]
public int Id { get; set; }
public int ContextId { get; set; }
[StringLength(256)]
public string Content { get; set; }
public DateTime DatePosted { get; set; }
public virtual Member Author { get; set; }
}
Presumably, I'd need the 'ContextId' field to refer to the particular thing being commented on. This Id might be the Id of a Blog, a Profile or a Photo. I was hoping to be able to refer to comments much like a normal ICollection in these classes, and I have some code like this for the Photos as an example:
public class Photo
{
[Key]
public int Id { get; set; }
[StringLength(48)]
public string FileName { get; set; }
public virtual Member Owner { get; set; }
public virtual ICollection<Comment> Comments { get; set; }
}
I've been pointed to various articles during my searches, but none really seem relevant to my particular situation. How can I map these comment collections to different tables, and avoid having a comment "super-table"?
Any help, pointers or advice would be hugely appreciated :)
You can create an abstract Comment class and inherit from it specific comments such as PhotoComment, ProfileComment. You will be able to map the comments to different tables.
public abstract class Comment
{
[Key]
public int Id { get; set; }
[StringLength(256)]
public string Content { get; set; }
public DateTime DatePosted { get; set; }
public virtual Member Author { get; set; }
}
public class PhotoComment : Comment
{
public int PhotoId { get; set; }
public virtual Photo Photo { get; set; }
}
public class Photo
{
[Key]
public int Id { get; set; }
[StringLength(48)]
public string FileName { get; set; }
public virtual Member Owner { get; set; }
public virtual ICollection<PhotoComment> Comments { get; set; }
}

asp.net mvc 3 entity framework, passing model info in Get request of create action

I'm having trouble passing view information from my Get/Create action to my view. Here are my three model classes;
public class Competition
{
public int Id { get; set; }
public int CompetitionId { get; set; }
public string Name { get; set; }
public string Prize { get; set; }
}
public class CompetitionEntry
{
public int Id { get; set; }
public int CompetitionEntryId { get; set; }
public string Name { get; set; }
public string Email { get; set; }
public int CompetitionId { get; set; }
}
public class CompetitionEntryViewModel
{
public int Id { get; set; }
public Competition Competitions { get; set; }
public int CompetitionId { get; set; }
public string Name { get; set; }
public string Email { get; set; }
}
Here is my Get/Create action in CompetitionEntry Controller;
public ActionResult Create(int id)
{
CompetitionEntryViewModel competitionentryviewmodel = db.CompetitionEntriesView.Find(id);
return View(competitionentryviewmodel);
}
I know this doesn't work. The id parameter goes into the URL fine. How to I get access to my Competition class in th Get action? I need to be able to show the competion name on my Create Competition entry view.
Thanks in advance!
public ActionResult Create(int id)
{
var data = db.CompetitionEntriesView.Find(id);
CompetitionEntryViewModel competitionentryviewmodel = new CompetitionEntryViewModel();
competitionentryviewmodel.CompetitionName = data.Name;
return View(competitionentryviewmodel);
}
What you are trying to do is build an object graph and display it through a view model. In order to do this, you need to map your domain model(s) to your view model.
You can do the mapping yourself by writing a lot of code (re-inventing the wheel), or, you could consider using third party tools to do this for you. I recommend you use an AutoMapper as it is very simple to use imo.
The other problem is that your view model contains a domain model. This is likely to cause you a lot of headache in near future. If I were you, I would replace Competition with CompetitionViewModel.
I would also consider creating a view model for a list of competitions, i.e. CompetitionsViewModel. Look into partial views to see how you can display a list of competitions.
Good luck

Resources