I have the following simplified setup:
Public User
{
//Primary key
public int Id {get;set;}
public string Name {get; set;}
}
Public UserInfo
{
//Primary key
public int Id {get;set;}
//Foreign key to user table
public int userKey {get; set;}
}
The relationship between the tables is one user to Many userInfo
I am trying to select from the user table and include the userInfo table.
I cannot do this:
var users = Context.user.Include(u => u.userInfos);
as there is no reference to the UserInfo table from the user table.
I can do this:
context.userInfo.include(x => x.user)
but if there are no corresponding entries in the userInfo table, this will not return any results, which is not what I want. Also, this will return one row for each userInfo, whereas I want one row for user, with a list of userInfo as a parameter.
Similarly I could join the tables like this:
var users = from us in Context.user
join inf in Context.userInfo
on us.Id equals inf.userKey
select new //etc
But this will also return one row per userInfo entry, the same problem as above.
To summarise, is there a way of including this table to produce a result in the same way as the include function does.
I know I could adjust my setup to all me to include this, but that is not what I am asking here.
I suspect this cannot be done, but from all my googling so far I have not been able to find a definitive answer....
I want one row for user, with a list of userInfo as a parameter
I assume you mean a list of userInfo as a property. My understanding of what you ask it that you're just looking for:
var users = from us in Context.user
join inf in Context.userInfo
on us.Id equals inf.userKey into infg
select new
{
User = us,
UserInfos = infg
};
join ... into amounts to a GroupJoin, i.e. a user entity joined with a group of userinfos.
Better still is to use a navigation property user.userInfos (reluctantly following your naming convention):
var users = Context.user.Include(u => u.userInfos);
Related
I am using entity Framework and LINQ two perform the join between these two tables:
var query =
orders.Join(
orderdetails,
order => order.Code,
orderdt => orderdt.Order.Code,
(order, orderdt)
=> new
{
Code = order.Code,
WarehouseDivision = orderdt.WarehouseDivision,
Type = order.Type,
SupplierCode = order.SupplierCode,
SupplierDescription = order.SupplierDescription,
ExpectedDeliveryDate = orderdt.ExpectedDeliveryDate
});
the join works fine.
Now for each join row I need to select the row with the minimum ExpectedDelivery Date.
Any hint on how to achieve this?
I see something strange on your Join.
Every Order has a Code. Every OrderDetail has an Order, which also has a Code.
If there is a one-to-many relationship between Orders and OrderDetails, then every Order has zero or more OrderDetails. Every OrderDetail belongs to exactly one Order, namely the Order that the foreign key points to. Is that the same Order as OrderDetail.Order?
In that case Order.Code equals OrderDetail.Order.Code for every OrderDetail of this code. Not very useful to perform a join on these values.
I think that you mean to join on primary key and foreign key.
Either way, whether you join on keys or on property Code, the solution to your problem is to perform a GroupJoin instead of a Join.
If you've followed the entity framework code first conventions, you'll have something similar to this:
class Order
{
public int Id {get; set;} // Primary key
... // other Order properties
// every Order has zero or more OrderDetails (one-to-many)
public virtual ICollection<OrderDetail> OrderDetails {get; set;}
}
class OrderDetail
{
public int Id {get; set;}
public DateTime ExpectedDeliveryDate {get; set;}
... // other OrderDetail properties
// every OrderDetail belongs to exactly one Order, using foreign key
public int OrderId {get; set;}
public virtual Order Order {get; set;}
}
It might be that you used different identifiers for your properties, but the main thing are the virtual properties.
In entity framework the non-virtual properties represent the columns in the table, the virtual properties represent the relations between the tables (one-to-many, many-to-many).
Requirement
From every Order, give me some properties, together with some properties of the OrderDetail with the minimum ExpectedDeliveryDate.
Solution with GroupJoin
GroupJoin on primary / foreign key:
var OrdersWithOldestOrderDetail = dbContext.Orders // GroupJoin Orders
.GroupJoin(dbContext.OrderDetails, // with OrderDetails
order => order.Id, // from every Order take the Id
orderDetail => orderDetail.OrderId, // from every OrderDetail take the OrderId
(order, orderDetailsOfThisOrder) => new // take the Order with all its matchin OrderDetails
{ // to make one new
// Select the Order properties that you plan to use:
...
// Select the OrderDetail with the minimum ExpectedDeliveryDate
// To do this, order by ascending deliveryDate
// then Select the properties you want to used
// and take the FirstOrDefault
EarliestDeliveredOrderDetail = orderDetailsOfThisOrder
.OrderBy(orderDetail => orderDetail.ExpectedDeliveryDate)
.Select(orderDetail => new
{
...
})
.ToList(),
});
If you really want to join on the properties you said:
var result = dbContext.Orders // GroupJoin Orders
.GroupJoin(dbContext.OrderDetails, // with OrderDetails
order => order.Code, // from every Order take the Code
orderDetail => orderDetail.Order.Code, // from every OrderDetail take Order.Code
(order, orderDetailsWithThisCode) => new // take the Order with all its matchin OrderDetails
{
... etc, same as above
Easier Solution using the ICollection
If you are using entity framework and you need to select "All orders with its OrderDetails", "All Schools with its Students", "All Products with its Components", in fact: all Items together with all related sub-items, it is usually way simpler to use the ICollections instead of executing a join yourself:
var OrdersWithOrderDetails = dbContext.Orders.Select(order => new
{
// Select the Order properties that you plan to use:
...
EarliestDeliveredOrderDetail = order.OrderDetails
.OrderBy(orderDetail => orderDetail.ExpectedDeliveryDate)
.Select(orderDetail => new
{
// Select the OrderDetail properties that you plan to use:
...
})
.FirstOrDefault(),
})
See how naturally it feels if you use the ICollections instead of the (Group-)join? Entity framework knows your relations and performs the correct join for you with the correct (foreign) keys.
In fact, I seldom create a Join myself. Most of the time I use the virtual properties.
I have a table Users which contain user information. I have a table Products which contain product information. I have a table called UserProduct which acts as a junction table and whose fields are UserId and ProductId. I am using a Entity Framework database first approach.
I want to outerjoin using Linq to find the following data.
All Users in the Users table.
All Users who have bought a particular product in terms of a Boolean called isPurchased.
My thinking was to left outer join table User with UserProduct and get all users and whether they have a product something like this.
var result = from a in Users
join b in UserProduct(Not available through EF) on a.Id equals b.prodId into group1
from g1 in group1.DefaultIfEmpty()
select new
{
id = g1.Id,
isPurchased = g1.prodId != null
}.ToList();
However in EF mapping, the object UserProduct is not created and so I cannot use it directly in my Linq query? So how do I go about this? Is there a way I can use linq to join tables with the actual table name(UserProduct) instead of joining entities?
Assuming Users contains a property List<Products> products to represent the junction information, and a variable boughtProductId to represent the particular product:
var result = from u in Users
let isPurchased = u.products.Any(p => p.Id == boughtProductId)
select new {
id = isPurchased ? boughtProductId : null,
isPurchased
}.ToList();
Let Users be a database containing typical users data (name, email...) with an ID as primary key.
Let Applications be a database storing a list of applications (name, developer...) with an ID as primary key.
Let UsersApps be the mapping table between the two, using the primary key. UsersApps thus stores rows of ..., {102, user1_Id, appA_Id}, {103, userN_ID, appB_Id}, {104, user1_Id, appC_Id}, ...
And so on.
I want to retrieve a list of users data {name, email, List<Application> boughtApps}
I am struggling to find a LINQ request to do that and I am trying to do it in two steps, get a big table and then build each user's list of applications.
var q1 = from user in _dbContext.Users
join appUti in _dbContext.AppUsers on user.Id equals appUti.UsersId
join app in _dbContext.Applications on appUti.ApplicationId equals app.Id
orderby user.Id
select new UserDataWithApp { Id = user.Id, Name = user.Name, firstName= user.FirstName, Email = user.Email, App = app };
Then parse q1.toList() to build my required results list.
var qRes = q1.ToList();
int i = 0;
int j = 0;
while (i<qRes.Count())
{
listUsersWithApps[j] = qRes[i];
while (qRes[i].Id == listUsersWithApps[j].Id) // llist is ordered !!
{
listUsersWithApps[j].Apps.Add(qRes[i].Apps[0]);
i++;
}
j++;
}
Isn't there a better way ?
You can use navigation properties to allow the following:
var userApps = context.Users.Select(u => new UserWithApp(u.Name, u.Email, u.Applications))
Just add to following to User:
public virtual ICollection<Application> Applications { get; set; }
and to Application:
public virtual ICollection<Users> Users { get; set; }
So you can "navigate" between your entities and write to following query (just adapt your ordering and what user data to be seleted):
var userApps = from user in context.Users
select new UserDataWithApp { ..., BoughtApps = user.Applications }
See here for an example: http://www.entityframeworktutorial.net/code-first/configure-many-to-many-relationship-in-code-first.aspx
or another interesting blog: https://lostechies.com/jimmybogard/2014/03/12/avoid-many-to-many-mappings-in-orms/
I have a customer table, where I have the basic information name, address, postcode etc. A company can have multiple customers. So the relationship is loosely related to the Company table with the foreign key CompanyId. To search for customers I use, a IQueryable method as,
private IQueryable<CustomerModel> MapCustomer(IQueryable<Customer> query)
{
return (from a in query
select new CustomerModel
{
CompanyId = a.CompanyId,
CustomerId = a.Id,
CustomerName = a.Name,
AddressLine1 = a.AddressLine1,
PhoneNo = a.TelephoneNumber,
Postcode = a.PostCode,
ServiceCompletion = a.ServiceCompleteted,
ServiceDate = a.ServiceDate
});
}
As you can see, if I needed to search all I do is, call the function to return an IEnumerable, then use Where on it.
return MapCustomer(db.Customers.AsNoTracking().Where(x => x.Id == id)).FirstOrDefault();
The problem is I need to be able to populate the company name instead of the CompanyID. So when I get the result I would be able to see the Name of the company. What should I do to get this info? Any help is appreciated.
Here are the methods mentioned above:
public IList<tst> testUsers()
{
IList<tst> testUsers = _test.GetAll().ToList();
return test(test);
}
To display Users with Location I think you need one class called AdsnapshotUsers
public class AdsnapshotUsers
{
// three fields UserId, UserLogonName, Location
}
Now create one method which return IList<AdsnapshotUsers>
public IList<AdsnapshotUsers> GetAdsnapshotUsers()
{
List<User> Users = GetAcitveUsers().ToList();
List<ADSnapshot> adSnapshotUsers = _adSnapshotRepository.GetAll().ToList();
return (from u in Users
join ad in adSnapshotUsers on u.UserLogonName equals ad.UserLogonName
select new AdsnapshotUsers {
UserId= u.UserId, UserLogonName = u.UserLogonName, Location = ad.Location
}
).ToList<AdsnapshotUsers>();
}
Left Outer Join to display all the values from the user table eventhough if a userlogonname is not present in the adsnapshot table (location value blank)
(from u in Users
join ad in adSnapshotUsers on u.UserLogonName equals ad.UserLogonName into aduserselect
from ad1 in aduserselect.DefaultIfEmpty()
select new AdsnapshotUsers {
UserId= u.UserId, UserLogonName = u.UserLogonName, Location = ad1.Location
}
).ToList<AdsnapshotUsers>();
Here all records from user table will get selected and for location if userlogonname exist then location name value set with ADSnapshot table value otherwise not exist then default empty value set.
The simplest way to join two IEnumerables in LINQ is with Join():
var joined = from u in GetActiveUsers()
join ad in _adSnapshotRepository.GetAll()
on u.UserLogonName equals ad.UserLogonName
select new { User = u, Location = ad.Location }
Of course, joined is now an IEnumerable of an anonymous type. If you are displaying this information in a grid view or something that requires a flat object, you may want to create your own class with the properties you want from each table and select that instead.
Two other things to note:
(1) you always seem to call ToList() early to convert from IQueryable to IEnumerable. By putting off ToList() until you've done all your joining and filtering, you can get most of your query to happen in SQL (rather than in memory).
(2) rather than joining, it is often preferable to set up associtation properties on your entities and use those:
IQueryable<User> users = ...
var combinedInfo = users.Select(u => new { User = u, Location = u.ADSnapshot.Location })
.ToList();
If you have relationship (1:1) between these two tables then you can get records very easily using just one query. and evenif records are not present in second table.
I am using above defined class definition to store data.
_userRepository.GetAll().Select(u=>new AdsnapshotUsers {
UserId= u.UserId,
UserLogonName = u.UserLogonName,
Location = u.ADSnapshot.Location
}).ToList();