Using navigation properties instead of LINQ Join in WebApi controller - c#

I am using Entity Framework Code First and LINQ to Entities in a Web Api Controller.
I have 3 tables that are related with the following relations/ navigation properties: (the model classes are as follows)
My Reservation class:
[Table("Reservations")]
public class Reservation
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int ReservationId { get; set; }
public DateTime Date { get; set; }
public int RoomId { get; set; }
public Room Room { get; set; }
public Customer Customer { get; set; }
public int CustomerId { get; set; }
// create association table ReservationMeals
public virtual ICollection<ReservationMealItems> ReservationMeals { get; set; }
}
Association class:
public class ReservationMealItems
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int Id { get; set; }
public int ReservationId { get; set; }
public int MealId { get; set; }
public virtual Reservation Reservation { get; set; }
public virtual Meal Meal { get; set; }
}
Meals class:
[Table("Meals")]
public class Meal
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int Id { get; set; }
public string Name { get; set; }
public string Description { get; set; }
public string ImageUrl { get; set; }
public int MealTypeId { get; set; }
public virtual MealType Type { get; set; }
public virtual ICollection<MenuMealItem> Menus { get; set; }
// create association table ReservationMeal
public virtual ICollection<ReservationMealItems> ReservationMeals { get; set; }
// one-to-one with MealPrice where MealPrice is the dependant
public virtual MealPrice MealPrice { get; set; }
}
And here is my controller to which I am passing a Date and then querying the tables in order to bring back all the meals from all the reservations made on that particular date.
public IHttpActionResult GetReservationMealsForDate(DateDto date)
{
using (var context = new CafeteriaContext())
{
var reservationMeals = from p in context.Reservations
where p.Date == date.Date
join d in context.ReservationMeals
on p.ReservationId equals d.ReservationId
select new SimpleId
{
Id = d.MealId
};
var meals = from p in context.Meals
join d in reservationMeals
on p.Id equals d.Id
select new SimpleMeal
{
Name = p.Name
};
return Ok(meals.ToList());
}
}
The controller works and brings back the list of meals from all reservations made on that date but I am certain that it's not optimal.
I tried not using the JOINs and instead make use of the navigation properties but with no success. I wonder if the relations are set up correctly and if the navigation properties are placed as they should be and if yes, then how can the controller be rewritten to bring back the same result set using navigation properties.
And as a bonus question I would like to know how to write the correct query using LINQ method syntax as I did not succeed in writing it as such.
I am currently learning and any explanations are greatly appreciated. I have read this blog post Coding Abel from where I managed to make it work. But I am still curious to know how correct my solution is and eager to learn better alternatives. Thanks!

You can try use eagerly loading with LINQ and method Include. Your table ReservationMealItems have relationships to Meal and Reservation in this case you need include your entities like :
Lambda
var x = context.ReservationMealItems.Include(res => res.Reservation)
.Where(dt => dt.Reservation.Date == DateTime.Now)
.ThenInclude(meal => meal.Meal)
.Select(d => d.Meal.Name)
.ToList();
Here you find more informations MSDN
Query Syntax
var z = (from resevationMealsItems in context.ReservationMealItems
join reservation in context.Reservation on resevationMealsItems.ReservationId equals
reservation.ReservationId
join meal in context.Meal on resevationMealsItems.MealId equals meal.Id
select meal.Name).ToList();

Related

How to use linq join between unrelated models? (one of them is IdentityUser)

I use asp.net core EF.
I have two models.
[1] the first one is employee which inherits IdentityUser and extending it.
[2] the second one is DocumentIn which has employee's key
note: they are not related
the problem:
I need to view all DocumentIn but with employee's names, not keys
next are my two models respectively (with comments included to demonstrate Primary and Foreign keys)
Employee inheriting from IdentityUser
public class Employee: IdentityUser
{
// this primary key (Id) is identified as a foreign key twice in the next model (Document In)
// but with different names
// the names are [EmpFrom] and [EmpTo]
[Key]
public override string Id { get => base.Id; set => base.Id = value; }
//-------------------------------------------------------------------------------------------
[EmailAddress]
public override string Email { get => base.Email; set => base.Email = value; }
public string HomePhone { get; set; }
public string EmpName { get; set; }
public int? DivID { get; set; }
[ForeignKey("DivID")]
public Division division { get; set; }
public string Password { get; set; }
[NotMapped]
public string ConfirmPassword { get; set; }
}
DocumentIn
public class DocumentIn
{
[Key]
public int DocInID { get; set; }
public string DocName { get; set; }
public string Notes { get; set; }
public string BarCode { get; set; }
public DateTime ActionDate { get; set; }
public DateTime DueDate { get; set; }
// here I use these names to contains employees Id which is GUID of IdentityUser
public string EmpFrom { get; set; }
public string EmpTo { get; set; }
}
Important:
I use EF concept, DbContext to get DocumentIn
however, I use UserManager on the other hand to get employees
like the following
gitting DocumentIn using _context
// _TableView is a ViewModel which has this attripute
// public List<DocumentIn> DocumentsInList { get; set; }
_TableView.DocumentsInList = await _context.DocumentIn.Where(bla bla bla).ToListAsync();
gitting employees using UserManager
List<Employee> Emps = await userManager.Users.ToListAsync();
Now, I need to include Emp with DocumentsInList's query to read all documents with the names of employees, not with their Id.
In other words, I need to view EmpName from Employee rather than EmpFrom and EmpTo in the above LINQ query.
I already made the next LINQ query but I don't know how to replace EmpFrom and EmpTo from DocumentIn with EmpName from Employee
// -------- all users
List<Employee> Emps = await userManager.Users.ToListAsync();
_TableView.DocumentsInList = await _context.DocumentIn.Include(e => Emps).ToListAsync();
I already made the next LINQ query but I don't know how to replace
EmpFrom and EmpTo from DocumentIn with EmpName from Employee
According to your Model code, since the DecumentIn model doesn't contain the reference navigation property for the Employee, if you want to display the Employee when query the DecumentIn table, you have to use the Join clause.
And, you could create a view Model (such as: DocumentInViewModel) which contains a EmpName property, then in the LINQ select clause, you could create the DocumentInViewModel instance and display the Employee Name.
Code as below (Suppose the DocumentIn's EmpFrom value equals the Emplyee's ID value):
public class DocumentInViewModel
{
public int DocInID { get; set; }
public string DocName { get; set; }
public string Notes { get; set; }
public string BarCode { get; set; }
public DateTime ActionDate { get; set; }
public DateTime DueDate { get; set; }
// used to display the Employee Name.
public string EmpName { get; set; }
}
Then, you could try to use the following code to query related data.
List<Employee> Emps = await userManager.Users.ToListAsync();
var resultee = _context.DocumentIn.ToList()
.Join(Emps, d => d.EmpFrom, e => ((Employee)e).Id, (d, e)=> new { DocumentIn= d, Employee = e })
.Select(c=> new DocumentInViewModel { DocName = c.DocumentIn.DocName, EmpName = c.Employee.EmpName })
.ToList();
More detail information about the Linq Join statement, you could check this thread.
Why dont you create a view in the db and use that to show both models as per your requirement

Entity Framework Core filter related entities and get top 2 for each group

I am using Entity Framework Core 2.0.1 and I have the following models
public class Article
{
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
[Key]
public int Id { get; set; }
[Required]
public string Title { get; set; }
public string Slug { get; set; }
public int Approved { get; set; }
public DateTime ArticleDate { get; set; }
// ... some other fields
public virtual ICollection<ArticleCategoryRelation> ArticleCategoryRelations { get; set; }
}
public class ArticleCategory
{
[Key]
public int Id { get; set; }
public string Name { get; set; }
public string Description { get; set; }
//... soem other fields
[ForeignKey("ArticleCategoryParent")]
public int? ArticleCategoryParentID { get; set; }
public virtual ArticleCategory ArticleCategoryParent { get; set; }
public virtual ICollection<ArticleCategory> SubCategories { get; set; }
public virtual ICollection<ArticleCategoryRelation> ArticleCategoryRelations { get; set; }
}
public class ArticleCategoryRelation
{
[Column(Order = 0)]
public int ArticleId { get; set; }
public Article Article { get; set; }
[Column(Order = 1)]
public int ArticleCategoryId { get; set; }
public ArticleCategory ArticleCategory {get; set;}
}
Every article belongs to one or more categories. Categories might have parent category.
I want to get from database last two articles (where Approved = 1) with related category details, for each category that belongs to a parent category which id is given as input.
I have tried but with no success. I can't filter results of an .Include() entity. Is it possible... or I don't know how to do it?
All my data are accessed through entity framework with appContext (the context used to get entities from database). Can I achieve what I want through entity framework core (lambda expression is preferred over Linq if possible), or should I use ADO.NET library (which I know how to execute custom queries).
P.S. I want to get data only to show in the view... no edit is needed.
You don't actually need to include here at all, as far as I can tell. Whenever you use data from a nav property, EF will go get the data from that table, as best it can filter it.
var CategoriesUnderParent = AppContext.ArticleCategories
.Where(c => c.ArticleCategoryParent == {parent});
foreach(var category in CategoriesUnderParent)
{
var ArticlesAllowed = category.ArticleCategoryRelations
.Where(acr => acr.Article.Approved == 1).Select(a => a.Article);
var ArticlesPicked = ArticlesAllowed
.OrderByDescending(ar => ar.ArticleDate)
.Take(2);
// Do something with your data
}

Entity Framework Joining Three Tables and Using Group Concat

I have three models like followings,
public class Team
{
public int Id { get; set; }
public string Name { get; set; }
}
public class Document
{
public int Id { get; set; }
public string Name { get; set; }
public string Type { get; set; }
public string Application { get; set; }
public ICollection<DocumentResponsible> DocumentResponsibles { get; set; }
public string Pcda { get; set; }
public DateTime CreatedAt { get; set; }
public DateTime UpdatedAt { get; set; }
}
public class DocumentResponsible
{
public int Id { get; set; }
public int DocumentId { get; set; }
public int TeamId { get; set; }
}
I want to write an entity framework expression to join three table and select all fields of document table and team names in one row per documents. So basicly I want to join three table use group_concat for team names. Then I want to bind it to a gridview in web form.
What I have tried,
(from dc in DBContext.Document
join dr in DBContext.DocumentResponsible on dc.Id equals dr.DocumentId
join t in DBContext.Team on dr.TeamId equals t.Id
select new
{
Name = dc.Name,
Type = dc.Type,
Application = dc.Application,
Pcda = dc.Pcda,
}).ToList();
and I have just tried it,
var data = DBContext.Dcoument.Include("DocumentResponsibles").Tolist();
It's hard to help without your DbContext and the Entity Mappings, but I'll go out on a limb saying you might just want to mark Document.DocumentResponsibles as virtual.
Also, in DocumentResponsible, maybe you'd want to add a property for Document and one for Team (both marked as virtual too) this way you don't have to do the join keys all the time you want to work with your data, EF would do it for you once properly configured.
If it doesn't work, can you add the following information to your question: First, the context class and the mappings you have. Second, if you do var firstDocument = yoneylemDB.Document.First(), how does firstDocument looks like? Does it have all it's fields and properties filled out? Is there anything weird?

Find all parents in hierarchical SQL database

Using ASP.NET 4.5 and EF 6, I've put together a multilevel data model that looks like this:
Organization, which has an ICollection of:
_____Workspaces, which has an ICollection of:
__________Projects, which has an ICollection of:
_______________Cards
If I've got a Card Id, how do I find the parents above it? I can't even figure out how to get one level of parent. To find the Project that is the parent of a card with Id of myCardId, I want to write something like:
var project = db.Projects.Where(p => p.Cards.Where(c => c.Id == myCardId));
Any guidance? Good tutorials you can point me to?
Update: Here's my Project model:
public class Project
{
public int Id { get; set; }
public string Title { get; set; }
public string Description { get; set; }
public DateTime Created { get; set; }
public virtual ICollection<Card> Cards { get; set; }
}
And here's my Card:
public class Card
{
public int Id { get; set; }
public string Title { get; set; }
public DateTime Created { get; set; }
public string Notes { get; set; }
}
When I look at the tables in the database, the Card has a Project_Id column, but that happened magically from something Entity Framework did - I believe. Because the Card model has no Parent object on it, I can't programatically ask for things like Card.Project.Workspace.Organization...
Did I set up the whole thing backward?
Hope you have references for each tables.
so you can get the organization by.
var organization = (from o in Organization
from w in Workspaces
from p in Projects
from c in Cards
where c.Id == myCardId && p.projectid == c.projectid && w.workspaceid == p.workspaceid && o.organizationid == w.organizationid select o).FirstOrDefault();
if its different from your code then post your collection structure here, so that anyone can help.
I imagine that find the parents means go up in the Hierarchy so you should be able to write somethin like
'var project = db.Projects.Where(p => p.Cards.Where(c => c.Id == myCardId));'
var t= db.Cards.where(x=> x.id==myCardId).Select(y=> y.Project.Workspace.Organization);
//this should return the organization
Your Model should be like this:
public class Project
{
public int projectId { get; set; }
public int workspaceId { get; set; }
public string Title { get; set; }
public string Description { get; set; }
public DateTime Created { get; set; }
public virtual ICollection<Card> Cards { get; set; }
}
public class Card
{
public int cardId { get; set; }
public int projectId { get; set; }
public string Title { get; set; }
public DateTime Created { get; set; }
public string Notes { get; set; }
}
Your Card table should have reference to projectid, and project table should have reference to workspaceid, and workspace table should have reference to organisationid.
Then you can use karthikb2win's query to get Organization or any parent tables.

Check if single element is in a list in LINQ when the single is provided first

I'm using LINQ to SQL for a service in a project. I have three entities involved: PCode is a property of PurchaseOrder, as well as in an M:N with User.
PCode (M:N) User
PCode (1:M) PurchaseOrder
The service is for PurchaseOrder, so that table is being queried. Because of that, using .Where() requires p => p.PCode at the beginning to utilize the value. I know of .Contains() and the other similar commands, but they all require that the list come first and I can't do that with this type of query. I ultimately need to check whether the PCodeId in the specific PurchaseOrder is in the list of PCodeIds associated with the current user. How can I filter this way in LINQ to SQL? I feel like this is really simple, I just can't figure it out.
Simplified User class:
public partial class User
{
public virtual int Id { get; set; }
public virtual ICollection<PCode> PCodes { get; set; }
}
PCode:
public partial class PCode
{
public virtual int Id { get; set; }
public virtual ICollection<User> Users { get; set; }
}
PurchaseOrder:
public partial class PurchaseOrder
{
public virtual int Id { get; set; }
public virtual int PCodeId { get; set; }
}
Beginning of query:
// list to be searched can either be passed into the function
// or retrieved via navigation property when the query runs, either works
var query = _poRepository.Table;
if (condition)
// query = query.Where( ? );
// query = query.Where(p => p.PCode ... ? );
var CurrentUser=db.Users
.Include(u=>u.PCodes)
.Where(u=>u.id==something);
if (CurrentUser.PCodes.Any(pc=>...))
You are also missing navigation properties on Purchase Order, and PCode:
public partial class PurchaseOrder
{
public int Id { get; set; } // Should not be virtual
public int PCodeId { get; set; } // Should not be virtual
public virtual PCode PCode { get; set; } // new Navigation
}
public partial class PCode
{
public int Id { get; set; } // Should not be virtual
public virtual ICollection<User> Users { get; set; }
public virtual ICollection<PurchaseOrders> PurchaseOrders { get; set; } // new Navigation
}
Or the reverse:
var po=db.PurchaseOrders
.Include(po=>po.PCode)
.Include(po=>po.PCode.Users)
.First(po=>po.id==someid);
if (po.PCode.Users.Contains(currentUser)) ...
or
if (po.PCode.Users.Any(u=>u.id==someuserid)) ...
or if all you have is a userid and poid:
if (db.PurchaseOrders.Any(po=>po.id==poid && po.PCode.Users.Any(u=>u.id==userid))) ...

Categories

Resources