EF Core, DbContext Include related entities - dbcontext

I have a db entity which has several related entities. I want to both query the parent entity by doing a Find or something similar, but also include the related entities. As an example,
public class Customer
{
[Key]
public int CustomerId { get; set; }
public string CustomerName { get; set; }
public ICollection<Address> CustomerAddresses { get; }
}
I want to query the customer and also include all known addresses. What is the accepted way of doing this via db context? Do I need to first query with a Where clause, then Include? Something like:
_dbContext.Set().Where(o => o.CustomerId == id).Include(o => o.CustomerAddresses).FirstOrDefault();

Related

How can I LINQ select from two EF DbSets and group join - but using AutoMapper

Note: These classes are related, but not part of the same Aggregate (like PurchaseOrder and OrderLine) - so I do not have a navigation property from "One" to "Many".
=== Entities ===
public class One
{
public Guid Id { get; set; }
public string Name { get; set; }
}
public class Many
{
public Guid Id { get; set; }
public string Name { get; set; }
public Guid One { get; set; }
}
=== Contracts ===
public class OneWithMany
{
public Guid Id { get; set; }
public string Name { get; set; }
public IEnumerable<Many>? ManyRelatedObjects { get; set; }
}
I want to select all One objects and any related Many objects from DbSet/DbSet into OneWithMany.
To ensure I don't miss properties added in future I am using ProjectTo in AutoMapper - but I can't work out how to fit it into the equation.
Unfortunately, it seems Entity Framework does not support GroupJoin.
The solution is to do the projection and as much filtering as possible as two separate queries, and then combine them into a result in memory.
If you find EF related answers on the web related to GroupJoin make sure you check the example code to see if they are actually showing code working on arrays instead of DbSet.

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);

Automapper and Linq with joined tables, populating ViewModel properties

Having just figured out how to populate my ViewModel from a model using Automapper, I am now onto the next challenge – populating the ViewModel properties from a joined table.
The image below depicts my simple database.
My ViewModel class is defined as:
public class PersonViewModel
{
public int Id { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public Nullable<int> Age { get; set; }
public Nullable<int> SuffixId { get; set; }
public string PersonStatus { get; set; }
public string PersonPreference { get; set; }
public virtual Suffix Suffix { get; set; }
} enter code here
Note the additional fields of PersonStatus and PersonPreference that will come from joining PersonInfo to Person.
Here is the code I use to instantiate my mapping configuration:
Mapper.CreateMap<PersonViewModel, Person>();
Mapper.CreateMap<Person, PersonViewModel>();
And now the code to populate the ViewModel
List<PersonViewModel> persons = null;
using (var context = new DemoEntities())
{
persons = (from p in context.People.AsEnumerable()
join i in context.PersonInfoes on p.Id equals i.PersonId
select Mapper.Map<PersonViewModel>(p)).ToList();
return persons;
}
How would I populate the two joined properties (PersonStatus and PersonPreference) from this setup?
Thanks in advance!
AutoMapper can automatically flatten an object structure if the source and target classes meet some requirements.
The source class, the class that the mapping starts from, should have reference property to the related class. In you case: Person.PersonInfo (1:1, but n:1 will also work).1
The target class should contain property names that allow AutoMapper to resolve the property path to the referenced object. in your case: PersonViewModel.PersonInfoStatus, so AutoMapper will see that there's a property PersonInfo in the source object and a property Status in the referenced object.
If you've got these things in place, you can do
persons = context.People.Project().To<PersonViewModel>().ToList();
The Project().To syntax is a relatively new API that allows working on IQueryables. For more details see Does AutoMapper support Linq?.
You will see that this sends a JOIN query to the database.
1AutoMapper can also aggregate/flatten associated collections, but that's a different chapter.

Entity Framework, two Many to Many relationship to same object using Fluent API

I am trying to define two many to many relationship to same object using fluent api.
Here is the simplified model:
public class PurchaseRequisition
{
[Key, ForeignKey("Transaction")]
public int TransactionId { get; set; }
public virtual ICollection<People> RequisitionedBys { get; set; }
public virtual ICollection<People> AuthorizedSignatures { get; set; }
}
public class People
{
[Key]
public string Id{ get; set; }
public string FullName { get; set; }
public virtual ICollection<PurchaseRequisition> PurchaseRequisitionsForRequisitionedBys { get; set; }
public virtual ICollection<PurchaseRequisition> PurchaseRequisitionsForAuthorizedSignatures { get; set; }
}
Here is the fluent api code:
modelBuilder.Entity<PurchaseRequisition>()
.HasMany(a => a.RequisitionedBys)
.WithMany(b => b.PurchaseRequisitionsForRequisitionedBys)
.Map(x =>
{
x.MapLeftKey("PurchaseRequisitionId");
x.MapRightKey("RequisitionedById");
x.ToTable("PurchaseRequisitionRequisitionedBy");
});
modelBuilder.Entity<PurchaseRequisition>()
.HasMany(a => a.AuthorizedSignatures)
.WithMany(b =>b.PurchaseRequisitionsForAuthorizedSignatures)
.Map(x =>
{
x.MapLeftKey("PurchaseRequisitionId");
x.MapRightKey("AuthorizedSignatureId");
x.ToTable("PurchaseRequisitionAuthorizedSignature");
});
What I want is to generate two separate linking tables, but what EF generates is two foreign key columns to PurchaseRequisition in People table and 1 foreign key column to People in PurchaseRequisition field.
Can anyone tell me what might be wrong?
The problem was fixed.
I mistakenly thought that my database initializer code would drop and recreate the database since I made changes to the model classes and my Initializer class extended DropCreateDatabaseIfModelChanges.
As Slauma suggested, fluent api code was not being reached even though the model has been changed. I was setting the initializer using SetInitializer() method and this code only ran when I used a context instance for the first time to access the DB.

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