Getting all related objects via related table. Entity Framework - asp.net-mvc

I have two tables. Parent table and link table that stores ids. Parent table have related data from itself. Here is Models:
public class People
{
public int Id { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public List<Relation> Relations { get; set; }
}
public class Relation
{
public int Id { get; set; }
public int ParentPeopleID { get; set; }
public int ChildPeopleID { get; set; }
public People People { get; set; }
}
Some test data
And mapped them like this
HasRequired(p => p.People).WithMany(p => p.Relations).HasForeignKey(p => p.ParentPeopleID);
When I'm calling
var Peoplelist = MyDbContext.People.Include(p=>p.Relations.Select(r=>r.People)).Where(p=>p.Id==1).ToList();
It returns itself not related People. In my case it should returns People with ids: 2,3,4 but returns three People with id 1
I can get what I need by MyDbContext.Relation but need by MyDbContext.People
What i'm doing wrong?
Is there another way to do that?

Ivan is correct. You should use join keyword to join the both tables as below. You will get the desired result (People with ids: 2,3,4) by using this query:
var Peoplelist = MyDbContext.Peoples.Join(MyDbContext.Relations, p => p.Id, r => r.ChildPeopleID,
(p, r) => new {p, r})
.Where(j => j.r.ParentPeopleID == 1).ToList();
Hope this will help.

Related

EF Core .ThenInclude as left join

New to EF Core...
I have the following EF Core class setup, but I am running into a problem loading the data into a view. When all data is populated for each table everything loads as is, however if for example I have an order and order line that has no orders received only the order loads. The orderline will not load.
Classes:
public class Order {
public Guid OrderID { get; set;}
public ICollection<OrderLine> OrderLines { get; set; }
}
public class OrderLine {
public Guid OrderLineId { get; set; }
public int OrderQty { get; set; }
public Order Order { get; set; }
public Guid OrderID { get; set; }
public ICollection<OrderReceived> OrdersReceived { get; set; }
}
public class OrderReceived {
public Guid OrderReceivedID { get; set; }
public int QuantityReceived { get; set; }
public OrderLine OrderLine { get; set; }
public Guid OrderLineId { get; set; }
}
My controller looks like this:
public async Task<IActionResult> Details(Guid? id) {
if (id == null) {
return NotFound();
}
var orderDetail = await _context.Orders
.Include(ol => ol.OrderLines)
.ThenInclude(rec => rec.OrdersReceived)
.SingleOrDefaultAsync(o => o.OrderId = id);
}
Ex: Order A has 1 order line and 2 OrdersReceived. This loads perfectly.
Ex: Order B has 1 order line and no orders received. Only the Order detail loads everything below that does not load (orderline or ordersreceived). I guess I'm looking for something more like a left join for the ordersreceived. I'm just not that familiar with EF Core.
Hopefully I explained this correcly. Any help is greatly appreciated. I did find this post with a similar question: ef core linq filtered child entities. Is best practice to implement a viewmodel for this type of situation?
Thank you,
E

Linq filtering entity's collection ends up in LINQ to Entities casting error

ASPNET MVC5 web app
In getting Products, whose relation with other entities is described by following models, I need to filter only those Products.Category.CategoryTrans whose language_id equals the culture param.
Please note I need to pass the result as an IQueryable to subsequent paging and sorting methods, already implemented.
Models:
public partial class Product
{
public int? ID { get; set; }
public string Name { get; set; }
public string Description { get; set; }
public bool IsDeleted { get; set; }
public bool IsApproved { get; set; }
public int CategoryID { get; set; }
public virtual Category Category { get; set; }
[NotMapped]
public virtual CategoryTrans CategoryTrans { get; set; }
}
public partial class Category
{
public int ID { get; set; }
public string Name { get; set; }
public int? ParentID { get; set; }
public bool IsDeleted { get; set; }
public virtual ICollection<Product> Products { get; set; }
public virtual ICollection<CategoryTrans> CategoryTrans { get; set; }
}
public class ISO_Languages
{
public int ID { get; set; }
public string code { get; set; }
public bool IsEnabled { get; set; }
public string name_en { get; set; }
public string name_fr { get; set; }
public string name_it { get; set; }
public string name_de { get; set; }
public string name_es { get; set; }
}
public class CategoryTrans
{
[Key, Column(Order = 1)]
public int category_id { get; set; }
[Key, Column(Order = 2)]
public int language_id { get; set; }
[ForeignKey("category_id")]
public virtual Category categoryId { get; set; }
[ForeignKey("language_id")]
public virtual ISO_Languages languageId { get; set; }
public string name { get; set; }
}
Following query returns ALL CategoryTrans in p.Category.CategoryTrans, that means any category translation
public static IQueryable<Product> ActiveProductsPerUser(BaseContext db, string userid, string culture)
{
var query = (from p in db.Products
join ct in db.CategoryTrans
on p.CategoryID equals ct.category_id
join l in db.ISO_Languages
on ct.language_id equals l.ID
where l.code.Substring(0, 2) == culture
select p);
return query;
}
What I am trying to do is filtering, for every product, the single category translation, depending on culture input parameter.
Something like:
public static IQueryable<Product> ActiveProductsPerUser(BaseContext db, string userid, string culture)
{
var query = from p in db.Products
join ct in db.CategoryTrans
on p.CategoryID equals ct.category_id
join l in db.ISO_Languages
on ct.language_id equals l.ID
where l.code.Substring(0, 2) == culture
select new Product
{
ID = p.ID,
Name = p.Name,
Description = p.Description,
CategoryTrans = p.Category.CategoryTrans.Where(b => b.language_id.Equals(l.ID)).SingleOrDefault()
};
return query;
}
but getting error:
The entity or complex type 'xyz.DAL.Product' cannot be constructed in a LINQ to Entities query.
Looking for this specific error I now tried projecting onto a DTO:
public class ProductDTO
{
public int? ID { get; set; }
public string Name { get; set; }
public string Description { get; set; }
public virtual CategoryTrans CategoryTrans { get; set; }
}
public static IQueryable<Product> ActiveProductsPerUser(BaseContext db, string userid, string culture)
{
var query = from p in db.Products
join ct in db.CategoryTrans
on p.CategoryID equals ct.category_id
join l in db.ISO_Languages
on ct.language_id equals l.ID
where l.code.Substring(0, 2) == culture
select new ProductDTO
{
ID = p.ID,
Name = p.Name,
Description = p.Description,
CategoryTrans = p.Category.CategoryTrans.Where(b => b.language_id.Equals(l.ID)).FirstOrDefault()
};
return query.Cast<Product>();
}
And that is now working as expected, returning only required translation to CategoryTrans.
So the query works now BUT casting ProductDTO to needed Product returns:
Unable to cast the type 'xyz.Controllers.ProductDTO' to type 'xyz.Models.Product'. LINQ to Entities only supports casting EDM primitive or enumeration types.
I cannot find a solution to this exception.
Even if you are able to cast ProductDTO to Product model, these objects will not be tracked automatically by EF.
One possible solution may be to first select the Products, then iterate over them to assign the required property.
In your first (failed) query, I believe teh select can be replaced with :
select new Product
{
ID = p.ID,
Name = p.Name,
Description = p.Description,
CategoryTrans = ct
};
which may work.
Otherwise, you cannot Cast<> from one object type to another unless they have a base/derived relationship. Otherwise, you'll need to convert them:
in Product DTO, add:
replace :
return query.Cast<Product>();
with:
return query.Select(p=> new Product
{
ID = p.ID,
Name = p.Name,
Description = p.Description,
CategoryTrans = p.CategoryTrans
};
UPDATE:
Ok, let's try something different. Drop the select from the query, and just use the p object:
public static IQueryable<Product> ActiveProductsPerUser(BaseContext db,
string userid, string culture)
{
var query = from p in db.Products
join ct in db.CategoryTrans
on p.CategoryID equals ct.category_id
join l in db.ISO_Languages
on ct.language_id equals l.ID
where l.code.Substring(0, 2) == culture;
return query;
}
that will leave the CategoryTrans property null, but it will get you through this and the other manipulations of the query. When you get to the end, and are actually doing things with the result, then pull out the p.Category.CategoryTrans.Where(b => b.language_id.Equals(l.ID)).FirstOrDefault() to get the CategoryTrans
I could not find an answer to the
The entity or complex type 'xyz.DAL.Product' cannot be constructed in a LINQ to Entities query.
error. I eventually solved the problem by adding a
[NotMapped]
public virtual string LocalizedCategoryName { get; set; }
to Product model Class, in charge for displaying localized category name, and moving the filtering to the ViewModel, setting two nested foreach loops, returning a new List of fully localized products:
private List<Product> _LocalizedProductList = new List<Product>();
public List<Product> LocalizedProductList
{
get
{
HttpUtilities HttpHelper = new HttpUtilities();
string culture = HttpHelper.getFullCulture();
int IsoCode = GenericUtilities.getIsoID(culture, db);
List<Product> localized = new List<Product>();
foreach (Product p in _LocalizedProductList)
{
foreach (CategoryTrans c in p.Category.CategoryTrans)
{
if (c.language_id.Equals(IsoCode))
{
Product x = new Product
{
ID = p.ID,
LocalizedCategoryName = c.name,
DateCreated = p.DateCreated,
DateExpire = p.DateExpire,
DateLastModified = p.DateLastModified,
Name = p.Name,
Description = p.Description,
IsApproved = p.IsApproved,
IsDeleted = p.IsDeleted,
ProductImages = p.ProductImages,
User = p.User
};
localized.Add(x);
};
}
}
return localized;
}
set { _LocalizedProductList = value; }
}
No idea if it's the best or the only way to do it, but works as intended. I can now stay with the simple query and pass the returned IQueryable of Product onto the sorting and paging async method or whatever. Whenever I am done, the result is being assigned to ViewModel.LocalizedProductList, where the getter takes care of the final filtering. Thanks to wonderbell for the suggestion that one possible solution could be to first select the Products, then iterate over them to assign the required property. That's why upvoted his/her post even it cannot be considered as a complete (or even partial) solution to the question.

Multiple tables update MVC .net

I am new to MVC and this is my function. There are three tables (Order, OrderNotes, Notes), ID is their primary key. One Order can have many Notes, the table OrderNotes has foreign key OrderID(from Booking table) and NotesID(from Notes table). I want to have a Order Edit page to display individual Order (FirstName, LastName), also display a list of its Notes. Here is my DB structure:
Booking table:
{ID,
FirstName,
LastName
}
BookingNotes table:
{ID,
BookingID,
NotesID
}
Notes table:
{ID,
NoteName,
StatusID
}
So how can I implement the list of Notes since it's from multiple tables? It will be able to Create New Note, Delete existing Note in the list row record, not Edit. Linq used in DB query. Thanks.
It would be a better idea to have only 2 tables:
public class Book
{
public int ID { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
// Navigational properties
public virtual List<Note> Notes { get; set; }
}
public class Note
{
public int ID { get; set; }
public int BookID { get; set; }
public string NoteName { get; set; }
public int StatusID { get; set; }
// Navigational properties
public virtual Book Book { get; set; }
public virtual Status Status { get; set; }
}
A third table is useful when you want to reuse the same Note for a different booking. However i think this is not the case.
So to retrieve data for your context make sure you have the DbSet<Book>
public class ApplicationDbContext : DbContext
{
public virtual DbSet<Book> Bookings { get; set; }
}
In your controller (or better in a repository class):
var BookingID = 10; // this is parameter passed to the function
var myBooking = this.dbContext.Bookings
.Include(p => p.Notes)
.ThenInclude(p => p.Status)
.FirstOrDefault(p => p.ID == BookingID);
Map the retrieved booking to a ViewModel, pass it to the View and you're good to go.

How to use EntityFramework to map a many to many relationShip in asp.net MVC?

I read a post from EF with MVC.The example used three tables with 1-to-many relationships. And with StudendId as the foreign key set up, I can directly call Model.Enrollments(model is of Student type ) in the view.
I'd like to know How could I set up a many-to-many relationship with two tables.
With a beginning:
public class Post
{
public int Id
{ get; set; }
public int CategoryId
{ get; set; }
public string Title
{ get; set; }
public string ShortDescription
{ get; set; }
public string Description
{ get; set; }
public virtual ICollection<Tag> Tags
{ get; set; }
}
public class Tag
{
[DatabaseGenerated(DatabaseGeneratedOption.None)]
public int TagId
{ get; set; }
public string Name
{ get; set; }
public string UrlSlug
{ get; set; }
public string Description
{ get; set; }
public virtual ICollection<Post> Posts
{ get; set; }
}
I'd like to call Model.Tags to get all the Tags related to a Post or call Model.Posts to get all the Posts belong to a Tag in the view. I guess I need a class something like
public class TagPost
{
public int TagId{get;set;}
public int PostId{get;set;}
}
But It looks like TagId and PostId both are foreign keys? I am not sure what to do.
Reading:
var post1 = dbContext.Post.FirstOrDefault(x => x.PostId ==1);
var tags = post1.Tags();
Inserting:
// Create a New Post Entity
var post = new Post();
// Assign it you values either from ViewModel or
post.Title ="ManyToMany";
// Create your list of tags related to Post
post.Tags = new List<Tags>();
// Add a Tag from Database to this List where TagId == 1
post.Tags.Add(dbContext.Tags.First(s => s.TagId == 1));
// save Changes
dbContext.SaveChanges();
after save Changes you will see that in you Many-to-Many Mappings table there is a new Record with
PostId = 2 // Considering you have PostId == 1 already in DB
TagId = 1 // Tag you added before dbContext.SaveChanges()

Select multiple table with Linq to Sql

I have two tables. There is one-to-many relationship between these tables.I want to select Company table and BankAccount List table (for appropriate CompanyID).
How can I do it with Linq-to-Sql?
public class Company
{
// My Fields
[Key]
public Guid ID { get; set; }
public string FullName { get; set; }
// My virtual properties FOR relationships(one-to-one,one-to-many etc.).
public virtual List<BankAccount> BankAccounts { get; set; }
}
and
public class BankAccount
{
// My Fields
//[ScaffoldColumn(false)]
[Key]
public Guid ID { get; set; }
[ForeignKey("Companies")]
public Nullable<Guid> CompanyID { get; set; }
public string BankName { get; set; }
// My virtual properties FOR relationships(one-to-one,one-to-many etc.).
public virtual Company Company { get; set; }
}
I write this as follow, but I didn't like it
List<List<BankAccount>> bankaccounts = new List<List<BankAccount>>();
foreach (var comp in companyRepository.Companies)
{
List<BankAccount> banks = new List<BankAccount>();
foreach (var bank in bankRepository.BankAccounts)
{
if (comp.ID == bank.CompanyID)
{
banks.Add(bank);
}
}
bankaccounts.Add(banks);
banks = new List<BankAccount>();
}
I think the following will yield the same result.
var bankaccounts = companyRepository.Companies.Select(c => c.BankAccounts)
.ToList();
If you are using entity framework, you can eagerload the 'BankAccounts' property to minimize DB calls.
Hope this helps.

Resources