How to select a single column with Entity Framework? - c#

Is there a way to get the entire contents of a single column using Entity Framework 4? The same like this SQL Query:
SELECT Name FROM MyTable WHERE UserId = 1;

You can use LINQ's .Select() to do that. In your case it would go something like:
string Name = yourDbContext
.MyTable
.Where(u => u.UserId == 1)
.Select(u => u.Name)
.SingleOrDefault(); // This is what actually executes the request and return a response
If you are expecting more than one entry in response, you can use .ToList() instead, to execute the request. Something like this, to get the Name of everyone with age 30:
string[] Names = yourDbContext
.MyTable
.Where(u => u.Age == 30)
.Select(u => u.Name)
.ToList();

I'm a complete noob on Entity but this is how I would do it in theory...
var name = yourDbContext.MyTable.Find(1).Name;
If It's A Primary Key.
-- OR --
var name = yourDbContext.MyTable.SingleOrDefault(mytable => mytable.UserId == 1).Name;
-- OR --
For whole Column:
var names = yourDbContext.MyTable
.Where(mytable => mytable.UserId == 1)
.Select(column => column.Name); //You can '.ToList();' this....
But "oh Geez Rick, What do I know..."

Using LINQ your query should look something like this:
public User GetUser(int userID){
return
(
from p in "MyTable" //(Your Entity Model)
where p.UserID == userID
select p.Name
).SingleOrDefault();
}
Of course to do this you need to have an ADO.Net Entity Model in your solution.

You could use the LINQ select clause and reference the property that relates to your Name column.

If you're fetching a single item only then, you need use select before your FirstOrDefault()/SingleOrDefault(). And you can use anonymous object of the required properties.
var name = dbContext.MyTable.Select(x => new { x.UserId, x.Name }).FirstOrDefault(x => x.UserId == 1)?.Name;
Above query will be converted to this:
Select Top (1) UserId, Name from MyTable where UserId = 1;
For multiple items you can simply chain Select after Where:
var names = dbContext.MyTable.Where(x => x.UserId > 10).Select(x => x.Name);
Use anonymous object inside Select if you need more than one properties.

Related

LINQ efficiency

Consider the following LINQ statements:
var model = getModel();
// apptId is passed in, not the order, so get the related order id
var order = (model.getMyData
.Where(x => x.ApptId == apptId)
.Select(y => y.OrderId));
var orderId = 0;
var orderId = order.LastOrDefault();
// see if more than one appt is associated to the order
var apptOrders = (model.getMyData
.Where(x => x.OrderId == orderId)
.Select(y => new { y.OrderId, y.AppointmentsId }));
This code works as expected, but I could not help but think that there is a more efficient way to accomplish the goal ( one call to the db ).
Is there a way to combine the two LINQ statements above into one? For this question please assume I need to use LINQ.
You can use GroupBy method to group all orders by OrderId. After applying LastOrDefault and ToList will give you the same result which you get from above code.
Here is a sample code:
var apptOrders = model.getMyData
.Where(x => x.ApptId == apptId)
.GroupBy(s => s.OrderId)
.LastOrDefault().ToList();
Entity Framework can't translate LastOrDefault, but it can handle Contains with sub-queries, so lookup the OrderId as a query and filter the orders by that:
// apptId is passed in, not the order, so get the related order id
var orderId = model.getMyData
.Where(x => x.ApptId == apptId)
.Select(y => y.OrderId);
// see if more than one appt is associated to the order
var apptOrders = model.getMyData
.Where(a => orderId.Contains(a.OrderId))
.Select(a => a.ApptId);
It seems like this is all you need:
var apptOrders =
model
.getMyData
.Where(x => x.ApptId == apptId)
.Select(y => new { y.OrderId, y.AppointmentsId });

Linq - Trying to filter out the Eager Selection

I am trying to filter out the second part of the tables (UserRoles.IsDeleted==false). Is there any advice how i can do that?
var Users = context.Users.Where(r => r.IsDeleted == IsDeleted).ToList<User>();
Users = context.Users.Include(x => x.UserRoles.Select(y=>y.IsDeleted==false)).ToList();
Thank you
You can do the following to filter using the second part:
var Users = context.Users.Where(r => r.IsDeleted == IsDeleted).ToList<User>();
if(condition)
{
Users = Users.where(y => y.IsDeleted == false)).ToList();
}
There are two options to filter related entities
Doing a projection.
Unfortunately, when you use Include method, you can't filter the related entities as you intend to do. You need to project your query to a DTO object or a anonymous object, as the below example.
var query=context.Users.Include(x => x.UserRoles)
.Where(r => r.IsDeleted == IsDeleted)
.Select(u=> new{ ...,
Roles=x => x.UserRoles.Where(y=>!y.IsDeleted)})
A second option could be using Explicitly Loading. But this is in case you can load the related entities of one specific entity,eg,.
var user=context.Users.FirstOrDefault(r.IsDeleted == IsDeleted);//Getting a user
context.Entry(user)
.Collection(b => b.UserRoles)
.Query()
.Where(y=>!y.IsDeleted)
.Load();
You can do this inside of a foreach per each entity you get from the first query,
var query=context.Users.Where(r => r.IsDeleted == IsDeleted);
foreach(var u in query)
{
context.Entry(u)
.Collection(b => b.UserRoles)
.Query()
.Where(y=>!y.IsDeleted)
.Load();
}
but it's going to be really inefficient because you are going to do a roundtrip to your DB per each entity. My advice is use the first option, projecting the query.

Linq query to select all numbers that match

I have some difficulties to query a table. In the table there are a column for ResourceID and a column for ProjectID. I want to get a list of all int numbers from the ResourceID where ProjectID match the projId number. Is this possible?
I have tested, but I guess this is wrong. I thought I could get the value by using the resources variable, but that isn't working.
var resources = db.Activities.Where(x => x.ProjectID == projId).ToList();
resources. ????
You could accomplish this via a Select() query to only pull the specific properties that you need (in this case the ResourceID property) for each of the elements within your collection :
// Get your resources that meet your requirement
var resources = db.Activities.Where(x => x.ProjectID == projId).ToList();
// Get your Resource IDs from your previous query
var resourceIds = resources.Select(r => r.ResourceID);
You could actually just perform this in a single call if you preferred :
// This will return a list of ResourceIDs that match
var resourceIds = db.Activities.Where(a => a.ProjectID == projId)
.Select(a => a.ResourceID)
.ToList();
I believe the answer is:
var resources = db.Activities.Where(x => x.ProjectID == projId).Select(x => x.ResourceID).ToList();
You're getting a list of the Activities. What you need to do is to select the item you want--in this case the ResourceID column.
var resources = db.Activities.Where(x => x.ProjectID == projId)
.Select(x => x.ResourceID)
.Distinct() // Thought you might want this
.ToList();
You can also use a different format like this, which can sometimes be clearer.
var resources = (from a in db.Activities
where a.ProjectID == projId
select a.ResourceID).Distinct().ToList();
Please update your query if you want to get list of int..
var resources = db.Activities.Where(x => x.ProjectID == projId).Select(x => x.ResourceID).ToList();

EF 7 Include a collection with where clause and then a collection one level down

I am trying to query a collection and child collection using EF 7. Here's the code:
var customerID = 86795;
var query = await _context.Contacts
.Where(g => g.CustomerID == customerID )
.Include(g => g.Address.Where(p => p.AddressTypeID == 1))
.ThenInclude(p=> p.City)
.ToListAsync();
> Error CS1061 'IEnumerable<Address>' does not contain a definition for
> 'City' and no extension method 'City' accepting a first argument of
> type 'IEnumerable<Address>' could be found (are you missing a using
> directive or an assembly reference?) Contacts.DNX 4.5.1, Contacts.DNX
> Core 5.0
It works fine when I just use
var customerID = 86795;
var query = await _context.Contacts
.Where(g => g.CustomerID == customerID )
.Include(g => g.Address)
.ThenInclude(p=> p.City)
.ToListAsync();
But this will load all the addresses for the customer where I only want the recent address for which the AddressTypeID is 1.
Any idea how to do this?
You can try anonymous projection, that will fetch translate your query into SQL.
var customerID = 86795;
var query = await _context.Contacts
.Where(g => g.CustomerID == customerID)
.Select(cntct=> new
{
contact = cntct,
address = cntct.Address.Where(p => p.AddressTypeID == 1),
city = cntct.Address.Where(p => p.AddressTypeID == 1)
.Select(h=>h.City),
}.ToList();
You can't filter in Include. In any version of entity framework.
If you need to load a subset of the collection then you need to Join instead of using navigation property and filter whenever you need using Where clause
Like this (simplified, extra steps for readability):
var filteredAddresses = Addresses.Where(x=>x.AddressTypeId==1);
var customersWithAddress = Customers.Join(filteredAddresses, x=>x.Id,x=>x.CustomerId,(c,a)=> new {
Customer=c,
Address=a
});
Or if you need a single customer, assuming you have Customer navigation property in Address:
var addressWithCustomer = Addresses
.Where(x=>x.AddressTypeId==1 && x.CustomerId == customerId)
.Include(x=>x.Customer)
.Include(x=>x.City)
.Single();
A lot of times, it is better to approach queries which involve conditional nested entities, to start with the nested entity, apply the conditions to this nested fellow and then project out the parent entity, since it is always easier to reach to the parent entities from the nested enumerable ones. (many to one)
in our case, we can apply the filter out on the Address entity and then group it on the Contact entity.
var customerID = 86795;
var query = await _context.Addresses
.Where(a => a.Contact.CustomerID == customerID
&& a.Contact.RegistrationDate.Year == 2016
&& a.AddressTypeID == 1)
.Include(a => a.Contact)
.Include(a => a.City)
.GroupBy(a => a.Contact)
.Take(20) // by the way, you should apply some orderby for a predicatble Take
.ToListAsync();
and if you absolutely want a list of Contacts as the output of the above query, you can do this.
var contacts = query.Select(g =>
{
g.Key.Addresses = g.ToList();
return g.Key;
}).ToList();
// now you can work off the Contacts list, which has only specific addresses
This will basically give you a grouped list of all Contacts with CustomerID, and with those address types and registration years only. The important thing here is to iterate through the group to get the addresses, and not use the grouping.Key.Addresses navigation. (grouping.Key will be the Contact entity)
Also, I don't know if CustomerID is a primary key on the Contact entity, but if it is, it looks like you would just need a list of matching addresses for one Contact. In that case, the query would be:
var query = await _context.Addresses
.Where(a => a.Contact.CustomerID == customerID && a.AddressTypeID == 1)
.Include(a => a.Contact)
.Include(a => a.City)
.ToListAsync();
Include The Collection for Eager Load then use Any instead of Where ... to Select specific items in the child of the wanted entity.
var customerID = 86795;
var query = await _context.Contacts
.Where(g => g.CustomerID == customerID )
.Include(g => g.Address.Any(p => p.AddressTypeID == 1))
.ThenInclude(p=> p.City)
.ToListAsync();

Linq Query with double sub queries

I am struggling converting the following SQL query I wrote into Linq. I think I'm on the right track, but I must be missing something.
The error I'm getting right now is:
System.Linq.IQueryable does not contain a definition for .Contains
Which is confusing to me because it should right?
SQL
select Users.*
from Users
where UserID in (select distinct(UserID)
from UserPermission
where SupplierID in (select SupplierID
from UserPermission
where UserID = 6))
LINQ
var Users = (from u in _db.Users
where (from up in _db.UserPermissions select up.UserID)
.Distinct()
.Contains((from up2 in _db.UserPermissions
where up2.UserID == 6
select up2.SupplierID))
select u);
EDIT: I ended up going back to SqlCommand objects as this was something I had to get done today and couldn't waste too much time trying to figure out how to do it the right way with Linq and EF. I hate code hacks :(
I think there is no need to do a distinct here (maybe I am wrong). But here is a simpler version (assuming you have all the navigational properties defined correctly)
var lstUsers = DBContext.Users.Where(
x => x.UserPermissions.Any(
y => y.Suppliers.Any(z => z.UserID == 6)
)
).ToList();
Above if you have UserID field in Supplier entity, if it is NOT you can again use the navigational property as,
var lstUsers = DBContext.Users.Where(
x => x.UserPermissions.Any(
y => y.Suppliers.Any(z => z.User.UserID == 6)
)
).ToList();
Contains() only expects a single element, so it won't work as you have it written. Try this as an alternate:
var Users = _db.Users
.Where(u => _db.UserPermissions
.Select(x => UserID)
.Distinct()
.Where(x => _db.UserPermissions
.Where(y => y.UserID == 6)
.Select(y => y.SupplierID)
.Contains(x))
);
I didn't try on my side but you can try using the let keyword:
var Users = (from u in _db.Users
let distinctUsers = (from up in _db.UserPermissions select up).Distinct()
let subQuery = (from up2 in _db.UserPermissions
where up2.UserID == 6
select up2)
where
distinctUsers.SupplierID== subQuery.SupplierID &&
u.UserID==distinctUsers.UserID
select u);

Categories

Resources