If have the following table structures
Key Site Building Person Grade
Primary key SiteId BuildingId PersonId GradeId
Foreign key SiteId BuildingId
GradeId
Knowing the SiteId, I am looking to obtain a list of Person objects which have a set grade.
I am using the uof and repository patterns and the code used to pull out the data is as follows:
var site = unitOfWork.Repository<Site>()
.Query(si => si.SiteId == siteId)
.Select()
.FirstOrDefault();
What I would like to do is something like:
List<Person> person = Person.Where(p => p.Site.SiteId == 99
&& p.Site.Building.person.Grade == 3);
but I cannot see site from person or grade objects in intellisence.
I have tried to include the tables, but again, intellisence does not allow me to dig deeper then the next object, so
var site = unitOfWork.Repository<Site>()
.Query(si => si.SiteId == siteId)
.Include(si => si.Building)
.Include(si => si.Building.Person) ..... not visible
.Select()
.FirstOrDefault();
What do i need to change to either enable me to:
pull back the extra table objects in the initial query
traverse the objects using intellisence
using existing code to pull a person object
var person = Person.Where(p => p.Building.SiteId == 99 && p.GradeId == 3).ToList();
Related
I was trying to select data using LINQ
and I have a list called "products" and I want just these items that exist in products list
var Owner = db.Owners
.Where(m => m.ID == id)
.Include(m => m.Products.Where(item1 => products.Any(item2 => item2.ProductID == item1.ProductID)).ToList())
.FirstOrDefault();
but I'm getting this error :
System.ArgumentException: 'The Include path expression must refer to a navigation property defined on the type. Use dotted paths for reference navigation properties and the Select operator for collection navigation properties.
Parameter name: path'
Include is meant to fetch complete rows of a table, inclusive primary and foreign keys.
Usually it is not efficient to fetch the complete rows of a table. For example, suppose you have a database with Schools and Students. There is a one-to-many relation between Schools and Students: every School has zero or more Students, every Student attends exactly one School, namely the School that the foreign key refers to.
If you fetch School [10] with its 2000 Students, then every Student will have a foreign key SchoolId with a value 10. If you use Include and fetch complete Student rows you will transfer this value 10 over 2000 times. What a waste of processing power!
A DbContext has a ChangeTracker object. Whenever you fetch data without using Select, so if you fetch complete rows, then the fetched rows are stored in the ChangeTracker, together with a Clone of it. You get a reference to the Clone (or the original, doesn't matter). When you change properties of the fetched data, you change the value in the Clone. When you call SaveChanges, the values of all properties of all originals in the ChangeTracker are compared with the values in the Clones. The items that are changed are updated in the database.
So if you fetch School [10] with its 2000 Students, you are not only fetching way more data than you will ever use, but you will also store all these Students in the ChangeTracker together with a Cloned Student. If you call SaveChanges for something completely different (change of the telephone number of the School for instance), then all Students are compared by value property by property with their Clones.
Generic rule:
Whenever you fetch data using Entity Framework, always use Select, and Select only the properties that you actually plan to use. Only fetch complete rows and only use Include if you plan to update the fetched data.
Using Select will also solve your problem:
int ownerId = ...
IEnumerable<Product> products = ...
var Owner = db.Owners.Where(owner => owner.ID == ownerId)
.Select(owner => new
{
// Select only the Owner properties that you actually plan to use
Id = owner.Id,
Name = owner.Name,
// get the Products of this Owner that are in variable products
Products = owner.Products
.Where(product => products.Any(p => p.ProductId == product.ProductId)
.Select(product => new
{
// Select only the Product properties that you plan to use
Id = product.Id,
Price = product.Price,
...
// No need to fetch the foreign key, you already fetched the value
// OwnerId = product.OwnerId,
})
.ToList(),
...
})
.FirstOrDefault();
I used automatic types (new {...}). If you really want to create Owner and Properties, use:
var Owner = db.Owners.Where(...)
.Select(owner => new Owner
{
Id = owner.Id,
...
Products = owner.Products.Where(...).Select(product => new Product
{
Id = product.Id,
...
})
.ToList(),
})
.FirstOrDefault();
Try the following:
var productIds = products.Select(x => x.ProductID);
var Owner = db.Owners
.Where(m => m.ID == id)
.Include(m => m.Products.Where(product => productIds.Contains(product.ProductID))
.FirstOrDefault();
Is there any better way to merge a Entity Table with a Entity View
A little example: I have a Person table with:
id,
name,
lastname
columns and a view named ViewPersonLastLocations with:
person_id
location_name.
I need to display Person table with the information of ViewPersonLastLocations.
Actually i can "merge" those entities with two foreachs, and i create a variable in the Person partial class.
Is there any other way to do this?
I am a little unclear on what you want based on your last comment, but will start with this code for a join if the relationship is 1:1. If it is 1:Many, then it is similar, but project into a collection.
var personWithLocation = context.Persons
.SelectMany(p => context.ViewPersonLastLocations
.Where(vp => vp.person_id == p.id)
.DefaultIfEmpty(),
(p, vp) => new PersonViewModel // create a viewmodel for results or anonymous
{
Id = p.id,
Name = p.name,
LastName = p.lastname,
LocationName = vp.location_name
}
).ToList();
I have two tables - "Customer" table and "Blacklist" customer table. When I blacklist a customer, I put the customerid as a foreign key to Blacklist table.
What I want is to get CusId and Name that are not in the BlackList Table.
How can I code this Entity Framework C#?
Customer
---------
(CusId,Name,Telephone,Email)
Blacklist
---------
(CusId)
What you want is something like the following:
db.Customers
.Where(c => !db.Blacklists
.Select(b => b.CusId)
.Contains(c.CusId)
);
EF will happily turn that into a sub-query that will run quite well.
This pattern works for static lists (creates an IN(a, b, c) expression) as well as other tables. You can use it to check for being in the list or not in the list.
If you want to test it and see the SQL it generates, I strongly recommend LINQPad (it is free). That's what I use to test out little ideas in LINQ all the time.
How about something like this:
var subselect = (from b in BlackList select b.CusId).ToList();
var result = from c in Customer where !subselect.Contains(c.CusId) select c;
Any() is the best performance :
db.Customers.Where(c => !db.Blacklists.Any(b => b.CusId == c.Id));
If you also need columns of Blacklists , use join with condition : b.CusId == c.Id
IF in table BlackList yo have a List of Customer You can perform something like
IEnumerable<Customer> model = (from c in db.Customer
from b in c.BlackList.DefaultIfEmpty()
where b.CusID== null
select new Customer
{
CusId= c.OrderID
}).ToList();
This query I used and it works perfectly:
var query = _context.Customer.Where(x => x.Id == id && !x.BlackList.Any(z => x.Id == x.CustId)).ToList();
So I am using EF, I have the following entities:
Website
Sector
Product
Attribute
AttributeTag
The relationships are as follow:
I need to retrieve something that is not directly linked to the table. Such as products which needs a Sector object in order to retrieve only the specific Products using something such as sector.Products.
But what if I need to retrieve all Products under a given Website instead of it's parent Sector ?
In my specific situation, my questions are:
1) How can I retrieve all the products given a specific website_id - (Disregarding the sector)
2) How can I retrieve all the products that have a specific tag_id + website_id. (Also retrieve it's corresponding Attribute)
Help is appreciated. Thanks!
Assuming you've got both side navigation properties :
you will have a List<Sector> SectorList in Product Entity.
you will have a List<Product> ProductList in Sector Entity.
(sectors_products won't be present as an entity, as it's not needed in an object world).
you will have a Website Website in Sector Entity
you will have a List<AttributeTag> AttributeTagList in Product Entity;
(products_tags won't be present as an entity, as it's not needed in an object world).
1) something like :
var result = ProductEntities
.Where(p => p.SectorList
.Any(s => s.WebSite.Id == <your_website_id>)
);
2) something like (taking 1) as base query)
result = result
.Where(p => p.AttributeTagList
.Any(at => at.Id == <your_tag_id>)
);
or all in one
var result = ProductEntitites
.Where(p =>
p.SectorList.Any(s => s.WebSite.Id == <your_website_id>) &&
p.AttributeTagList.Any(at => at.Id == <your_tag_id>)
);
The relationships in your schema form a pathway. If you want to figure out the relationships between two entity sets, you have to follow that pathway and query all the entities in between.
var part1 = (from w in Websites
from s in Sectors
from p in s.Products
where s.Website equals w
&& w.website_id equals web_id
select p).Distinct();
var part2 = from p in part1
let attr = p.Attributes.Where(a => a.tag_id + web_id == target_val)
where attr.Any()
select new { p, attr };
If I understand your schema correctly, that should pull down the data to answer both parts of your question.
I have this classic scenario where I have a User table and a Contact table containing only UserId and ContactId columns (so it is a self many to many relationshsip). What I would like is a query that gives me a list of userIds with number of common contacts with the specified User. In plain old SQL I have the following query (contacts of user and user itself is filtered out to get facebook like friend suggestions):
SELECT COUNT(c1.ContactId) as CommonContact, c2.UserId
from Contacts as c1
inner join Contacts as c2 on c1.ContactId = c2.ContactId
Where c1.UserId = #Id AND c2.UserId != #Id
AND c2.UserId NOT IN (SELECT ContactId from Contacts Where UserId = #Id)
Group By c2.UserId
ORDER BY CommonContact Desc
This simple query works great but I can not figure out how to write the same query in LINQ to Entity, because in the Entity Framework model I have User entity that entity have Contact navigation property but the connection table is not there directly....
Thanks a lot for any help...
Didn't have time and try to run it but something like this should work.
public class Test
{
//simulate an IQueryable
private readonly IQueryable<Person> _people = new List<Person>().AsQueryable();
public void FindContactMatchCount(Guid personId)
{
//we'll need the list of id's of the users contacts for comparison, we don't need to resolve this yet though so
//we'll leave it as an IQueryable and not turn it into a collection
IQueryable<Guid> idsOfContacts = _people.Where(x => x.Id == personId).SelectMany(x => x.Contacts.Select(v => v.Id));
//find all the people who have a contact id that matches the selected users list of contact id's
//then project the results, this anonymous projection has two properties, the person and the contact count
var usersWithMatches = _people
.Where(x => idsOfContacts.Contains(x.Id))
.Select(z => new
{
Person = z, //this is the person record from the database, we'll need to extract display information
SharedContactCount = z.Contacts.Count(v => idsOfContacts.Contains(v.Id)) //
}).OrderBy(z => z.SharedContactCount)
.ToList();
}
}