Linq joining into list property of object - c#

Hey I have two following classes:
public class Project
{
public int Id { get; protected set; }
public string ProjectName { get; protected set; }
public List<Task> TaskList{ get; protected set; }
}
public class Task
{
public int Id { get; protected set; }
public int ProjectId { get; protected set; }
public string Name { get; protected set; }
}
If I am using Entity Framework is it possible to get Project object from database (eg. by id) and in the same time join tasks list into [Project] TaskList property, or I should first get a project, and then tasks?
How it should be implemented properly?

If you described relationship between tables Project and Task as explained here
than you can directly just call your Project entity; it will come with a list of related Tasks; if you didn't described relationships than you have to create a join query by linq to etnity or use .Inlcude for relate entites to each other.
var tasks = (from items in db.Task
join projects in db.Project on items.ProejctId equals projects.Id
where 1==1 // you can add some other confitions to here
select new SelectItemsList() { //Items you want to select })

Do you require the Project class object like below code? without using .include<>
var res = (from p in db.ProjectTable
select new Project
{
Id = p.Id,
ProjectName = p.ProjectName,
TaskList = (from q in db.TaskTable
where q.ProjectId = p.Id
select q
).ToList()
}).ToList();
return res; //you will get List<Project> with their List<TaskList>

Related

how to show values of three table with foreign key associated in table using entity framework c#?

hello all i am new to entity and asp.net M.V.C i am using entity framework code first approach with already made database i want to show three tables data in a table using inner join and confused how to do it
i have tried the following code given below
public List<NewGroupRoles> GetAllGroups()
{
try
{
using(var p=new VMSDBContext())
{
var group = (from group_role in p.group_roles
join groups in p.groups on group_role.group_id equals groups.group_id
join roles in p.roles on group_role.roles_id equals roles.role_id
select new
{
Id = group_role.group_roles_id,
group_name = groups.group_name,
group_role = roles.role_name
}).ToList();
}
}
catch(Exception ex)
{
return new List<NewGroupRoles>();
}
}
i want to return it from a function in model
model classes areths class defines all the database entity classes
this a group table
[this is group role class][3]
role class
You are trying to do work, which EF is supposed to do for you.
It looks like you have many-to-many relationship between the groups and roles tables. Why wouldn't you remove the group_roles class and just define navigation properties
public virtual IEnumerable<roles> roles { get; set; }
in groups model class and
public virtual IEnumerable<groups> groups { get; set; }
in roles model class
and get list of groups and roles somehow like this:
var groupRoles = p.groups.SelectMany(g=>g.roles, (g,r)=>new {Group = g.group_name, Role = r.role_name}).ToList();
Or if your use EF Core, which yet does not support many-to-many relationships without intermediate model class or just insist on having an intermediate class group_roles in your model, you can define two navigation properties in it, something like this:
public class group_roles
{
[Key]
public int group_roles_id { get; set; }
[ForeignKey("groups")]
public int group_id { get; set; }
[ForeignKey("roles")]
public int role_id { get; set; }
public virtual groups group { get; set; }
public virtual roles role { get; set; }
}
Then your query is just
var groupRoles = p.group_roles.Select(gr=> new {gr.group_roles_id, Group=gr.group.group_name, Role=gr.role.role_name).ToList();
Use this as you need to have new object while selecting
public List<NewGroupRoles> GetAllGroups()
{
try
{
using(var p=new VMSDBContext())
{
var group = (from group_role in p.group_roles
join groups in p.groups on group_role.group_id equals groups.group_id
join roles in p.roles on group_role.roles_id equals roles.role_id
select new NewGroupRoles()
{
Id = group_role.group_roles_id,
group_name = groups.group_name,
group_role = roles.role_name
}).ToList();
}
}
catch(Exception ex)
{
return new List<NewGroupRoles>();
}
return group;
}

LINQ: Populating a LIST<> via multiple navigations

Ok, I'll try and make this make sense.
In a model, Lead, among other properties, we have these:
public class Lead
{
....
public int LeadID {get; set; }
public virtual ICollection<QuoteRevision> QuoteRevisions { get; set; }
....
}
And QuoteRevision...
public class QuoteRevision
{
....
[ForeignKey(nameof(LeadID))]
public virtual Lead Lead { get; set; }
public virtual ICollection<QuoteRevisionProduct> QuoteRevisionProduct{ get; set; }
....
}
And the model for QuoteRevisionProduct:
public class QuoteRevisionProduct
{
....
[ForeignKey(nameof(QuoteRevisionID))]
public virtual QuoteRevision QuoteRevision { get; set; }
[ForeignKey("ProductID")]
public virtual Product Product { get; set; }
....
}
And last of all...
public class Product
{
....
public string Code { get; set; }
....
}
Ok. So these are the models I need to query to build an object called QuoteSearchItem that has multiple properties. Here's two of them:
public class QuoteSearchItem
{
....
public LeadID {get; set; }
public List<string> Codes { get; set; }
....
}
I'm starting with this IQueryable of all rows in Lead:
leads = IQueryable<Lead>
And then doing this:
var results = from l in leads
from qr in l.QuoteRevisions
from rp in qr.RevisionProducts
select new QuoteSearchItem
{
....
LeadID = l.LeadID,
AdditionalProducts = ???
....
};
I'm not sure how to get that list of Codes. I can do this:
Code = rp.Product.Code,
And that will get me a single code, the first in the list. But how do I get ALL that match?
I know this was a lot to follow. I hope it makes sense. Thank you!
EDIT:
This is (almost) the SQL that I'm looking for:
SELECT
l.ID,
p.Code
FROM
dbo.Leads AS l
JOIN QuoteRevisions qr ON qr.LeadID = l.ID
JOIN QuoteRevisionProducts qrp on qrp.QuoteRevisionID = qr.QuoteRevisionID
JOIN Products p on p.ProductID = qrp.ProductID
Except that this will just return multiple rows per product. But, at least it gives an idea.
EDIT 2:
Code = l.QuoteRevisions.SelectMany(qr => qr.RevisionProducts).Select(p => p.Product.Code).ToList()
This doesn't throw an error, but it's returning a row of data for each code, which isn't what I need.
You can use SelectMany to flatten the models and get all the codes, something like this:
var results = from l in leads
select new QuoteSearchItem
{
....
LeadID = l.LeadID,
Codes = l.QuoteRevisions.SelectMany(qr => qr.QuoteRevisionProduct)
.Select(p => p.Product.Code)
....
};
not sure how your DB looks, but you can probably use Distinct as well to eliminate duplicate Codes

Return non-anonymous list entity framework - c#

I have 2 tables in the database :
Table: Order (item_id)
Table: Item ( item_id)
When I'm doing the inner join in entity framework, as you can see below, I need to return in one list the result to manipulate this. Usually when I do the select in one single table , I return a LIST from the entity with the tables name, but I dont know how can I return a LIST when I have 2 or more entity , I mean, using inner join, I would like to return a List that I can manipulate in other class. When I use for only one entity, it is perfect and easy.
public List<????????> getTransdataByStatus(string status)
{
contenxt = new Finance_ManagementEntity();
var _result = (from a in contenxt.Orders
join b in contenxt.Items on a.item_id equals b.item_id
select new
{
a.order_numer,
a.total,
b.item_code,
b.item_qty
});
return _result;
}
I don't know how to return it !! I tried to use the .TOLIST(), but still coming "anonymous".
Thank you
First you need to create a custom type like
public class OrderItems
{
public int Order_numer { get; set; }
public int Total { get; set; }
public string Item_code { get; set; }
public int Item_qty { get; set; }
}
After then modify your function like
public List<OrderItems> getTransdataByStatus(string status)
{
contenxt = new Finance_ManagementEntity();
var _result = (from a in contenxt.Orders
join b in contenxt.Items on a.item_id equals b.item_id
select new OrderItems()
{
Order_numer= a.order_numer,
Total= a.total,
Item_code=b.item_code,
Item_qty=b.item_qty
}).ToList();
return _result;
}
I hope it will work for you.
You can create a compound model that has a property representing each entity.
public class CompoundModel
{
public Entities.Order { get; set; }
public Entities.Item { get; set; }
}
public List<CompoundModel> getTransdataByStatus(string status)
{
contenxt = new Finance_ManagementEntity();
var _result = (from a in contenxt.Orders
join b in contenxt.Items on a.item_id equals b.item_id
select new CompoundModel
{
Order = a
Item = b
});
return _result;
}
Alternatively, if you want to flatten your structure, you can create a class that only has four properties.
public class CompoundModel
{
public string OrderNumber { get; set; }
public int Total { get; set; }
public string ItemCode { get; set; }
public int ItemQuantity { get; set }
}
public List<CompoundModel> getTransdataByStatus(string status)
{
contenxt = new Finance_ManagementEntity();
var _result = (from a in contenxt.Orders
join b in contenxt.Items on a.item_id equals b.item_id
select new CompoundModel
{
OrderNumber = a.order_number,
Total = a.total,
ItemCode = b.item_code,
ItemQuantity = b.item_qty
});
return _result;
}
The problem with your code is this part:
select new // This will create an anonymous type
{
a.order_numer,
a.total,
b.item_code,
b.item_qty
}
As the select generates an anonymous type you will get a list of theses anonymous types as a result of the query. In order to get a list typed results, you need to specify the type in the select-clause:
select new TypeYouWantToReturn() // This will create an real type
{
PropA = a.order_numer, // You also need to specify the properties
PropB = a.total, // of the class that you want to assign
PropC = b.item_code, // the resulting values of the query.
PropD = b.item_qty
}
Now the result of the query will return a list of real types. You need to finally call .ToList() so you get a list instead of the IEnumerable that the select statement will return.

linq - get most recent associated record

Assuming I have Customer and Order objects, where one Customer can have many Orders (so the Order class has a CustomerId property), and I want to return a collection of all CustomerAndMostRecentOrder objects which are defined as follows:
public class CustomerAndMostRecentOrder
{
public Customer Customer { get; set; }
public Order MostRecentOrder { get; set; }
}
How would I write a Linq query which does this (I'm using Linq to SQL)?
You can use the following query:
from c in customers
select new CustomerAndMostRecentOrder
{
Customer = c,
MostRecentOrder = c.Orders.OrderByDescending(o => o.PurchaseDate).FirstOrDefault()
};
This will use a navigation property from customer to order. The MostRecentOrder is taken by ordering the Orders on some DateTime property and then loading the first one.
You will need to have an CreatedDate date in your Order table to get the most recent order. Then to get your CustomerAndMostRecentOrder object, do the following query:
from c in customers
join o in orders on c.ID equals o.CustomerID into co
select new CustomerAndMostRecentOrder
{
Customer = c,
MostRecentOrder = co.OrderByDescending(o => o.CreatedDate).FirstOrDefault()
}
public class CustomerAndMostRecentOrder
{
public CustomerAndMostRecentOrder(Customer customer, Order mostRecentOrder)
{
Customer = customer;
MostRecentOrder = mostRecentOrder;
}
public Customer Customer { get; set; }
public Order MostRecentOrder { get; set; }
}
public class Order
{
}
public class Customer
{
public IEnumerable<Order> GetOrders()
{
}
}
public static class UsageClass
{
public static void Sample(IEnumerable<Customer> allCustomers)
{
IEnumerable<CustomerAndMostRecentOrder> customerAndMostRecentOrders =
allCustomers.Select(customer => new CustomerAndMostRecentOrder(customer, customer.GetOrders().Last()));
}
}
As another alternative, you may want to take a look into the DataLoadOptions.AssociateWith discussed at http://msdn.microsoft.com/en-us/library/system.data.linq.dataloadoptions.associatewith.aspx. Simply set your requirements on the context and you don't need to worry about filtering the children at the query level.

LINQ Multiple Nested Table Aggregate Query

I have 4 entities which I've defined Navigation properties on like the following:
internal class ShipSet
{
[Key]
[Column("SHIPSET_ID")]
public decimal ID { get; set; }
public virtual ICollection<InstallLocation> InstallLocations { get; set; }
}
internal class InstallLocation
{
[Key]
[Column("INSTLOC_ID")]
public decimal ID { get; set; }
[Column("SHIPSET_ID")]
public decimal ShipSetID { get; set; }
public virtual ICollection<ShipSetPart> ShipSetParts { get; set; }
public virtual ShipSet ShipSet { get; set; }
}
internal class ShipSetPart
{
[Key]
[Column("PARTS_ID")]
public decimal ID { get; set; }
[Column("INSTLOC_ID")]
public decimal InstallLocationID { get; set; }
public virtual CmQueueItem CmQueueItem { get; set; }
public virtual InstallLocation InstallLocation { get; set; }
}
internal class CmQueueItem
{
[Key]
[Column("INVENTORY_ITEM_ID")]
public decimal ID { get; set; }
public virtual ICollection<ShipSetPart> ShipSetParts { get; set; }
}
I have the following fluent config:
modelBuilder.Entity<CmQueueItem>().HasMany(p => p.ShipSetParts).
WithRequired(s=>s.CmQueueItem).Map(m=>m.MapKey("INVENTORY_ITEM_ID"));
modelBuilder.Entity<ShipSetPart>().HasRequired(p => p.InstallLocation);
modelBuilder.Entity<InstallLocation>().HasRequired(p => p.ShipSet);
modelBuilder.Entity<ShipSet>().HasRequired(p => p.Program);
modelBuilder.Entity<CmQueueItem>().Property(p => p.LastUpdateDate).IsConcurrencyToken();
So in a nutshell, I have
ShipSet -> InstallLocation (1 to many)
InstallLocation -> ShipSetPart (1 to many)
CmQueueItem -> ShipSetPart (1 to many via INVENTORY_ITEM_ID)
I am trying to figure out how to write a LINQ query where I can create an anonymous object which includes the count of ShipSets for each CmQueueItem.
var queueItems = from c in dbContext.CmQueueItems
select new
{
InventoryItemID = c.ID,
ShipSets = 0 //[magical LINQ goes here]
};
It should generate a SQL statement similar to the following:
select d.inventory_item_id, count(a.shipset_id) as ShipSets
from shipsets a,
instlocs b,
ss_parts c,
cm_queue d
where a.shipset_id = b.shipset_id
and b.instloc_id = c.instloc_id
and c.inventory_item_id = d.inventory_item_id
group by d.inventory_item_id;
I'm new to LINQ and am having a hard time understanding how to perform aggregates and groupings like this. Any ideas?
The answer as provided below is to use the "let" keyword in LINQ:
var query = from c in dbContext.CmQueueItems
let shipSets = (from s in c.ShipSetParts
select s.InstallLocation.ShipSet)
select new
{
InventoryItemId = c.ID,
ShipSets = shipSets.Count(),
};
I haven't got an EF model to test this against, but give this a try...
UPDATE: Here's how to perform the join, see if that works. Something isn't right though because we shouldn't have to perform the join manually, that's what your property mapping is for.
var queueItems = from c in dbContext.CmQueueItems
join s in dbContext.ShipSetParts
on c.ID equals s.CmQueueItem.ID
group s by s.CmQueueItem.ID into grouped
select new
{
InventoryItemID = grouped.Key,
//this may need a distinct before the Count
ShipSets = grouped.Select(g => g.InstallLocation.ShipSetID).Count()
};
Here's an alternate approach that's much cleaner, but I'm unsure if it will work in EF. Give it a try and see what you think.
var queueItems = from c in dbContext.CmQueueItems
let shipSets = (from s in c.ShipSetParts
select s.InstallLocation.ShipSet)
select new
{
InventoryItemID = c.ID,
ShipSets = shipSets.Count()
};

Categories