GetAll groupjoin that populates list - c#

I have a GetAll statement that grabs every person and populates the collection of tickets they have. My classes are abbreviated as follows:
public class Person
{
public int Id {get;set;}
public string Name {get;set;}
public ICollection<Ticket> Tickets {get;set;}
}
public class Ticket
{
public int Id {get;set;}
public int PersonId {get;set;}
public string MovieName {get;set;}
}
I'm using entity framework to generate the classes with linq from the dbcontext to populate the request.
public async Task<List<Person>> GetPersonsAsync()
{
return await _context.Person.GroupJoin(_context.Ticket,
p => p.Id,
c => c.PersonId,
(per, tix) => PopulatePerson(per,tix) )
.ToListAsync();
}
private Person PopulatePerson(Person per, IEnumerable<Ticket> tix)
{
per.Tickets= tix.ToList();
return per;
}
This works but I had to create the seperate private PopulatePerson method in order to accomplish this. How can I avoid creating a private method and populate the list of tickets in the same GroupJoin statement?

You can use Include() and let EntityFramework do the Joins for you:
public async Task<List<Person>> GetPersonsAsync()
{
return await _context.Person
.Include(p => p.Tickets)
.ToListAsync();
}

Related

Is there a better way to include additional fields/columns in EF Core without loading the whole navigation property

Is there a better way to include additional fields/columns in EF Core without loading the whole navigation property
i have two entities say Employee and Department.
public class Employee
{
public int Id { get; set; }
public string Name { get; set; }
public int DepartmentId { get; set; }
public string DepartmentName { get; set; }
[JsonIgnore]
public Department Department { get; set; }
}
public class Department
{
public int Id { get; set; }
public string Name { get; set; }
// ... more fields.
}
i need to return list of Employees to the client, but i dont want to include the whole Department entity which is only for server side process, except the DepartmentId and DepartmentName.
i have two solutions.
Solution 1 is to use calculated property and navigation property.
public class Employee
{
// ...
public string DepartmentName
{
get
{
if (Department != null)
{
return Department.Name;
}
else
{
return null;
}
}
}
}
// Query for department name but also have to include all fields.
public static List<Employee> GetEmployees(MyDbContext context)
{
return context.Employees.Include(e => e.Department).ToList();
}
solution 2 is to use dynamic joining.
public class Employee
{
// ...
[NotMapped]
public string DepartmentName { get; set; }
}
// Query for department name without navigation property.
public static List<Employee> GetEmployees(MyDbContext context)
{
Func<Employee, Department, Employee> map = (em, dept) =>
{
em.DepartmentName = dept.Name;
return em;
};
var query = from em in context.Employees
join dept in context.Departments on em.DepartmentId equals dept.Id
select map(em, dept);
return query.ToList();
}
but if there 're more foreign fields to include, the linq query will be tedious. Another problem is, this is not generic way for arbitrary entity, and i have more entities to implement like above.
i want to know if there's any other elegent implementation or official way.
For methods which are running within the scope of a DbContext, as appears to be your case by passing through the DbContext instance, I would recommend having them return IQueryable<TEntity> rather than List<TEntity>. In this way the consumers can further refine the query as they see fit. This includes projecting the entity structure into what is needed, (Needing just a DepartmentName) handling things like sorting, pagination, etc. This is commonly the case when developing a Repository pattern.
public class Employee
{
public int Id { get; set; }
public string Name { get; set; }
public virtual Department Department { get; set; }
}
public class Department
{
public int Id { get; set; }
public string Name { get; set; }
// ... more fields.
}
// Then somewhere in a Repository class...
public IQueryable<Employee> GetEmployees(MyDbContext context)
{
return context.Employees.AsQueryable();
}
The first question that usually comes up is "what's the point? why not just use the DbContext?". There can be two good reasons for implementing a repository pattern like this. Firstly to enable unit testing. Mocking a DbContext is "messy", where-as mocking a dependency that has a method that returns IQueryable<Employee> is easy. The other reason is that systems often have low level rules to enforce, such as systems that use soft-delete (i.e. IsActive=false rather than deleting rows) or multi-tenancy. (I.e. employees for multiple ClientId that access the system) Repositories can serve as excellent boundaries to ensure these low-level rules are enforced.
public static IQueryable<Employee> GetEmployees(MyDbContext context, bool includeInactive = false)
{
var clientId = ClientService.ResolveCurrentUserClient();
var query context.Employees.Where(x => x.ClientId == clientId);
if(!includeInactive)
query = query.Where(x => x.IsActive);
return query;
}
In the above example the ClientService would be a dependency that is configured to check the current user and their association to a tenancy client to filter data by, then optionally filter out only active rows.
If you're not planning on implementing unit tests and have no low level rules to enforce, then my recommendation would be to just use the DbContext than adding a layer of abstraction.
When it comes to methods that return data outside of the DbContext's scope, the best solution I can recommend is to use projection to return a data object (View Model or DTO) rather than an entity. The view model can represent only the data your consumer needs and you can either leverage Select or Automapper's ProjectTo to populate it directly from an IQueryable resulting in a minimum sized payload and maximum performance.
[Serializable]
public class EmployeeViewModel
{
public int Id { get; set; }
public string Name { get; set; }
public int DepartmentId { get; set; }
public string DepartmentName { get; set; }
public static MapperConfigurationExpression BuildMapExpression(MapperConfigurationExpression expression = null)
{
if (expression == null)
expression = new MapperConfigurationExpression();
expression.CreateMap<Employee, EmployeeViewModel>()
.ForMember(x => x.DepartmentId, opt => opt.MapFrom(src => src.Department.Id))
.ForMember(x => x.DepartmentName, opt => opt.MapFrom(src => src.Department.Name));
return expression;
}
}
Then in the controller action or other method that would serialize our Employee & Department info:
public ViewResult List()
{
using(var context = new MyDbContext())
{
var config = new MapperConfiguration(EmployeeViewModel.BuildMapExpression());
var employees = EmployeeRepository.GetEmployees(context)
.ProjectTo<EmployeeViewModel>(config)
.ToList(); // Consider pagination /w Skip/Take
return View(employees);
}
}
So rather than serializing an Employee entity and worrying about the serializer lazy loading data or having a bunch of #null references, the method packaging up the data would project the resulting IQueryable down into a DTO or ViewModel class that is safe for serialization. This produces highly efficient queries for both performance and memory use. I cover off why to avoid sending entities beyond the scope of their DBContext and why they should always be considered complete, or complete-able in my response to this question:
What's the real difference between EntityState.Deleted and Remove() method? When to use each of them?
If I need to load only certain properties I usually go this way . Add to employee class extra property DepartmentName and add attribute [NotMaped] (or Ignore in fluent)
public class Employee
{
public int Id { get; set; }
public string Name { get; set; }
.......
[NotMapped]
public string DepartmentName { get; set; }
}
action
public static List<Employee> GetEmployees(MyDbContext context)
{
return context.Employees
.Select(e=> new Employee
{
Id=e.Id,
Name=e.Name,
DepartmentName=e.Department.Name
}).ToList();
}
I don't like to use mapper, it is very limited, so if I need to include lots of properties I usually to this
public static List<Employee> GetEmployees(MyDbContext context)
{
var employees= context.Employees
.Include(i=> Department)
.ToList();
foreach(var item in employees)
{
item.DepartmentName=item.Department.Name
.....
..... another calculated fiels if needed
item.Department=null;
}
return employees;
}

lambda expression to select single row from parent table where join table object value contains specific object

I apologize if this is a duplicate, but I searched and can not find my scenario.
I have a "parent" table called Tournaments and a Players table which houses participants of tournaments. This is a many to many relationship because I can have multiple tournaments and the same players (or more or less) can participate in each tournament.
I have created a join table for the purpose of allowing my many-to-many relationship.
my objects look like this:
TournamentEntity.cs
public class TournamentEntity : IInt32Identity
{
public int Id { get; set; }
...
public List<TournamentPlayers> Participants { get; set; }
}
TournamentPlayers.cs (this is my join entity)
public class TournamentPlayers
{
public int TournamentId { get; set; }
public TournamentEntity Tournament { get; set; }
public int PlayerId { get; set; }
public PlayerEntity Player { get; set; }
}
PlayerEntity.cs
public class PlayerEntity : IInt32Identity
{
public int Id { get; set; }
...
public List<TournamentPlayers> Participants { get; set; }
}
I would like to return a list and/or a single instance of TournamentEntity with a simple lambda expression from a single PlayerEntity passed into my method.
example method:
public async Task<TournamentEntity> GetTournamentByTournamentParticipant(PlayerEntity tournamentParticipant)
{
return await EntityDbSet
.Where(x => x.Participants.Contains) // this is where I'm a bit lost on how to link to "tournamentParticipant"
...
.Include(x => x.Participants)
.ThenInclude(b => b.Player)
.FirstOrDefaultAsync();
}
thanks in advance.
Have you tried this
return await EntityDbSet
.Where(x => x.Participants.Any(p => p.PlayerId == tournamentParticipant.Id))
.Include(x => x.Participants)
.ThenInclude(b => b.Player)
.FirstOrDefaultAsync();

Join and get Distinct on Foreign Objects/Keys Linq

I have a Model like this
public class Product
{
public int Id { get;set;}
...
public virtual ICollection<Supplier> Suppliers {get;set;}
}
public class Suppliers
{
public int Id {get;set;}
...
public string Name {get;set;}
}
My Linq query is constructed like this to get Products which are unique
var suppliers = _context.Products.Where(condition).Select(u => u.Suppliers).ToList(); //
The result of this query returns a List<Iqueryable<Supplier>> How do I get a list of Supplier Names from this list??
Thanks to Camilo Terevinto this query works.
var suppliers = _context.Products.Where(condition).SelectMany(u => u.Suppliers).GroupBy(u => u.Name).Select(u => u.First()).ToList()
You can try this
var supplier_names = _context.Products.Where(condition)
.SelectMany(u => u.Suppliers).Select(a => a.Name).ToList();

Entity Framework 5.0 code first one to one and one to many relationship

I have two entities- Product and Picture. The idea is that a product can have only one picture or none and a picture can be mapped to many products.
public class Product
{
public int Id {get; set;}
public Picture Picture { get; set; }
public bool Enabled { get; set; }
public string Text { get; set; }
}
The Picture entity is a mapping to another Pictures entity that contains all of the pictures (this Picture entity contains a mapping only for the pictures for the products), so basically it contains only two columns with Ids.
public class Picture
{
public int Id {get; set;}
public int PictureId { get; set; }
}
This is my model binder for the Product entity
modelBuilder.Entity<Product>().HasOptional<Picture>(pr => pr.Picture);
When I create a Product entity with picture, or add a picture to an existing entity the Picture_Id column that is created in the Product table is correctly populated with the Id from the Picture table. The problem is when I try to retrieve the Product. The Picture entity inside the Product entity is null. I suspect that the mapping is incorrect. Can you tell me how to populate the Picture entity inside the Product entity when I retrieve it. Here is the retrieval code:
public Product GetProductById(int productId)
{
var query = from pr in _productRepository.Table
where pr.Id == productId
select pr;
return query.FirstOrDefault();
}
EDIT: I am using custom IRepository service that I include with dependency injection
IRepository<Product>
The IRepository interface contains the following methods:
void Insert(T entity);
void Update(T entity);
void Delete(T entity);
IQueryable<T> Table { get; }
This is the implementation of Table:
public virtual IQueryable<T> Table
{
get
{
return this.Entities;
}
}
private IDbSet<T> Entities
{
get
{
if (_entities == null)
_entities = _context.Set<T>();
return _entities;
}
}
I can not add .Include("Picture") to the Table, because it is IQueryable.
I also enabled the Lazy Loading, but there were no result.
var products = db.Pictures.Include("Product");
public Product GetProductById(int productId)
{
var query = from pr in products
where pr.Id == productId
select pr;
return query.FirstOrDefault();
}
you should use include for loading related entities :
public Product GetProductById(int productId)
{
using (var db = new ProductContext())
{
var query = from pr in db.Products.Include("Picture")
where pr.Id == productId
select pr;
return query.FirstOrDefault();
}
}
I fixed the problem. Not sure if this is the correct way, but it works.
I did the following changes:
public class Product
{
public int? PictureId { get; set; }
public int Id {get; set;}
public Picture Picture { get; set; }
public bool Enabled { get; set; }
public string Text { get; set; }
}
modelBuilder.Entity<Product>().HasOptional<Picture>(pr => pr.Picture).WithMany().HasForeignKey(pr => pr .PictureId);
public Product GetProductById(int productId)
{
var query = from pr in _productRepository.Table
where pr.Id == productId
select pr;
var product = query.FirstOrDefault();
AddProductPicture(product);
return product;
}
private void AddProductPicture(Product product)
{
var query = from pic in _pictureRepository.Table
where pic.Id == product.PictureId
select pic;
product.Picture = query.FirstOrDefault();
}

Entity Framework - Selective Condition on Included Navigation Property

Assume I have these simplified EF generated entities...
public class PurchaseOrder
{
public int POID {get;set;}
public int OrderID {get;set;}
public int VendorID {get;set;}
public IEnumerable<Order> Orders {get;set;}
}
public class Order
{
public int OrderID {get;set;}
public decimal Price {get;set;}
public IEnumerable<Item> Items {get;set;}
}
public class Item
{
public int OrderID {get; set;}
public string SKU {get;set;}
public int VendorID {get;set;}
public Order Order {get;set;}
}
Business Logic:
An order can have multiple POs, one for each distinct vendor on the order (vendors are determined at the Item level).
How Can I selectively Include Child Entities?
When querying for POs, I want to automatically include child entites for Order and Item.
I accomplish this, using Include()...
Context.PurchaseOrders.Include("Orders.Items");
This does it's job and pulls back related entities, but, I only want to include Item entities whose VendorID matches the VendorID of the PurchaseOrder entity.
With traditional SQL, I'd just include that in the JOIN condition, but EF builds those internally.
What LINQ magic can I use tell EF to apply the condition, without manually creating the JOINs between the entities?
You can't selectively pull back certain child entities that match a certain condition. The best you can do is manually filter out the relevant orders yourself.
public class PurchaseOrder
{
public int POID {get;set;}
public int OrderID {get;set;}
public int VendorID {get;set;}
public IEnumerable<Order> Orders {get;set;}
public IEnumerable<Order> MatchingOrders {
get {
return this.Orders.Where(o => o.VendorId == this.VendorId);
}
}
}
You can't. EF doesn't allow conditions for eager loading. You must either use multiple queries like:
var pos = from p in context.PurchaseOrders.Include("Order")
where ...
select p;
var items = from i in context.Items
join o in context.Orders on new { i.OrderId, i.VendorId}
equals new { o.OrderId, o.PurchaseOrder.VendorId }
where // same condition for PurchaseOrders
select i;
Or you can use projection in single query:
var data = from o in context.Orders
where ...
select new
{
Order = o,
PurchaseOrder = o.PurchaseOrder,
Items = o.Items.Where(i => i.VendorId == o.PurchaseOrder.VendorId)
};
You could use the IQueryable-Extensions here:
https://github.com/thiscode/DynamicSelectExtensions
The Extension builds dynamically an anonymous type. This will be used for projection as described by #Ladislav-Mrnka.
Then you can do this:
var query = query.SelectIncluding( new List<Expression<Func<T,object>>>>(){
//Example how to retrieve only the newest history entry
x => x.HistoryEntries.OrderByDescending(x => x.Timestamp).Take(1),
//Example how to order related entities
x => x.OtherEntities.OrderBy(y => y.Something).ThenBy(y => y.SomeOtherThing),
//Example how to retrieve entities one level deeper
x => x.CollectionWithRelations.Select(x => x.EntityCollectionOnSecondLevel),
//Of course you can order or subquery the deeper level
//Here you should use SelectMany, to flatten the query
x => x.CollectionWithRelations.SelectMany(x => x.EntityCollectionOnSecondLevel.OrderBy(y => y.Something).ThenBy(y => y.SomeOtherThing)),
});

Categories

Resources