BreezeJs - Using expand() when two foreign keys are related to a common entity - breeze

Say, I have two entities, Movement (id, #fromLocationId, #toLocationId), fromLocationId and toLocationId being two foreign keys of the second entity Location (id, name). I would like to write a Breeze query that retrieves all movements with location names related to fromLocationId and toLocationId. This is what I got thus far:
var query = breeze.EntityQuery('movement').expand('location');
When I debug it and check the first record, for example, I find out that it has a location() and location1() properties. I can retrieve the location name of fromLocationId from data[0].location().name() but cannot do the same with that of toLocationId, as location1() is null. I even tried var query = breeze.EntityQuery('movement').expand('location, location1'); but it is still not working.
Any ideas on how to solve this? Thanks in advance.
EDIT
Here are the .NET classes:
[Table("Location")]
public partial class Location
{
public Location()
{
Movements = new HashSet<Movement>();
Movements1 = new HashSet<Movement>();
}
public int Id { get; set; }
[StringLength(250)]
public string Name { get; set; }
public virtual ICollection<Movement> Movements { get; set; }
public virtual ICollection<Movement> Movements1 { get; set; }
}
[Table("Movement")]
public partial class Movement
{
public int Id { get; set; }
public int FromLocationId { get; set; }
public int ToLocationId { get; set; }
public virtual Location Location { get; set; }
public virtual Location Location1 { get; set; }
}
In the DbContext class, the relationships look like this:
modelBuilder.Entity<Location>()
.HasMany(e => e.Movements)
.WithRequired(e => e.Location)
.HasForeignKey(e => e.FromLocationId)
.WillCascadeOnDelete(false);
modelBuilder.Entity<Location>()
.HasMany(e => e.Movements1)
.WithRequired(e => e.Location1)
.HasForeignKey(e => e.ToLocationId)
.WillCascadeOnDelete(false);
Thanks.

OK, I found out what I needed to do. Here is the solution:
var query = breeze.EntityQuery('movement').expand('location, location')
I guess, Breeze understands this as, the first location in the expand() will be related to fromLocationId and the second location will be related to toLocationId. In other words, add as many entity as foreign keys related to that common entity.
Hope it helps someone else.

Related

Cannot create foreign key constraint on self-joining Many-to-Many relationship

I have created the following classes:
public class Character
{
public int ID { get; set; }
public string Title { get; set; }
public ICollection<Relationship> RelatedTo { get; set; }
public ICollection<Relationship> RelatedFrom { get; set; }
}
public class Relationship
{
public int ToID { get; set; }
public int FromID { get; set; }
public Character CharacterFrom { get; set; }
public Character CharacterTo { get; set; }
public string Details { get; set; }
}
In my Context I have this:
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Relationship>()
.HasKey(r => new { r.ToID, r.FromID });
modelBuilder.Entity<Relationship>()
.HasOne(r => r.CharacterFrom)
.WithMany(r => r.RelatedTo)
.HasForeignKey(r => r.FromID)
.OnDelete(DeleteBehavior.ClientSetNull);
modelBuilder.Entity<Relationship>()
.HasOne(r => r.CharacterTo)
.WithMany(r => r.RelatedFrom)
.HasForeignKey(r => r.ToID)
.OnDelete(DeleteBehavior.ClientSetNull);
}
I think that it is right but I cannot apply the migration due to the following error:
Cannot create the foreign key "FK_Relationship_Character_FromID" with the SET NULL referential action, because one or more referencing columns are not nullable.
I've tried every combination of DeleteBehaviour for OnDelete. None of them work. I don't believe I can make the ICollections nullable and it doesn't seem right that I'd want to. I've spent two hours on this searching for answers. Every tutorial or explanation on EF Core that I've tried to follow seems to take a slightly different approach and be subtly incompatible with every other one. Please help!
The error is telling you that you cannot use DeleteBehavior.ClientSetNull (or DeleteBehavior.SetNull) because the corresponding FK property is not nullable - both ToID and FromID are of type int, hence does not allow setting to null (neither client nor server).
To turn off the cascade delete (in order to break the multiple cascade paths I guess) for required FK relationships, use DeleteBehavior.Restrict instead.

Entity Framework Code First - map same entity twice for different purpose

I have two models, One ApplicationUser which holds all users in the system and I have a Quotation model which will hold all Quotations made. now I want to store two mappings to ApplicationUser inside Quotations. So that I can map to created User as well as cancelled User. My model looks like this
public class Quotation
{
public int QuotationID { get; set; }
public DateTime QuotationDate { get; set; }
public DateTime QuotationCancelDate { get; set; }
public int ApplicationUserID { get; set; }
public virtual ApplicationUser CreatedUser { get; set; }
[ForeignKey("ApplicationUserID")]
public ApplicationUser CancelledUser { get; set; }
}
But this throws an error
Quotation_CancelledUser_Target_Quotation_CancelledUser_Source: : The types of all properties in the Dependent Role of a referential constraint must be the same as the corresponding property types in the Principal Role. The type of property 'ApplicationUserID' on entity 'Quotation' does not match the type of property 'Id' on entity 'ApplicationUser' in the referential constraint 'Quotation_CancelledUser'.
So I guess , The approach I am taking is wrong. Can anyone point out the correct way to achieve this?
The problem you are observing is called "Multiple Cascade Path". A Multiple Cascade Path happens when a cascade path goes from column col1 in table A to table B and also from column col2 in table A to table B.
The exception is caused by SQL Server when code first attempted to add table that has columns appearing more than once of another table.
In SQL Server, a table cannot appear more than one time in a list of all the cascading referential actions that are started by either a DELETE or an UPDATE statement. For example, the tree of cascading referential actions must only have one path to a particular table on the cascading referential actions tree.
You will need to use FluentAPI to configure the relationship. I am using EF5 currently and do not know if this can be accomplished in EF6/7.
So modifying your code sample, it would look like:
public class Quotation
{
public int QuotationID { get; set; }
public DateTime QuotationDate { get; set; }
public DateTime QuotationCancelDate { get; set; }
public int CreatedUserID { get; set; }
// Navigation property
public virtual ApplicationUser CreatedUser { get; set; }
public int CancelledUserID { get; set; }
// Navigation property
public virtual ApplicationUser CancelledUser { get; set; }
}
// Created a simple class for example
public class ApplicationUser
{
[Key]
public int Id { get; set; }
public string Name { get; set; }
}
Now in you context class you can write:
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
// Disable the default PluralizingTableNameConvention
modelBuilder.Conventions.Remove<PluralizingTableNameConvention>();
modelBuilder.Conventions.Remove<OneToManyCascadeDeleteConvention>();
// Add configuration here
modelBuilder.Entity<Quotation>()
.HasKey(e => e.QuotationID);
modelBuilder.Entity<ApplicationUser>()
.HasKey(e => e.Id);
modelBuilder.Entity<Quotation>()
.HasRequired(a => a.CreatedUser)
.WithMany()
.HasForeignKey(u => u.CreatedUserID);
modelBuilder.Entity<Quotation>()
.HasRequired(a => a.CancelledUser)
.WithMany()
.HasForeignKey(u => u.CancelledUserID);
}
For more information with example refer this link.

Defining multiple Foreign Key for the Same table in Entity Framework Code First

I have two entities in my MVC application and I populated the database with Entity Framework 6 Code First approach. There are two city id in the Student entity; one of them for BirthCity, the other for WorkingCity. When I define the foreign keys as above an extra column is created named City_ID in the Student table after migration. Id there a mistake or how to define these FKs? Thanks in advance.
Student:
public class Student
{
public int ID { get; set; }
public string Name { get; set; }
public string Surname { get; set; }
public int BirthCityID { get; set; }
public int LivingCityID { get; set; }
[ForeignKey("BirthCityID")]
public virtual City BirthCity { get; set; }
[ForeignKey("LivingCityID")]
public virtual City LivingCity { get; set; }
}
City:
public class City
{
public int ID { get; set; }
public string CityName { get; set; }
public virtual ICollection<Student> Students { get; set; }
}
To achieve what you want you need to provide some aditional configuration.Code First convention can identify bidirectional relationships, but not when there are
multiple bidirectional relationships between two entities.You can add configuration (using Data Annotations or the Fluent API) to present this
information to the model builder. With Data Annotations, you’ll use an annotation
called InverseProperty. With the Fluent API, you’ll use a combination of the Has/With methods to specify the correct ends of these relationships.
Using Data Annotations could be like this:
public class Student
{
public int ID { get; set; }
public string Name { get; set; }
public string Surname { get; set; }
public int BirthCityID { get; set; }
public int LivingCityID { get; set; }
[ForeignKey("BirthCityID")]
[InverseProperty("Students")]
public virtual City BirthCity { get; set; }
[ForeignKey("LivingCityID")]
public virtual City LivingCity { get; set; }
}
This way you specifying explicitly that you want to relate the BirthCity navigation property with Students navigation property in the other end of the relationship.
Using Fluent Api could be like this:
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<Student>().HasRequired(m => m.BirthCity)
.WithMany(m => m.Students).HasForeignKey(m=>m.BirthCityId);
modelBuilder.Entity<Student>().HasRequired(m => m.LivingCity)
.WithMany().HasForeignKey(m=>m.LivingCityId);
}
With this last solution you don't need to use any attibute.
Now, the suggestion of #ChristPratt in have a collection of Student in your City class for each relationship is really useful. If you do that, then the configurations using Data Annotations could be this way:
public class Student
{
public int ID { get; set; }
public string Name { get; set; }
public string Surname { get; set; }
public int BirthCityID { get; set; }
public int LivingCityID { get; set; }
[ForeignKey("BirthCityID")]
[InverseProperty("BirthCityStudents")]
public virtual City BirthCity { get; set; }
[ForeignKey("LivingCityID")]
[InverseProperty("LivingCityStudents")]
public virtual City LivingCity { get; set; }
}
Or using Fluent Api following the same idea:
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<Student>().HasRequired(m => m.BirthCity)
.WithMany(m => m.BirthCityStudents).HasForeignKey(m=>m.BirthCityId);
modelBuilder.Entity<Student>().HasRequired(m => m.LivingCity)
.WithMany(m => m.LivingCityStudents).HasForeignKey(m=>m.LivingCityId);
}
Sheesh. It's been a long day. There's actually a very big, glaring problem with your code, actually, that I completely missed when I commented.
The problem is that you're using a single collection of students on City. What's actually happening here is that EF can't decide which foreign key it should actually map that collection to, so it creates another foreign key specifically to track that relationship. Then, in effect you have no navigation properties for the collections of students derived from BirthCity and LivingCity.
For this, you have to drop down to fluent configuration, as there's no way to configure this properly using just data annotations. You'll also need an additional collection of students so you can track both relationships:
public class City
{
...
public virtual ICollection<Student> BirthCityStudents { get; set; }
public virtual ICollection<Student> LivingCityStudents { get; set; }
}
Then, for Student:
public class Student
{
...
public class StudentMapping : EntityTypeConfiguration<Student>
{
public StudentMapping()
{
HasRequired(m => m.BirthCity).WithMany(m => m.BirthCityStudents);
HasRequired(m => m.LivingCity).WithMany(m => m.LivingCityStudents);
}
}
}
And finally in your context:
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Configurations.Add(new Student.StudentMapping());
}

"principal end of an association" - EF

Unable to determine the principal end of an association between the
types 'XYZ.Models.Attachment' and 'XYZ.Models.Accounts.User'. The
principal end of this association must be explicitly configured using
either the relationship fluent API or data annotations.
Exception has been thrown by the target of an invocation.
That error I get, when I try to update-database with my EF Models.
Part of User.cs:
[Table("Users")]
public class User
{
[Key]
[DatabaseGeneratedAttribute(DatabaseGeneratedOption.Identity)]
public int UserId { get; set; }
public int MainPhotoId { get; set; }
[ForeignKey("MainPhotoId")]
public virtual Attachment Photo { get; set; }
}
Attachment.cs
[Table("Attachments")]
public class Attachment
{
[Key]
[DatabaseGeneratedAttribute(DatabaseGeneratedOption.Identity)]
public int AttachmentId { get; set; }
public string name { get; set; }
public int UserId { get; set; }
public DateTime AddDate { get; set; }
public bool del { get; set; }
[ForeignKey("UserId")]
public virtual User Author { get; set; }
}
Why I get this error? And how to resolve it?
Regards
Mapping conventions detect a one-to-one relationship between User.Photo and Attachment.Author and cannot infer what the principal and what the dependent end is. Hence the exception.
Actually, according to your comments, you want two relationships and not a single one-to-one relationship. You can achieve that only by overriding the convention with Fluent API and you probably need to make one of the relationships optional because otherwise you have a circular mutual dependency between User and Attachment. You can, for example, make the User.Photo property optional by choosing a nullable foreign key:
public int? MainPhotoId { get; set; }
Then the mapping would look like this:
modelBuilder.Entity<User>()
.HasOptional(u => u.Photo)
.WithMany()
.HasForeignKey(u => u.MainPhotoId);
modelBuilder.Entity<Attachment>()
.HasRequired(a => a.Author)
.WithMany()
.HasForeignKey(a => a.UserId);
With this mapping you can remove the [ForeignKey] attributes because the definition of the FK properties is part of the Fluent API mapping (HasForeignKey).

MVC How to map from my domain model to a specific view model

I'm getting started with the concept of mapping domain models to view models in ASP.NET MVC after watching a recommendation to do this to pass specific viewModels to the views.
I've been able to manage a basic mapping of one domain model to a simpler viewmodel with less properties but now need to produce a more complex viewmodel and can't figure it out. I have the following domain models
public class Club
{
public int ClubID { get; set; }
public string FullName { get; set; }
public string Description { get; set; }
public string Telephone { get; set; }
public string URL { get; set; }
public DateTime CreatedDate { get; set; }
public virtual ICollection<Member> Members{ get; set; }
}
public class Member
{
public int MemberID{ get; set; }
public string Name { get; set; }
public MemberType Membership{ get; set; }
public virtual Club Club { get; set; }
public virtual int ClubID { get; set; }
}
public enum MemberType
{
Standard,
Special,
Limited
}
I want to map to a view model such as this (note: I've split it like this because I think it makes sense but I'm not sure)...
public class ClubDetailsViewModel
{
public int ClubID { get; set; }
public string FullName { get; set; }
public string Description { get; set; }
public IList<ClubDetailsMemberSummaryViewModel> Members { get; set; }
}
public class ClubDetailsMemberSummaryViewModel
{
public MemberType Membership { get; set; }
public int MemberCount { get; set; }
}
What I'm trying to end up with is a page which displays some of the club details plus a summary report of the member types at the club with a count of the members. Such as:
Some Club Name
Description of the club.....
CLUB MEMBERS
Limited - 15
Standard - 100
So I think the viewmodel makes sense for this (although might be a better way to do it). Where I'm struggling is how to map the elements. I can get the Club to map the main fields to the club viewmodel but really can't work out how to map the result of the list of clubs onto their view model and then add that to the main view model as a list.
I'm getting the clubs from my repository using this
var clubs = _clubRepository.GetClubByID(ID);
Then I can transform the Courts which are returned using an include in the data access layer from entity framework using this
var grpCourts = from c in clubs.Members
group c by c.Membership into grp
select new { st = grp.Key, count = grp.Distinct().Count() };
How would I loop through the resulting records and map those to the ClubDetailsMemberSummaryViewModel and then add the list of those to the main ClubDetailsViewModel?
Your mapping from Club to ClubDetailsViewModel will be trivial with the exception of Members. For that property, you could write a quick resolver inline or write your own custom resolver. An inline resolver would look something like this:
Mapper.CreateMap<Club, ClubDetailsViewModel>()
.ForMember(dest => dest.Members, opt => opt.ResolveUsing(src =>
{
return src.Members
.GroupBy(m => m.Membership)
.Select(grp => new ClubDetailsMemberSummaryViewModel
{
Membership = grp.Key,
MemberCount = grp.Distinct().Count()
});
}));
I think it's good practice to refactor more complex resolvers like this out to their own classes:
public class MembershipClubDetailsResolver : ValueResolver<Club, IList<ClubDetailsMemberSummaryViewModel>>
{
protected override IList<ClubDetailsMemberSummaryViewModel> ResolveCore (Club source)
{
return source.Members
.GroupBy (m => m.Membership)
.Select(grp => new ClubDetailsMemberSummaryViewModel
{
Membership = grp.Key,
MemberCount = grp.Distinct().Count()
})
.ToList();
}
}
And then use that resolver in your mapping:
Mapper.CreateMap<Club, ClubDetailsViewModel>()
.ForMember(dest => dest.Members, opt => opt.ResolveUsing<MembershipClubDetailsResolver>());
Your mapping appears to be rather complex, I think I would use the .ConvertUsing method of automapper
Mapper.CreateMap<List<Club>,List<ClubDetailsViewModel>>()
.ConvertUsing<ClubToClubDetailsViewModel>();
The conversion class has the following inheritance
public class ClubToClubDetailsViewModel: TypeConverter<List<Club>,List<ClubDetailsViewModel>>
{
....
}
Alternatively you can tinker with creating two "simple" mappings
Mapper.CreateMap<Club,ClubDetailsViewModel>()
That will map everything except the property called Members
Then you need to create a mapping for the members to ClubDetailsMemberSummaryViewModel, you can do that mapping manually or you can configure this in automapper aswell.
For more specific details on automapper you can visit https://github.com/AutoMapper/AutoMapper/wiki

Resources