Many to Many relationship - Entity Framework Core - c#

I have two tables in my database:
Contact
ContactRoles
The join table is Contact_ContactRole.
I have defined them in code as
Contact.cs
ContactRoles.cs
Contact_ContactRole.cs
I have defined a virtual property as follows:
public class Contact
{
public Contact()
{
}
public int ID { get; set; }
public int ClientID { get; set; }
public string Name { get; set; }
public string Email { get; set; }
public string Phone { get; set; }
public string Mobile { get; set; }
public int IsDefault { get; set; }
public bool IsActive { get; set; }
public bool IsInvoiceEmail { get; set; }
public DateTime CreationDate { get; set; }
public bool IsSubConsultant { get; set; }
public string Notes { get; set; }
//[NotMapped]
public virtual ICollection<ContactRoles> ContactRoles { get; set; }
}
public class ContactRoles
{
public int ID { get; set; }
public string Name { get; set; }
public virtual ICollection<Contact> Contacts { get; set; }
}
public class Contact_ContactRole
{
public int ContactID { get; set; }
public int ContactRoleID { get; set; }
//public Contact Contact { get; set; }
//public ContactRoles ContactRole { get; set; }
}
AuthContext.cs
protected override void OnModelCreating(ModelBuilder builder)
{
builder
.Entity<Contact>()
.ToTable("Contact");
builder
.Entity<Contact_ContactRole>()
.ToTable("Contact_ContactRole");
builder
.Entity<ContactRoles>()
.ToTable("ContactRoles");
builder.Entity<Contact>()
.HasMany(p => p.ContactRoles)
.WithMany(p => p.Contacts)
.UsingEntity(j => j.ToTable("Contact_ContactRole"));
builder
.Entity<Contact_ContactRole>()
.HasKey(ccr => new { ccr.ContactID, ccr.ContactRoleID });
}
I am getting an error:
Cannot use table 'Contact_ContactRole' for entity type 'Contact_ContactRole'
since it is being used for entity type
'ContactContactRoles (Dictionary<string, object>)' and potentially other
entity types, but there is no linking relationship. Add a foreign key
to 'Contact_ContactRole' on the primary key properties and pointing to the
primary key on another entity typed mapped to 'Contact_ContactRole'.'
If I comment the code:
builder.Entity<Contact>()
.HasMany(p => p.ContactRoles)
.WithMany(p => p.Contacts)
.UsingEntity(j => j.ToTable("Contact_ContactRole"));
I get the following error:
Invalid object name 'ContactContactRoles'.
at Microsoft.Data.SqlClient.SqlConnection.OnError(SqlException exception, Boolean breakConnection, Action`1 wrapCloseInAction)

Try this
public class Contact
{
public Contact()
{
}
public int ContactId { get; set; } //PK
public int ClientID { get; set; }
public string Name { get; set; }
public string Email { get; set; }
public string Phone { get; set; }
public string Mobile { get; set; }
public int IsDefault { get; set; }
public bool IsActive { get; set; }
public bool IsInvoiceEmail { get; set; }
public DateTime CreationDate { get; set; }
public bool IsSubConsultant { get; set; }
public string Notes { get; set; }
[ForeignKey("ContactId")]
public virtual ICollection<Contact_ContactRole> ContactContactRoles { get; set; }
}
public class ContactRoles
{
public int ContactRoleId { get; set; } //PK
public string Name { get; set; }
[ForeignKey("ContactRoleId")]
public virtual ICollection<Contact_ContactRole> ContactContactRoles { get; set; }
}
public class Contact_ContactRole
{
public int ContactId { get; set; }
public Contact Contact { get; set; }
public int ContactRoleId { get; set; }
public ContactRoles ContactRole { get; set; }
}
also I believe that Contact_ContactRole needs a Primary Key. Currently the data annotations in EF Core does not have the option of creating Composite Primary Key.
Hence, we may have to fall back to Fluent API to create the Composite Key.
modelBuilder.Entity<Contact_ContactRole>()
.HasKey(e => new { e.ContactId, e.ContactRoleId });

Related

EF core 2.0, OwnsOne in TPH model classes

I have problem when I try to migrate my model in EF Core 2.0.
public class Profile
{
[Key]
public Guid Id { get; set; }
public Guid UserId { get; set; }
public ExternalUser User { get; set; }
}
public class OrganizationCustomerProfile : Profile
{
public string CompanyName { get; set; }
public Address LegalAddress { get; set; }
public Address ActualAddress { get; set; }
public BusinessRequisites Requisites { get; set; }
public string President { get; set; }
public IEnumerable<ContactPerson> ContactPerson { get; set; }
}
public class PersonCustomerProfile : Profile
{
public FullName Person { get; set; }
public Address Address { get; set; }
public string PhoneNumber { get; set; }
}
public class ContactPerson
{
[Key]
public Guid Id { get; set; }
public FullName Person { get; set; }
public string Rank { get; set; }
public string Email { get; set; }
public string PhoneNumber { get; set; }
public Guid ProfileId { get; set; }
public Profile Profile { get; set; }
}
Here I want to add complex datatypes Address and BusinessRequisites, which are:
public class BusinessRequisites
{
public string OGRN { get; set; }
public string INN { get; set; }
public string KPPCode { get; set; }
public string SettlementAccount { get; set; }
public string RCBIC { get; set; }
public string CorrespondentAccount { get; set; }
public string BankName { get; set; }
}
public class Address
{
public string FullAddress { get; set; }
public float Latitude { get; set; }
public float Longtitude { get; set; }
}
Code which I use for TPH binding:
public DbSet<Profile> UserProfiles { get; set; }
public DbSet<ContactPerson> ContactPerson { get; set; }
public DbSet<OrganizationCustomerProfile> OrganizationCustomerProfile { get; set; }
...
modelBuilder.Entity<Profile>().HasKey(u => u.Id);
modelBuilder.Entity<OrganizationCustomerProfile>().OwnsOne(e => e.ActualAddress);
modelBuilder.Entity<OrganizationCustomerProfile>().OwnsOne(e => e.LegalAddress);
modelBuilder.Entity<OrganizationCustomerProfile>().OwnsOne(e => e.Requisites);
But when I try to make a migration, I get an error:
"Cannot use table 'UserProfiles' for entity type
'OrganizationCustomerProfile.ActualAddress#Address' since it has a
relationship to a derived entity type 'OrganizationCustomerProfile'.
Either point the relationship to the base type 'Profile' or map
'OrganizationCustomerProfile.ActualAddress#Address' to a different
table."
So, what the reason of this error? Is it not possible to create hierarchy inheritance in EF Core 2.0?
Thank you!
It seems like this isn't supported at the moment:
https://github.com/aspnet/EntityFrameworkCore/issues/9888

Automapper many to many mapping confusion

I have many to many relationship tables such as "User & Notification & UserNotification" and their entities, view models also.
There is only a difference between ViewModel and Entity classes. HasRead property is inside NotificationViewModel. How Can I map this entities to view models? I could not achieve this for HasRead property.
What I did so far is,
Mapping Configuration:
CreateMap<Notification, NotificationViewModel>();
CreateMap<User, UserViewModel>().ForMember(dest => dest.Notifications, map => map.MapFrom(src => src.UserNotification.Select(x => x.Notification)));
Notification class:
public class Notification : IEntityBase
{
public Notification()
{
this.UserNotification = new HashSet<UserNotification>();
}
public int Id { get; set; }
public string Header { get; set; }
public string Content { get; set; }
public System.DateTime CreateTime { get; set; }
public bool Status { get; set; }
public virtual ICollection<UserNotification> UserNotification { get; set; }
}
User Class
public class User : IEntityBase
{
public User()
{
this.UserNotification = new HashSet<UserNotification>();
}
public int Id { get; set; }
public string Name { get; set; }
public string Surname { get; set; }
public string Email { get; set; }
public string PhoneNumber { get; set; }
public bool Status { get; set; }
public virtual ICollection<UserNotification> UserNotification { get; set; }
}
UserNotification class:
public class UserNotification : IEntityBase
{
public int Id { get; set; }
public int UserId { get; set; }
public int NotificationId { get; set; }
public bool HasRead { get; set; }
public virtual Notification Notification { get; set; }
public virtual User User { get; set; }
}
UserViewModel class
public class UserViewModel : IValidatableObject
{
public int Id { get; set; }
public string Name { get; set; }
public string Surname { get; set; }
public string Email { get; set; }
public string PhoneNumber { get; set; }
public bool Status { get; set; }
public IList<NotificationViewModel> Notifications { get; set; }
}
NotificationViewModel class
public class NotificationViewModel
{
public int Id { get; set; }
public string Header { get; set; }
public string Content { get; set; }
public System.DateTime CreateTime { get; set; }
public bool Status { get; set; }
public bool HasRead { get; set; } // this is the difference
}
In order to fix up the HasRead, maybe you can utilize the AfterMap(Action<TSource, TDestination> afterFunction) function. It's not as elegant as the rest of automapper, but it might work.
CreateMap<User, UserViewModel>()
.ForMember(dest => dest.Notifications, map => map.MapFrom(src => src.UserNotification.Select(x => x.Notification)))
.AfterMap((src, dest) =>
{
foreach (var notificationVM in dest.Notifications)
{
notificationVM.HasRead = src.UserNotification.Where(x => x.NotificationId == notificationVM.Id).Select(x => x.HasRead).FirstOrDefault();
}
});

The member with identity 'PmData.SafetyRequirement_Assets' does not exist in the metadata collection.\r\nParameter name: identity

I am trying to update an record in my system. Everything on the model saves great, except any of my many to many type relationships on the form. When I get to those in my model it gives me the error. "The member with identity 'PmData.SafetyRequirement_Assets' does not exist in the metadata collection.\r\nParameter name: identity". I've read over some of the other answers but I do not have any triggers on my database, and I've gone through several changes in my model based on other suggestions and it doesn't seem to change anything. The project is in vNext.
Here is my first model
public partial class Asset : DataModel
{
[Required]
[StringLength(64)]
public string Name { get; set; }
[StringLength(256)]
public string Description { get; set; }
[StringLength(1024)]
public string SystemFunction { get; set; }
[StringLength(2048)]
public string Remarks { get; set; }
public bool IsSystem { get; set; }
public bool IsGrouping { get; set; }
[StringLength(128)]
public string FieldTag { get; set; }
[ForeignKey("Parent")]
public int? ParentId { get; set; }
[ForeignKey("Building")]
public int? BuildingId { get; set; }
public bool IsOperable { get; set; }
public bool IsAvailable { get; set; }
public virtual Asset Parent { get; set; }
public virtual Building Building { get; set; }
public virtual ICollection<Asset> Children { get; set; }
public virtual ICollection<DrawingReference> DrawingReferences { get; set; }
public virtual ICollection<SpecReference> SpecReferences { get; set; }
public virtual ICollection<SafetyRequirement> SafetyRequirements { get; set; }
public virtual ICollection<SupportSystem> SupportSystems { get; set; }
}
The model for one the other table with a many to many.
public partial class SafetyRequirement : DataModel
{
[StringLength(256)]
[Required]
public string Name { get; set; }
[StringLength(2048)]
public string SafetyFunction { get; set; }
[StringLength(2048)]
public string FunctionalRequirements { get; set; }
[StringLength(2048)]
public string SystemBoundary { get; set; }
[StringLength(255)]
public string Reference { get; set; }
[ForeignKey("QualityLevel")]
public int QualityLevelId { get; set; }
public virtual QualityLevel QualityLevel { get; set; }
public virtual ICollection<Asset> Assets { get; set; }
}
The map for the joining table
modelBuilder.Entity<Asset>().HasMany(t => t.SafetyRequirements)
.WithMany(t => t.Assets)
.Map(m =>
{
m.MapRightKey("SafetyRequirementId");
m.MapLeftKey("AssetId");
m.ToTable("AssetSafetyRequirement");
});
Finally here's the area that it fails...
public virtual void SaveAsync(TEntity model)
{
Task.Run(() =>
{
using (
var dbContext =
(TContext)
Activator.CreateInstance(typeof (TContext),
ConfigOptions == null ? ConfigService.ConnectionString : ConfigOptions.ConnectionString))
{
var dbSet = dbContext.Set<TEntity>();
dbSet.Attach(model);
dbContext.Entry(model).State = EntityState.Modified;
dbContext.SaveChanges();
}
});
}
Any information or pointers would be greatly appreciated.
You're trying to use both Fluent API and Data Annotations to define the relationships between your tables. Remove one or the other.

Entity Framework Unable to determine principal end of relationship. Multiple added entities may have same primary key

I'm having some issues with EF.
Migrations go fine, but when I try to run update-database I get the error :
Unable to determine the principal end of the 'LeagueInsight.Models.Image_Passive' relationship. Multiple added entities may have the same primary key.
Here are my models and config:
Passive.cs
public class Passive
{
public long Id { get; set; }
public string Description { get; set; }
public string Name { get; set; }
public string SanitizedDescription { get; set; }
// Navigation
public virtual Image Image { get; set; }
public virtual Champion Champion { get; set; }
}
Image.cs
public class Image
{
public long Id { get; set; }
public string Full { get; set; }
public string Group { get; set; }
public int H { get; set; }
public string Sprite { get; set; }
public int W { get; set; }
public int X { get; set; }
public int Y { get; set; }
// Navigation
public virtual Champion Champion { get; set; }
public virtual ChampionSpell ChampionSpell { get; set; }
public virtual Passive Passive { get; set; }
}
DBContext Configuration Partial:
modelBuilder.Entity<Passive>()
.HasRequired(p => p.Champion)
.WithRequiredPrincipal(c => c.Passive);
modelBuilder.Entity<Info>()
.HasRequired(i => i.Champion)
.WithRequiredPrincipal(c => c.Info);
modelBuilder.Entity<Image>()
.HasOptional(i => i.Champion)
.WithRequired(c => c.Image);
modelBuilder.Entity<Image>()
.HasOptional(i => i.Passive)
.WithRequired(p => p.Image);
base.OnModelCreating(modelBuilder);
It is just supposed to be a 1-1 relationship between the two, an I can't figure out why there would be multiple entities with the same Id here.
Edit: I was asked for the champion class:
public class Champion
{
public int Id { get; set; }
public string Blurb { get; set; }
public string Key { get; set; }
public string Lore { get; set; }
public string Name { get; set; }
public string Partype { get; set; }
public string Title { get; set; }
// Navigation
[InverseProperty("Ally")]
public virtual ICollection<Tip> Allytips { get; set; }
[InverseProperty("Enemy")]
public virtual ICollection<Tip> Enemytips { get; set; }
public virtual Image Image { get; set; }
public virtual Info Info { get; set; }
public virtual Passive Passive { get; set; }
public virtual ICollection<Recommended> Recommended { get; set; }
public virtual ICollection<Skin> Skins { get; set; }
public virtual Stats Stats { get; set; }
public virtual ICollection<ChampionSpell> Spells { get; set; }
public virtual ICollection<Tag> Tags { get; set; }
}

EF 4.2 Many to many bug?

We are using the ASP.NET membership to manager our user and I found a problem using Entity Framework 4.2
I have the table in db called Aspnet_Users and I Created My class as:
public class Aspnet_User
{
[Key]
public Guid ID { get; set; }
public Guid ApplicationID { get; set; }
public string UserName { get; set; }
public string LoweredUserName { get; set; }
public string MobileAlias { get; set; }
public bool IsAnonymous { get; set; }
public DateTime LastActivityDate { get; set; }
public virtual Aspnet_Membership Aspnet_Membership { get; set; }
public virtual Aspnet_Profile Aspnet_Profile { get; set; }
public virtual ICollection<Aspnet_Role> Aspnet_Roles { get; set; }
public virtual ICollection<Agent> Agents { get; set; }
}
My Entity Configuration:
public class Aspnet_UserConfiguration : EntityTypeConfiguration<Aspnet_User>
{
public Aspnet_UserConfiguration()
: base()
{
HasKey(p => p.ID);
Property(p => p.ID).HasColumnName("UserId").IsRequired();
Property(p => p.MobileAlias).IsOptional();
HasOptional(p => p.Aspnet_Profile).WithRequired();
HasOptional(p => p.Aspnet_Membership).WithRequired();
HasMany(p => p.Aspnet_Roles).WithMany(a => a.Aspnet_Users).Map(mc =>
{
mc.MapLeftKey("UserId");
mc.MapRightKey("RoleId");
ToTable("aspnet_UsersInRoles");
});
HasMany(p => p.Agents).WithMany(a => a.Aspnet_Users).Map(mc =>
{
mc.MapLeftKey("UserId");
mc.MapRightKey("AgentId");
ToTable("aspnet_UsersAgent");
});
ToTable("aspnet_Users");
}
And Finally my Agent table:
public class Agent
{
[Key]
public int ID { get; set; }
public string Title { get; set; }
public string FirstName { get; set; }
public string Surname { get; set; }
public string ShortBio { get; set; }
public string ExtendedBio { get; set; }
public string Tel { get; set; }
public string Fax { get; set; }
public string Email { get; set; }
public string CompanyName { get; set; }
public string ImageURL { get; set; }
public string BranchName { get; set; }
public string UserPassword { get; set; }
public DateTime? LastLogin { get; set; }
public bool? Active { get; set; }
public bool ShowMap { get; set; }
public string BatchNoPrefix { get; set; }
public string FullName
{
get { return string.Format("{0} {1}", FirstName, Surname); }
}
public virtual AgentsAddress Address { get; set; }
public virtual ICollection<PaymentNotification> PaymentNotifications { get; set; }
public virtual ICollection<Authority> Authorities { get; set; }
public virtual ICollection<Aspnet_Users> Aspnet_Users { get; set; }
}
The problem is: When I try to select my Aspnet_User in the class Agent, I'm getting a error:
Invalid object name 'dbo.Aspnet_UserAgent'.
Description: An unhandled exception occurred during the execution of the current web request. Please review the stack trace for more information about the error and where it originated in the code.
Exception Details: System.Data.SqlClient.SqlException: Invalid object name 'dbo.Aspnet_UserAgent'.
I figured out that this error occur because my classname is in singular: Aspnet_User instead of Aspnet_Users.
But if I created the relationship inside my Configuration(see class aspnet_UserConfiguration), the system should not try to get aspnet_UsersAgent? Why is it try to get dbo.ClassName1_ClassName2? Is It a bug?
Thank you in advance.

Categories