Linq Method syntax for retrieving single nested child entity - linq

I wonder if I could get some help with the following. I'm retrieving set of data as follows using EF.
var booking = this.GetDbSet<Booking>().Include(c => c.BookingProducts.Select(d => d.Product.PrinterProducts.Select(e => e.ProductPrices))).Single(c => c.BookingId == bookingId)
Within a PrinterProduct there is a foreign key PrinterId for an additional entity Printer. With the Booking Entity I also have PrinterId also linked by foreign key to the additonal entity Printer.
What I'm hoping to do is retrieve only the PrinterProduct relating to the PrinterId held in the booking entity rather that all the PrinterProducts as in my code. I've tried to use Join but have tied myself in knots!
Grateful for any help!
Edit:
Object structure:
public class Booking
{
public Guid BookingId { get; set; }
public string BookingName { get; set; }
public Printer Printer { get; set; }
public IEnumerable<BookingProduct> BookingProducts { get; set; }
}
public class BookingProduct
{
public int BookingProductId { get; set; }
public Booking Booking { get; set; }
public Product Product { get; set; }
}
public class Product
{
public int ProductId { get; set; }
public string ProductName { get; set; }
public IEnumerable<PrinterProduct> PrinterProducts { get; set; }
}
public class PrinterProduct
{
public int PrinterProductId { get; set; }
public Product Product { get; set; }
public Printer Printer { get; set; }
public IEnumerable<ProductPrice> ProductPrices { get; set; }
}
public class ProductPrice
{
public int ProductPriceId { get; set; }
public PrinterProduct PrinterProduct { get; set; }
public decimal Price { get; set; }
}
public class Printer
{
public int PrinterId { get; set; }
public string PrinterName { get; set; }
public IEnumerable<Booking> Bookings { get; set; }
public IEnumerable<PrinterProduct> PrinterProducts { get; set; }
}

Given the newly added class structures in your question, I hope I can clear it up now.
From what I see, Bookings and Products have a many-to-many relation (where BookingProduct is used as the connection). The same is true for Product and Printer (where PrinterProduct is used as the connection).
From what I understand, you are trying to get from a singular Booking item to a singular PrinterProduct. I don't see any efficient way to do this without introducing the possibility of inconsistency with your data. You're expecting some Lists to return you one result. If it's only one result, why is it a List in the first place?
You have a single Booking. You take its BookingProducts. Now you have many items.
You take the Product from each individual BookingProduct. If all BookingProducts have the same product, you're in luck and will only have a List<Product> with a single Product in it. However, there is nothing stopping the system from return many different products, so we are to assume that you now hold a List of several Products
From each Product in the list, you now take all of its PrinterProducts. You now hold many PrinterProducts of many Products.
As you see, you end up with a whole list of items, not just the singular entity you're expecting.
Bookings, Products and Printers are all connected to eachother individually, like a triangle. I have seen scenarios where that is correct, but nine times out of ten, this is not what you want; and only leads to possible data inconsistency.
Look at it this way: Is it ever possible for the Product to have a Printer other than the Printer that is already related to the Booking? If not, then why would you have two relations? This only introduces the possibility that Booking.Printer is not the same as PrinterProduct.Printer.
Your relational model is set up to yield many results, but I think you expect a single result in some places. I would suggest taking another look at your data model because it does not reflect the types of operation you wish to perform on it. Change the many-to-many relations to one-to-many where applicable, and you should be able to traverse your data model in a more logical fashion, akin to the answer I provided in my previous answer.

If you've set up navigational properties, you can just browse to it:
var myBooking = ... //A single Booking, don't know how you retrieve it in your case.
var myPrinter = myBooking.Printer; //the Printer that is related to the Booking.
var myPrintproducts = myPrinter.PrintProducts; //The products that are related to the printer.
You don't need to keep nesting select statements, that only creates unnecessary confusion and overhead cost.
Keep in mind that you need to do this while in scope of the db context. Every time you try to access a property, EF will fill in the needed variables from the database. As long as there is an open db connection, it works.
Edit
If you really need to optimize this, you can use a Select statement. But you only need a single one. For example:
var myPrintproducts = db.Bookings.Single( x => x.ID == some_id_variable ).Select( x => x.Printer.PrintProducts);
But unless you have a very strict performance requirement, it seems better for code readability to just browse to it.

Related

Breeze where query for many-to-many with intermediate entity in EF

I am using EF6, WebApi2, AngularJS and BreezeJs.
I have the following entities:
Person
{
public string Name { get; set;}
public virtual ICollection<GenericProfileCountry> Countries { get; protected set; }
}
public class GenericProfileCountry
{
public string PersonId{ get; set; }
public virtual Person Person{ get; set; }
public string CountryIso { get; set; }
public virtual Country Country { get; set; }
}
public class Country
{
public string Iso { get; set; }
public string Name { get; set; }
}
Now I have a query that brings all Persons through breeze as follows:
return zEntityQuery.from('Contacts').expand('Profile, Countries')
.orderBy(contactOrderBy)
.toType(entityName)
.using(self.manager).execute()
.to$q(querySucceeded, self._queryFailed);
What I would like to do is perform a where statement on the above query with criteria that are on the intermediate entity. So say I want to bring only contacts that their first country (a person can have multiple countries) iso code is equal to 'GB'.
In Linq it would be something like Contacts.Where(contact => contact.Countries.First().CountryIso == 'GB')
Could something similar be expressed in the where(predicate) of breeze? I thought of going the other way (start from the intermediate table and filter from there), but not sure if that is the correct approach.
You can achieve that by creating a predicate with the keyword any or all
.where('Countries','any','CountryIso','eq','GB')
In case you want to create a predicate on grand children : BreezeJS Predicates on 2nd level expanded entities
Edit
If you want to get the first contacts whose countries Isos start with 'GB', you can achieve that by:
Jay's suggestion.
using Linq at Breeze controller:
public IQueryable<Person> ContactsWithFilteredCountryIso(string CountryIso)
{
return _contextProvider.Context.Persons.Where(p => p.Countries.First().CountryIso== CountryIso);
}
Then on the client:
return zEntityQuery.from('Contacts')
.withParameters({ CountryIso: "GB"})
.expand('Profile, Countries')
.orderBy(contactOrderBy)
.toType(entityName)
.using(self.manager).execute()
.to$q(querySucceeded, self._queryFailed);
Writing a select projection on countries with bringing up Contacts can be implemented by issuing a Breeze query on countries and expanding contact:
return zEntityQuery.from('Countries').expand('Contact')
.select('Country.name')
.where('CountryIso','eq','GB')
.orderBy(contactOrderBy)
.toType(entityName)
.using(self.manager).execute()
.to$q(querySucceeded, self._queryFailed);

Multilingual MVC 4 best practice

I know if I want to create a multilingual MVC4 application I would use resource files according to CultureInfo, but that would be useful for application's labels, messages, titles..etc, however I was thinking about defining a list of counties' names and their cities in many languages, now should I define them in a resource files (which can be exhausting) or should I use a table with many columns for each language?
And if I used resource files, how can I tell which country a user is from when he register in the system?
Which one is best practice? Is there any other approach?
Using multiple columns for each language will work, but it will also get out of hand pretty quickly as more columns and languages need to be added down the road. So I'd advise against that approach.
What you can do however is move the columns that need to be localized to a different table with a compound primary key. Here's a simple example with a cities table :
You'll have classes that look somewhat like this :
// City.cs
public class City
{
public int CityId { get; set; }
public string UnlocalizedField1 { get; set; }
public string UnlocalizedField2 { get; set; }
// Optional
public virtual List<CityTranslation> Translations { get; set; }
}
// CityTranslation.cs
public class CityTranslation
{
public int CityId { get; set; }
public string LanguageId { get; set; }
public string LocalizedField1 { get; set; }
public string LocalizedField2 { get; set; }
}
Then it becomes rather trivial to query your data in the language you need.

Can I limit how many levels the .include adds with LINQ and Entity Framework 5?

Given the following:
public class Department
{
public int DepartmentID { get; set; }
public string Name { get; set; }
public virtual ICollection<Course> Courses { get; set; }
}
public class Course
{
public int CourseID { get; set; }
public string Title { get; set; }
public int Credits { get; set; }
public int DepartmentID { get; set; }
public virtual Department Department { get; set; }
}
If I turn lazy loading off and issue the following:
var departments = _DepartmentRepository.GetAll()
.Include(c => c.courses);
Then I get the answers with a Department object inside of them.
Is there a way I can just include the courses and not get back the Department object. For example can I just include one level (courses).
You are just including one level. The department object inside the course is there because EF has done some relationship fixup so that you can navigate to the department from the course.
If you don't want departments then just get the courses directly. That is context.Courses.ToList(); or via a courses repo if you have one.
When fetching entities EF will automatically populate navigation properties where it is already tracking the target object. This means if you do say:
// Load the department with a PK of 1
_DepartmentRepository.Find(1);
and then, using the same context, for example:
// Load a course with PK of 17
_CourseRepository.Find(17);
If this courses department id is 1, then EF will have automatically populated it's Department navigation property even though you didn't specify the include. You could stop this behavior by not making the Department navigation property virtual.

How to maintain the statuses of parent and child database objects, by updating children when the parent changes

I am currently working on a ASP.NET MVC 3 Project where I have to maintain Statuses for about 60+ Database Models. E.g.
Parent:
public class DistributorModel
{
public DistributorModel()
{
Title = "Distributor";
}
public int DistributorId { get; set; }
public string Code { get; set; }
public string DistributorName { get; set; }
public ActiveStatus IsActive { get; set; }
}
Child:
public class DistributorVehicleModel
{
public DistributorModel()
{
Title = "Distributor Vehicle";
}
public int DistributorVehicleId { get; set; }
public string Make { get; set; }
public string Model { get; set; }
public int DistributorId { get; set; } // This is the foreign key
public ActiveStatus IsActive { get; set; }
}
The problem with maintaining statuses for both parent and child is as follows:
First:
When you change the status of a Distributor to be Inactive, none of it's vehicles may be active, which is the required result. But how do you update the status of all the children - there could be multiple child objects? Without explicitly specifiying which children to update (or at least minimize the amount of calls)? This might be a bit ambitious.
Second:
A second approach I thought might work is to remove the status field in the Vehicle Model, however a vehicle's status can be set to inactive due to repairs being done etc, so this won't really help and you don't want to delete a vehicle record and add it again in 5 days when it is repaired. What would be the best approach?
I have considered using database triggers, but I would prefer to keep the functionality at programming level for future project templates which might not use MS SQL Server 2008.
What will be the best approach to maintain the statuses of these entities? Without opening a can of worms. I am NOT using Entity Framework

EF 4.1 Code First Relationship table

Setup
Using MVC 3 + Code First
Here are my classes
public class Member
{
[Key]
public Guid ID { get; set; }
[Required]
public String Email { get; set; }
[Required]
public String FirstName { get; set; }
[Required]
public String LastName { get; set; }
public String Sex { get; set; }
public String Password { get; set; }
public String PasswordSalt { get; set; }
public DateTime RegisterDate { get; set; }
public DateTime LastOnline { get; set; }
public String SecurityQuestion { get; set; }
public String SecurityAnswer { get; set; }
public virtual ICollection<FamilyMember> Families { get; set; }
public virtual ICollection<Relationship> Relationships { get; set; }
}
public class Relationship
{
[Key]
public Guid ID { get; set; }
[ForeignKey("Member1")]
public Guid Member1ID { get; set; }
[ForeignKey("Member2")]
public Guid Member2ID { get; set; }
public Guid RelationshipTypeID { get; set; }
public virtual RelationshipType RelationshipType { get; set; }
public virtual Member Member1 { get; set; }
public virtual Member Member2 { get; set; }
}
Here is the problem
The database table "Relationship" is being created with the following columns:
ID, Member1ID, Member2ID, RelationshipTypeID, Member_ID
Why is it creating the Member_ID column?
I've seen this post in which the user has the same type of setup, but I am unsure of how to define the InverseProperty correctly. I tried using fluent API calls but from what I can tell they will not work here since I have two foreign keys referring to the same table.
Any help would be appreciated!
Member_ID is the foreign key column which EF created for the navigation property Member.Relationships. It belongs to a third association from Member.Relationships refering to an end endpoint which is not exposed in your Relationship entity. This relationship has nothing to do with the other two relationships from Relationship.Member1 and Relationship.Member2 which also both have an endpoint not exposed in Member.
I guess, this is not what you want. You need always pairs of endpoints in two entities to create an association. One endpoint is always a navigation property. The second endpoint can also be a navigation property but it is not required, you can omit the second navigation property.
Now, what is not possible, is to associate two navigation properties (Member1 and Member2) in one entity with one navigation property (Relationships) in the other entity. That is what you are trying to do apparently.
I assume that your Member.Relationships property is supposed to express that the member is either Member1 or Member2 in the relationship, or that it participates in the relationship, no matter if as Member1 or Member2.
Unfortunately you cannot express this in the model appropriately. You have to introduce something like RelationsshipsAsMember1 and RelationsshipsAsMember2 and for these two collection you can use the InverseProperty attribute as shown in the other question. In addition you can add a helper property which concats the two collections. But this is not a mapped property but readonly:
public class Member
{
// ...
[InverseProperty("Member1")]
public virtual ICollection<Relationship> RelationshipsAsMember1 { get; set; }
[InverseProperty("Member2")]
public virtual ICollection<Relationship> RelationshipsAsMember2 { get; set; }
public IEnumerable<Relationship> AllRelationships
{
get { return RelationshipsAsMember1.Concat(RelationshipsAsMember2); }
}
}
Accessing AllRelationships will cause two queries and roundtrips to the database (with lazy loading) to load both collections first before they get concatenated in memory.
With this mapping the Member_ID column will disappear and you will only get the two expected foreign key columns Member1ID, Member2ID because now you have only two associations and not three anymore.
You could also think about if you need the Relationships collection in the Member entity at all. As said, navigation properties on both sides are not required. If you rarely need to navigate from a member to its relationships you could fetch the relationships also with queries on the Relationship set, like so:
var relationships = context.Relationships
.Where(r => r.Member1ID == givenMemberID || r.Member2ID == givenMemberID)
.ToList();
...or...
var relationships = context.Relationships
.Where(r => r.Member1ID == givenMemberID)
.Concat(context.Relationships
.Where(r => r.Member2ID == givenMemberID)
.ToList();
This would give you all relationships the member with ID = givenMemberID participates in without the need of a navigation collection on the Member entity.

Resources