I have an entity with one field I do not want to return sometimes.
I am setting it to null right now. Is there a way to specify this in the query itself instead of clearing it out like I am here?
public async Task<IQueryable<XYZXY>> GetStuff()
{
histories =
_db.Stuffs
.Where(n => n.NationId == User.NationId)
.OrderBy(x => x.DateSent);
await histories.ForEachAsync(d => d.Attachment = null);
return histories;
}
What you are looking for is called projection, this is what stuff do you want to project into your result set from the server.
In EF projection is done by a combination of the select line of your query and any Includes which you have done.
If attachment is a second table accessed by a navigation property it wont be returned by your current query (IE it will be null) unless you are doing lazy loading (normally signified by the virtual keyword on the nav property eg public virtual Attachment Attachment {get;set;}). If attachment is a column projection is more complicated
You have 2 options, use an annonomous type eg:
_db.Stuffs
.Where(n => n.NationId == User.NationId)
.OrderBy(x => x.DateSent)
.Select(x=> new { A = x.A, B = x.B .... /*Dont list attachment*/});
or reuse the existing object
_db.Stuffs
.Where(n => n.NationId == User.NationId)
.OrderBy(x => x.DateSent)
.Select(x=> new Stuff { A = x.A, B = x.B .... /*Dont list attachment*/});
Do note custom projections will not be tracked so changing a property and calling save wont work.
Related
I'm trying to get specific columns from a context with several includes, but when I try:
Context.Include(i => i.c)
.Include(i => i.l).Select(s=> new LocationCatalog { Name = s.Name})
.Include(i => i.p)
.Include(i => i.li)
.Include(i => i.pcl)
VS throws an error after the select.
How can I achieve this? I want to specify the columns for each include.
The error says that for example: i.p doesn't contains definition for i.l
That's not possible with Entity Framework. You either include the entire table (with Include, as you are doing) or you don't include it at all.
If you want to load only specific columns, you can do this, but see how it's a manual process:
Context
.Select(i => new YourType
{
c = i.l,
l = i.l,
x = new X
{
a = i.x.a // only the properties you want here
}
...
});
The moment you use Select, Include is completely ignored, so you cannot use both.
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.
I am trying to cut this linq down
var sys = db.tlkpSystems
.Where(a => db.tlkpSettings.Where(e => e.Hidden < 3)
.Select(o => o.System)
.ToList().Contains(a.System)) //cannot get this part in?
.OrderBy(a => a.SystemName).ToList();
foreach (var item in sys)
model.Add(new SettingSystem {
System = item.System,
SystemName = item.SystemName
});
I have tried the following:
List<SettingSystem> model = new List<SettingSystem>();
model = db.tlkpSettings.Where(e => e.Hidden < 3)
.OrderBy(e => e.Setting)
.Select(e => new SettingSystem
{
System = e.System,
SystemName = e.Setting
}).ToList();
How can I call the .Contains(a.System) part in my query?
Thanks
Some general rules when working with LINQ to Entities:
Avoid using ToList inside the query. It prevents EF to build a correct SQL query.
Don't use Contains when working with entities (tables). Use Any or joins.
Here is your query (in case System is not an entity navigation property):
var sys = db.tlkpSystems
.Where(a => db.tlkpSettings.Any(e => e.Hidden < 3 && e.System == a.System))
.OrderBy(a => a.SystemName).ToList();
As an addendum, there is also AsEnumerable for when you must pull a query into memory (such as calling methods within another clause). This is generally better than ToList or ToArray since it'll enumerate the query, rather than enumerating, putting together a List/Array, and then enumerating that collection.
I have the following queries:
var ground = db
.Ground
.Where(g => g.RowKey == Ground_Uuid)
.ToList();
var building = db
.Building
.Where(b => ground.Any(gr => gr.RowKey == b.Ground.RowKey))
.ToList();
var floor = db
.Floor
.Where(b => building.Any(by => by.RowKey == b.Building.RowKey))
.ToList();
So the second relies on the id from the first set and so on.
I got following error when an execution goes to the second query:
Unable to create a constant value of type 'Domain.Model.Entities.Ground'. Only primitive types or enumeration types are supported in this context.
Any ideas how to resolve it?
The problem with your code is that ToList is converting the result into an in-memory object and a collection of objects in memory cannot be joined with a set of data in the database.
var ground = db.Ground.Where(g => g.RowKey == Ground_Uuid);
var building = db.Building.Where(b => ground.Any(gr => gr.RowKey == b.Ground.RowKey));
var floor = db.Floor.Where(b => building.Any(by => by.RowKey == b.Building.RowKey));
Also, frankly after reading #juharr's comment, I saw the relationship between floor, building & ground. Since you are already doing b.Building.RowKey, b.Ground.RowKey predicting the relationship was easy and I totally agree, it can be simplified as:-
var floor = db.Floor.Where(b => b.Building.Ground.RowKey == Ground_Uuid);
It seems like the first query is redundant. You already know that the RowKey column for each row will be equal to Ground_Uuid.
var building = db.Building.Where(b => b.Ground.RowKey == Ground_Uuid);
var floor = db.Floor.Where(b => b.Building.Ground.RowKey == Ground_Uuid);
Removing ToList() would do the job, but moreover if RowKey is the foreign key you can utilize Linq:
var floor = db.Floor
.Where(b => b.Building.Ground.RowKey == Ground_Uuid)
.ToList();
I am using nHibernate to retrieve a collection of orders (and its order lines) from a Sql Server database.
This is my ShipmentOrder class:
public class ShipmentOrder
{
private ICollection<ShipmentDetail> _ShipmentsDetails;
public virtual ReadOnlyCollection<ShipmentDetail> ShipmentsDetails
{
get { return (new List<ShipmentDetail>(_ShipmentsDetails).AsReadOnly()); }
}
}
nHibernate returns a IList with all the details (ShipmentsDetails) loaded (since I Eager load them).
Now, I would like to filter my collection of ShipmentOrder and ShipmentDetail and get back a collection of ShipmentDetail.
I've tried something like this:
IList<ShipmentOrder> Results;
// Fetch orders using nHibernate
Results = FetchOrders();
var shipmentLines = Results
.Where(x => x.Company == "XXX" && x.OrderNumber == "111")
.SelectMany(x => x.ShipmentsDetails)
.Where(s => s.RowNumber == 1 && s.RowSeq == 0)
.ToList();
But I've realized that I obtain multiple results of the same line.
I've converted the lamda expression like this:
var shipmentLines = Results
.Where(x => x.Company == "XXX" && x.OrderNumber == "111")
.SelectMany(x => x.ShipmentsDetails)
.Where(s => s.RowNumber == 1 && s.RowSeq == 0)
.Distinct()
.ToList();
and it works fine.
I was wondering if there's a better way to achieve the same result without the distinct.
UPDATE:
I am using SelectMany here cause this is the only way I've found to apply filters to children (ShipmentsDetails).
this looks like Linq2Objects, therefor the IList Results contains duplicate records, probably you do eager fetching to levels deep which unfortunatly results in duplicate root entities. use distinctrootentity-resulttransformer in the query