Querying a Single Item in a List - c#

Problem
I want to show a single pet on my details page. I'm having trouble understanding how to write LINQ(Method Syntax) to pull a single pet/item from the database.
My understanding so far
When working with LINQ, it's shining purpose is to be able to loop through an IEnumerable array or list in the database. As you look in my HomeController, I feel fairly confident that I have the right pieces of code to build what I want MINUS the query.
Request
Using method syntax, what query should I be using to pull a single pet?
HomeController.cs
[HttpGet("pet/{PetId}")]
public IActionResult Detail(Pet singlePet)
{
List<Pet> Animal = _context.Pets
//***MISSING QUERY CODE HERE***
return View("Pet", singlePet);
}
Details.cshtml
#model PetViewModel
<div>
<h1>Pet Shelter</h1>
#foreach (Pet creature in Model.Pets)
{
<h3>Details about: #creature.Name</h3>
<div>
<p>Pet Type: #creature.Type</p>
<p>Description: #creature.Description</p>
<p>Skill One:#creature.Skill1</p>
<p>Skill Two:#creature.Skill2</p>
<p>Skill Three:#creature.Skill3</p>
</div>
}
</div>
PetViewModel.cs
using System.Collections.Generic;
namespace petShelter.Models
{
public class PetViewModel
{
public Pet Animal { get; set; }
public List<Pet> Pets { get; set; }
public List<Owner> Owners { get; set; }
public Owner Owner { get; set; }
}
}
Pet.cs
using System;
using System.ComponentModel.DataAnnotations;
namespace petShelter.Models
{
public class Pet
{
[Key]
public int PetId { get; set; }
[Required]
public string Name { get; set; }
[Required]
public string Type { get; set; }
[Required]
public string Description { get; set; }
public string Skill1 { get; set; }
public string Skill2 { get; set; }
public string Skill3 { get; set; }
public DateTime CreatedAt { get; set; }
public DateTime UpdatedAt { get; set; }
public Owner Owner { get; set; }
public int? OwnerId { get; set; }
}
}

Short Version
Use
var singlePet=_context.Pets.Find(someId);
Or
var singlePet=_context.Pets.FirstOrDefault(x=>x.PetId=someId);
Explanation
LINQ queries don't query lists. They're translated by a LINQ provider to whatever the target data storage understands. When querying a database, they're translated to SQL.
List<Pet> Animal = _context.Pets won't compile, which is a very good thing - if it did, it would load the entire table in memory instead of querying it. Filtering will be performed on the client, without the benefit of indexing. Performance will be orders of magnitude worse than a simple SQL query due to wasted IO, wasted RAM and even worse, taking locks on every row in the table when only one was needed. Excessive locks will cause delays for every other request that tries to use the same table.
Pets is a DbSet<T>, not a List<T>. It's not a container, it represents an entity without holding any data itself. It can be used to write queries that translate to SQL.
Assuming PetId is the primary key for Pet, the easiest and fastest way to load a single record is calling DbSet.Find. This method retrieves an object using the supplied primary key values.
This query:
var singlePet=_context.Pets.Find(someId);
translates to
select ...
from Pets
where PetID=#id
This will load and cache a Pet instance inside DbContext. After that, every time Find is called with the same PK value during the same request, the cached object will be returned.
The query
var singlePet=_context.Pets.FirstOrDefault(x=>x.PetId=someId);
Will translate to
select top 1 ...
from Pets
where PetID=#id
Nothing is cached in this case.

If you don't care and just want a single result from the database you can do something like this
var singlePet = _context.Pets.First();
If you want the first result that matches an expression you can do something like this
var singlePet = _context.Pets.First( e => e.Id == '1');
If you are expecting ONLY a single result that matches an expression you can do something like this
var singlePet = _context.Pets.Single( e => e.PetId == '1' );

If you want just one, with no filter at all on which, you can use this, it will get you the first on the list:
var aSpecificPet = Pets.FirstOrDefault();
If you need to specify some kind of condition to determine which pet to bring, you can use something like:
var aMoreSpecificPet = Pets.FirstOrDefault(x=>x.Type == "Dog");
Take in mind that whatever you pass as a condition will filter the Pets list, and with FirstOrDefault you will get the first (if there are any) pets that match your criteria.
Also, default value is usually null (in case no pets match your criteria), so its a good idea to always add a line like
if(pet != null){
your code
}
If you want all pets that match that criteria, use:
var allDogsInPets = Pets.Where(x=>x.Type == "Dog").ToList();
The .ToList() returns a new list with the pets that match your criteria.

Related

Filter list of entity objects by string child object property using Linq Lambda

I am trying to return an IQueryable lands filtered by a child object property Owner.Name. Is working well with the query style solution, but I want to use a lambda one.
On short these are my classes mapped by EntityFramework:
public class Land
{
public int Id { get; set; }
public virtual ICollection<Owner> Owners { get; set; }
}
public class Owner
{
public int Id { get; set; }
public string Name { get; set; }
public int LandId { get; set; }
public virtual Land Lands { get; set; }
}
The query which is working fine:
var list = from land in db.Lands
join owner in db.Owners on land.Id equals Owner.LandId
where owner.Name.Contains("Smit")
select land;
I was trying using this:
var list = db.Lands.Where(lnd => lnd.Owners.Count() > 0 &&
lnd.Owners.Where(own => own.Name.Contains("Smit")).Count() > 0);
It works only for small lists, but for some with thousands of records it gives timeout.
Well, one issue which may be causing the speed problem is that your lambda version and your non-lambda versions do very different things. You're non lambda is doing a join with a where on one side of the join.
Why not just write the lambda equivalent of it?
var list = db.Lands.Join(db.Owners.Where(x=> x.Name.Contains("Smit")), a=> a.Id, b => b.LandId, (a,b) => a).toList();
I mean, that is the more direct equivalent of your non lambda
I think you can use this one:
var list = db.Lands.Where(lnd => lnd.Owners.Any(x => x.Name.Contains("Smit")));
Try something more straightforward:
var lands = db.Owners.Where(o => o.Name.Contains("Smit")).Select(o => o.Lands);
You just need to make sure that Owner.Name is not null and LINQ will do the rest.

Projecting an IEnumerable inside an Projected IQueryable making N Requests to the Database

I'm making an Asp.net Core Api and one of the Actions of the Controller i need to return an IQueryable of a DTO, but one of the properties is an IEnumerable of another DTO in a relationship one to many in the database model of EF.
For example:
public class Customer
{
public int Id { get; set; }
public string Name { get; set; }
public DateTime Birthday { get; set; }
public List<Order> Orders { get; set; }
}
public class Order
{
public int OrderNumber { get; set; }
public Customer Customer { get; set; }
}
And the DTO
public class CustomerDTO
{
public int Id { get; set; }
public string Name { get; set; }
public IEnumerable<OrderDTO> Orders { get; set; }
}
public class OrderDTO
{
public int OrderNumber { get; set; }
}
This is just a simple example because in my application there is alot more fields on each table and i cannot expose everything to the frontend application, that's why i'm using DTOs.
I'm using the Select to Project each element to the DTO, there is no problem there because i can see on the ASP.NET Core Web Server output that the system is only making one request to the database (to get the Customers), but the problem comes when i try to project the OrdersDTO inside the CustomerDTO. What's happening is for example if i have 100 customers the EF will make 101 requests to the database. (1 to get the Customers and 100 to get the Orders for each customer)
[HttpGet]
[EnableQuery]
public IEnumerable<CustomerDTO> Get()
{
return context.Customer
.Select(s => new CustomerDTO
{
Id = s.Id,
Name = s.Name,
Orders = s.Orders.Select(so => new OrderDTO
{
OrderNumber = so.OrderNumber
})
});
}
If i call ToList() before i project the elements using Select it will make only one request to the Database (as intended), but i need to return an IQueryable because i'm using OData, so that the frontend application can execute queries directly to the database even if is just a DTO
I already tried putting like this
Orders = s.Orders.Any() ? s.Orders.Select(so => new OrderDTO
{
OrderNumber = so.OrderNumber
}) : new List<OrderDTO>()
It solved the problem partially because if out of the 100 customers theres is only 50 that have orders the EF will only make 50 requests to the Database.
I would like to know if there is a solution to this problem because i don't want the application doing hundreds of queries to the database each time some user calls this endpoint of the API.
You need to add ToList() when projecting the inner collection.
Orders = s.Orders.Select(so => new OrderDTO
{
OrderNumber = so.OrderNumber
}).ToList() // <--
First because CustomerDTO.Orders property type is List<OrderDTO>, so the code does not compile w/o that.
But even it wasn't (let say it's IEnumerable<OrderDTO>), you still need ToList in order to get EF Core 2.1 introduced Optimization of correlated subqueries
:
We have improved our query translation to avoid executing "N + 1" SQL queries in many common scenarios in which the usage of a navigation property in the projection leads to joining data from the root query with data from a correlated subquery. The optimization requires buffering the results from the subquery, and we require that you modify the query to opt-in the new behavior.
Note the last sentence - "we require that you modify the query to opt-in the new behavior". Then documentation contains an example and continues with:
By including ToList() in the right place, you indicate that buffering is appropriate for the Orders, which enable the optimization

Many to Many in Linq using Dapper

I have Places, each place can have many tags. Each tag can be assigned to many places.
public class Place {
public int Id { get; set; }
public string PlaceName { get; set; }
public IEnumerable<Tag> Tags { get; set; }
}
public class Tag {
public int Id { get; set; }
public string TagName { get; set; }
}
public class TagPlace {
public int Id { get; set; }
public PlaceId { get; set; }
public TagId { get; set; }
}
The database has equivalent tables with foreign keys as appropriate.
I want to get a collection of Places, and I want each Place to have an appropriate colleciton of Tags. I guess using Linq might be required.
I've found various articles on this, but they aren't quite the same / deal with a list of ints rather than two collections of objects.
eg
https://social.msdn.microsoft.com/Forums/en-US/fda19d75-b2ac-4fb1-801b-4402d4bd5255/how-to-do-in-linq-quotselect-from-employee-where-id-in-101112quot?forum=linqprojectgeneral
LINQ Where in collection clause
What's the best way of doing this?
The classical approach with Dapper is to use a Dictionary to store the main objects while the query enumerates the records
public IEnumerable<Place> SelectPlaces()
{
string query = #"SELECT p.id, p.PlaceName, t.id, t.tagname
FROM Place p INNER JOIN TagPlace tp ON tp.PlaceId = p.Id
INNER JOIN Tag t ON tp.TagId = t.Id";
var result = default(IEnumerable<Place>);
Dictionary<int, Place> lookup = new Dictionary<int, Place>();
using (IDbConnection connection = GetOpenedConnection())
{
// Each record is passed to the delegate where p is an instance of
// Place and t is an instance of Tag, delegate should return the Place instance.
result = connection.Query<Place, Tag, Place(query, (p, t) =>
{
// Check if we have already stored the Place in the dictionary
if (!lookup.TryGetValue(p.Id, out Place placeFound))
{
// The dictionary doesnt have that Place
// Add it to the dictionary and
// set the variable where we will add the Tag
lookup.Add(p.Id, p);
placeFound = p;
// Probably it is better to initialize the IEnumerable
// directly in the class
placeFound.Tags = new List<Tag>();
}
// Add the tag to the current Place.
placeFound.Tags.Add(t);
return placeFound;
}, splitOn: "id");
// SplitOn is where we tell Dapper how to split the record returned
// in the two instances required, but here SplitOn
// is not really needed because "Id" is the default.
}
return result;
}

Execute Linq to SQL queries on Navigation properties in Model

Is it possible to make Statistics.Sum(s => s.Conversions) linq query as Linq to SQL and not Linq to Object like in this code below. Every time when I access TotalConversions property, the whole Statistics table downloaded from database and then SUM linq executed locally. I want to do that in database server as SQL.
public class User : Entity
{
public int Id { get; set; }
public string Email { get; set; }
public virtual ICollection<Statistic> Statistics { get; set; }
[NotMapped]
public int TotalConversions
{
get
{
return Statistics.Sum(s => s.Conversions);
}
}
}
Yes, but you need a reference to the DbContext. This is one of the costs of having the Entities be persistence-ignorant.
Then the property would look something like:
return db.Users.Single(s => s.Id = this.Id).Statistics.Sum(s => s.Conversions);

EF Code First improving performance for self referencing, one to many relationships

I have an AccountGroup which is a self-referencing entity. A leaf AccountGroup can contain 1 or more Accounts. Both entities have Balance property. Each AccountGroup has a Balance which is either a sum of Balances in sub-groups or sum of Balances of all Accounts (in case of leaf group).
In order to build a tree listing of all AccountGroups and Accounts I have to traverse this object graph recursively, which causes a lot (I mean a lot!!!) of calls to DB...
Is there any way to improve upon this in such way that # of DB calls is reduced?
Thanks
Here is the trimmed down code
Account (belongs to only 1 AccountGroup)
public class Account
{
public int Id { get; set; }
public int GroupId { get; set; }
public string Name { get; set; }
public decimal Balance { get; set; }
public string AccountType { get; set; }
public virtual AccountGroup Group { get; set; }
}
AccountGroup (has 0 or many AccountGroups, has 1 or more Accounts if it is a leaf)
public class AccountGroup
{
public AccountGroup()
{
Accounts = new HashSet<Account>();
Groups = new HashSet<AccountGroup>();
}
public int Id { get; set; }
public bool IsRoot { get { return Parent == null; } }
public bool IsLeaf { get { return !Groups.Any(); } }
public decimal Balance { get { return IsLeaf ? Accounts.Sum(a => a.Balance) : Groups.Sum(g => g.Balance); } } // if leaf group, get sum of all account balances, otherwise get sum of all subgroups
public int? ParentId { get; set; }
public string Name { get; set; }
public string Description { get; set; }
public virtual ISet<Account> Accounts { get; private set; }
public virtual ISet<AccountGroup> Groups { get; private set; }
public virtual AccountGroup Parent { get; set; }
}
Calling Code
// start processing root groups (ones without parent)
foreach (var rootGroup in db.AccountGroups.Include(g=>g.Groups).Where(g => g.ParentId == null))
{
TraverseAccountGroup(rootGroup, 0);
}
// recursive method
private static void TraverseAccountGroup(AccountGroup accountGroup, int level)
{
//
// process account group
//
Console.WriteLine("{0}{1} ({2})", String.Empty.PadRight(level * 2, '.'), accountGroup.Name, level);
//
// if subgroups exist, process recursivelly
//
if (accountGroup.Groups.Any())
{
foreach (var subGroup in accountGroup.Groups)
{
TraverseAccountGroup(subGroup, level + 1);
}
}
//
// otherwise, process accounts belonging to leaf subgroup
//
else
{
foreach (var account in accountGroup.Accounts)
{
Console.WriteLine("ACCOUNT [{0}]", account.Name);
}
}
}
CTE Approach
There are two ways to increase speed of queries against tree data types. The first (and likely easiest) is using a Stored Procedure and the execute sql functionality of EF to load the tree. The SProc will cache and the result set execution speed will be increased. My recommendation for the query in the sproc would be a recursive CTE.
http://msdn.microsoft.com/en-us/library/ms186243(v=sql.105).aspx
with <CTEName> as
(
SELECT
<Root Query>
FROM <TABLE>
UNION ALL
SELECT
<Child Query>
FROM <TABLE>
INNER JOIN <CTEName>
ON <CTEJoinCondition>
WHERE
<TERMINATION CONDITION>
)
Edit
Execute your sproc or CTE inline with:
DbContext ctx = new SampleContext();
ctx.Database.SqlQuery<YourEntityType>(#"SQL OR SPROC COMMAND HERE", new[] { "Param1", "Param2", "Etc" });
Flatten Your Tree Structure
The second approach is to build a flat representation of your tree. You can flatten a tree into a flat structure for quick querying and then use a linkage between the flat structure and the actual tree node to cut out the self referencing entity. You can build the flat structure using the above recursive CTE query.
This is just one approach but there are many papers on the subject:
http://www.governor.co.uk/news-plus-views/2010/5/17/depth-first-tree-flattening-with-the-yield-keyword-in-c-sharp/
EDIT: Adding additional clarification
Just a note, the Recursive CTE cache's the symbols for the query before iterating over the structure. This is the fastest and simplest way to write a query to solve your problem. However, this HAS to be a SQL query. You can use execute sql directly or you can execute a SProc. Sprocs cache the execution graph after being ran so they perform better than native queries that have to build an execution plan prior to running. This is entirely up to you.
The issue with a flat representation of your tree is you have to routinely rebuild or constantly upkeep the flat structure. Depending on your query path would determine what flattening algorithm you should use, but the end result remains the same. The flat structure is the only way to "accomplish" what you want to do inside EF without having to cheat and execute raw SQL through the DBConnection.

Categories