Select Linq query Map to Entity - c#

I have this code to get all Meal from the database but considering optimization, i want to only get the entities needed, instead of returning all.
public async Task<IEnumerable<Meal>> GetAllMeal()
{
return await _dbSet.Include(x => x.MealPrices).ToListAsync();
}
The above code will fetch all entities from the database including the ones i don't need.
Is there a way to map my dto at the point of fetching the data from db

I think you're looking for the Where clause. For example, if your Meal model has an integer Id as the primary key you could do this:
public async Task<Meal> GetMealById(int id)
{
return await _dbSet
.Include(x => x.MealPrices)
.Where(y => y.Id == id)
.FirstOrDefault();
}

Related

What is better/faster or safer .Include() or Get Entity separately?

I have an entity Project and then I have an entity Ticket that has a projectId. Before for Details page, I find the project
public async Task<Project> FindByIdAsync(int id)
{
return await _db.Projects
.Include(p => p.ApplicationUsers)
.FirstOrDefaultAsync(p => p.Id == id);
}
and then I find the tickets by projectId
public async Task<ICollection<Ticket>> GetAllTicketsForProject(int projectId)
{
return await _db.Tickets
.Where(t => t.ProjectId == projectId)
.Include(t => t.AssignedUser)
.Include(t=>t.Project)
.OrderBy(t=> t.DateCreated)
.ToListAsync();
}
When deleting the ticket I found out about cascade deletion and that I can just make
public IList<Comment> Comments { get; set; } = new List<Comment>();
and now I can just Include all the comments in the query. That was something new to me.
Now I would like to know what is the better practice? To use Include on queries or Get the entities separately?
In this case, you would be better off to include your Tickets entity in your Projects query. You are putting more work on the database, sure, but thats what the power of the DB and JOINs are there for.
As it stands, you are making two round trips to the database, and, you will most likely see a performance increase by including the child entity in the query.
Where you would really have trouble with not using an .Include() properly is fetching a collection of entities, and then looping through those entities and attempting to access a navigation property off that entity that hasn't been fetched; the infamous n+1 problem.

Net Core: Async method with ThenInclude Filter and Where

I am trying to use an Async method with ThenInclude filter and Where.
Its throwing this error. Think has it has something to do with this line:
LkDocumentTypeProduct = new Collection<LkDocumentTypeProduct>(dept.LkDocumentTypeProduct.Where(c => c.LkDocumentTypeId == lkDocumentTypeId).ToList())
and also skipping these lines below in my App Service: How would I make this ThenInclude Filter Async and fix the skipping of lines?
//ITS SKIPPING THESE THREE LINES BELOW
IEnumerable<Product> ProductModel = mapper.Map<IEnumerable<Product>>(Products);
var ProductDto = mapper.Map<IEnumerable<Product>, IEnumerable<ProductDto>>(ProductModel);
return ProductDto;
Error:
ArgumentException: Expression of type 'System.Collections.Generic.IAsyncEnumerable`1[Data.Entities.LkDocumentTypeProduct]' cannot be used for parameter of type
'System.Collections.Generic.IEnumerable`1[Data.Entities.LkDocumentTypeProduct]' of method 'System.Collections.Generic.List`1[Data.Entities.LkDocumentTypeProduct] ToList[LkDocumentTypeProduct]
(System.Collections.Generic.IEnumerable`1[Data.Entities.LkDocumentTypeProduct])'
Parameter name: arg0
Repository:
public async Task<IEnumerable<Product>> GetProductsByDocumentType(int lkDocumentTypeId)
{
var ProductsByDocumentType = All
.Include(dept => dept.LkDocumentTypeProduct)
.Select(dept => new Product
{
ProductId = dept.ProductId,
ProductName = dept.ProductName,
ProductCode = dept.ProductCode,
LkDocumentTypeProduct = new Collection<LkDocumentTypeProduct>(dept.LkDocumentTypeProduct.Where(c => c.LkDocumentTypeId == lkDocumentTypeId).ToList())
}).Where(dept=>dept.LkDocumentTypeProduct.Any());
return await ProductsByDocumentType.ToListAsync();
}
AppService:
public async Task<IEnumerable<ProductDto>> GetProductsByDocumentTypeId(int id)
{
var Products = await ProductRepository.GetProductsByDocumentType(id);
//ITS SKIPPING THESE THREE LINES BELOW !
IEnumerable<Product> ProductModel = mapper.Map<IEnumerable<Product>>(Products);
var ProductDto = mapper.Map<IEnumerable<Product>, IEnumerable<ProductDto>>(ProductModel);
return ProductDto;
}
Controller:
[HttpGet("[Action]/{id}")]
[ProducesResponseType(typeof(ProductDto), StatusCodes.Status200OK)]
[ProducesResponseType(StatusCodes.Status404NotFound)]
public async Task<ActionResult<IEnumerable<ProductDto>>> GetByDocumentType(int id)
{
IEnumerable<ProductDto> Product = await ProductAppService.GetProductsByDocumentTypeId(id);
if (Product == null)
{
return NotFound();
}
How to add where clause to ThenInclude
It's not "skipping" lines, it's erroring on this line:
IEnumerable ProductModel = mapper.Map>(Products);
Automapper is a helper, not a magician. :)
From what I'm guessing you had a method that was returning Product entities that you've been told to change over to a DTO. Automapper can help you, in an Async way:
Firstly, ignore the fact that you're working with async collections. Automapper maps objects to one another really well, not collections, it works within collections.
In your case given your repository is returning IEnumerable<Product>, to map this to an IEnumerable<ProductDto> use this in your Service:
public async Task<IEnumerable<ProductDto>> GetProductsByDocumentTypeId(int id)
{
var products = await ProductRepository.GetProductsByDocumentType(id);
var dtos = await products.Select(x => Mapper.Map<ProductDto>(x)).ToListAsync();
return dtos;
}
That should get you working, but isn't ideal. The reason is that the repository is returning back a collection of Entities which means that we'd be materializing all fields in each of those entities returned, whether we need them or not. This also opens the door if Automapper "touches" any related entities that haven't been eager loaded as this will trigger lazy-load calls to the database. You may cater for this with Include, but as code matures these lazy load hits can sneak in or you have eager load costs for fields you no longer need.
Automapper offers a brilliant method to address this, ProjectTo but it requires code to leverage EF's IQueryable implementation rather than returning IEnumerable.
For instance if we change the repository method to:
public IQueryable<Product> GetProductsByDocumentType(int lkDocumentTypeId)
{
var query = _dbContext.Products
.Where(p => p.LkDocumentTypeProduct.Any(c => c.LkDocumentTypeId == lkDocumentTypeId));
return query;
}
Then in the service:
public async Task<IEnumerable<ProductDto>> GetProductsByDocumentTypeId(int id)
{
var dtos = await ProductRepository.GetProductsByDocumentType(id)
.ProjectTo<ProductDto>().ToListAsync();
return dtos;
}
What this does is change the repository to return an IQueryable, basically return a promise of retrieving a known set of entities that our consumer can query against. I would be a bit wary of the "All" property as to what that property returns. I've seen a few cases where methods like that do something like return _context.Products.ToList(); which is a real performance / resource trap.
We feed that Queryable to Automapper's provided extension method ProjectTo which then queries just the columns needed to satisfy the ProductDto. The advantages of this approach are considerable. We don't need to explicitly Include related tables or worry about tripping lazy loaded references, and the built query only pulls back the fields our DTO cares about.

How to do a search in two or more tables using Join with Entity framework Lambda and Linq

How would I do to search two or more tables.
In the system I'm doing, I have the Boat, TypeOfBoat, and ClassBoat tables.
My mapping is as follows:
HasRequired(c => c.ClassBoat )
.WithMany(c => c.Boat)
.HasForeignKey(c => c.ClassBoatId);
HasRequired(c => c.TypeOfBoat)
.WithMany(c => c.Boat)
.HasForeignKey(c => c.TypeOfBoatId);
I made a generic repository for searching
public virtual IEnumerable<TEntity> Search(Expression<Func<TEntity, bool>> predicate)
{
return Dbset.Where(predicate);
}
And I use a generic Interface repository to do specific searches for boats, here are some examples below:
public IEnumerable<Boat> GetByActive()
{
return Search(c => c.Active && !c.excluded);
}
public Boat GetByName(string name)
{
return Search(c => c.Name== name).FirstOrDefault();
}
And if I want to fetch the TypeOfBoat and the ClassBoat, which are different tables, but which are related with the Boat table, how would I do?
My idea is to show this data in a table
Here's a SS how my Database is.
Database
Do you mean including related records for boat?
Try if this query gives you the expected result
public Boat GetByName(string name)
{
var boat = DbContext.Boat
.Include(boat => boat.ClassBoat)
.Include(boat => boat.TypeOfBoat)
.FirstOrDefault(boat => boat.Name == name);
return boat;
}
If you want to use generic repository and then extend query you should return IQueryable<T> instead of IEnumerable<T>.

How to convert early bound query to IQueryable<TEntity>

Assuming that AccountSet returns an IQueryable<Account>, how can I convert this early binding:
XrmServiceContext _xrmServiceContext;
var result = _xrmServiceContext.AccountSet.FirstOrDefault(x => x.Id == locationGuid);
Into something that is more reusable like:
_xrmServiceContext.Where(Set == "Account").FirstOrDefault(x => x.Id == locationGuid);
AccountSet is defined as:
public System.Linq.IQueryable<Xrm.Account> AccountSet
{
get
{
return this.CreateQuery<Xrm.Account>();
}
}
How do I generalize this to be reusable for any IQueryable member of XrmServiceContext ?
As long as the Id is the primary key you can use that in conjunction with find to make a generic call
public T Get<T>(object[] Id)
where T : class
{
return _xrmServiceContext.Set<T>().Find(Id )
}
if your looking for an IQueryable that may be a bit harder but you can find out how to do that off of a previous question I asked:
Entity Framework Filter By PrimaryKey
Courtesy of this post, I've found the following solution:
private static T EntityGet<T>(Guid id)
where T : Entity
{
T item =
(
from query in Context.CreateQuery<T>()
where query.Id == id
select query
).Single();
return item;
}

Entity get relations in one query and cannot get in another

public ActionResult Users()
{
var user = _userRepository.Find(x => x.Id == 1).FirstOrDefault();
return View(user);
}
public ActionResult CashAccountInfo(int id)
{
var result = _cashAccountRepository.Find(x => x.Id == id).FirstOrDefault();
return View(result);
}
When i debug Users() i get
where i have AppUser
but when i debug CashAccountInfo(int id)
i get
without AppUser
Find method from Repository
public virtual IEnumerable<TEntity> Find(Expression<Func<TEntity, bool>> predicate)
{
return DbContext.Set<TEntity>().AsExpandable().Where(predicate).ToList();
}
Please help why in one query get entity but in another cannot get
Assuming you are using entity framework, this is a Lazy Loading issue. Maybe add an Include to your calls when using your context.
For example:
context.Include(x => x.AppUser).FirstOrDefault(x => x.Id == id);
If this isn't the case or you're not using entity framework then you need to show the code for your repository methods.

Categories

Resources