I have the following entities:
public class User
{
[Key]
public int UserId { get; set; }
virtual public ICollection<Transaction> Transactions { get; set; }
}
public class Transaction
{
[Key]
public Int64 TransactionId { get; set; }
[Required]
public virtual User Sender {get; set;}
public virtual User Receiver { get; set; }
}
The relationship is described in fluent API
modelBuilder.Entity<User>()
.HasMany(r => r.Transactions)
.WithRequired(s => s.Sender);
modelBuilder.Entity<User>()
.HasMany(r => r.Transactions)
.WithOptional(r => r.Receiver);
There are two users, one is a sender of the transaction the second is the receive.
Now when I add a transaction to the first user everything works. When the second user accepts the transaction and I add the same transaction to its ICollection of transactions it magically disappears from the first user and vise versa. In other words EF prevents me from referencing the same entity in both parents. Is there way around it ?
You need to have two navigation properties in your User Class.
public class User
{
[Key]
public int UserId { get; set; }
virtual public ICollection<Transaction> SenderTransactions { get; set; }
virtual public ICollection<Transaction> ReceiverTransactions { get; set; }
}
Each navigation property must have equivalent navigation property in related class.
modelBuilder.Entity<User>()
.HasMany(r => r.SenderTransactions)
.WithRequired(s => s.Sender);
modelBuilder.Entity<User>()
.HasMany(r => r.ReceiverTransactions)
.WithOptional(r => r.Receiver);
Related
I'm working on a database where tables have composite keys and part of that key is shared between tables. I don't know how to set the relationship properly in entity.
Imagine the following:
public class Sale
{
public long ID { get; set; } //Key
public long RetailerID { get; set; } //Key
public virtual Location Location { get; set; } //Foreign, Many-to-One
}
public class Location
{
public long ID { get; set; } //Key
public long RetailerID { get; set; } //Key
public virtual IEnumerable<Sale> Sales { get; set; } //Relationship, One-to-Many
}
Both are using the fluent API to define the composite keys OnModelCreating.
modelBuilder.Entity<Sale>().HasKey(x => new { x.RetailerID, x.ID });
modelBuilder.Entity<Location>().HasKey(x => new { x.RetailerID, x.ID });
However I am unsure how to finish this to set up the proper relationship as it sets itself up as having duplicate columns for RetailerID which is unnecessary. How is this supposed to be done properly (if at all?)
It's possible in several ways, all including additional LocationID FK property (either explicit or shadow).
With shadow FK property (without modifying the entity model):
Data Annotations:
[Required]
[ForeignKey("RetailerID, LocationID")]
public virtual Location Location { get; set; } //Foreign, Many-to-One
Fluent API:
modelBuilder.Entity<Sale>()
.HasOne(e => e.Location)
.WithMany(e => e.Sales)
.HasForeignKey("RetailerID", "LocationID")
.IsRequired();
with explicit FK property
Model:
public long LocationID { get; set; } // added
public virtual Location Location { get; set; } //Foreign, Many-to-One
Data annotations:
[ForeignKey("RetailerID, LocationID")]
public virtual Location Location { get; set; } //Foreign, Many-to-One
Fluent API:
modelBuilder.Entity<Sale>()
.HasOne(e => e.Location)
.WithMany(e => e.Sales)
.HasForeignKey(e => new { e.RetailerID, e.LocationID });
(note: use either data annotations or fluent API - no need for both)
I am trying to do a database model for my social network school project. I am using Entity Framework 6. The problem is how should I model my Friendship and Chat entity.
I get this error:
Unhandled Exception:
System.Data.Entity.ModelConfiguration.ModelValidationException: One or more validation errors were detected during model generation:
Friendship_Chat_Source: : Multiplicity is not valid in Role 'Friendship_Chat_Source' in relationship 'Friendship_Chat'. Because the dependent role properties are not the key properties, the upper bound of the multiplicity of the dependent role must be '*'.
It has probably something to do with this: EF Code-First One-to-one relationship: Multiplicity is not valid in Role * in relationship
I don't really get it how should I do it here. Even if I get rid off this error somehow (different db model), entity framework in Friendship table creates User_Id column as a foreign key, and I dont really know why it does it (I don't want it there). But I really feel that my model should look like this, not different. So I want to really figure out, how should I edit this model. However if you have different model idea I would also appreciate it.
User entity:
public class User
{
public int Id { get; set; }
[Required, MinLength(1), MaxLength(50)]
public string NickName { get; set; }
[Required, MinLength(6), MaxLength(50)]
public string PasswordHash { get; set; }
#region Settings
//true if anyone can see user posts
public Visibility PostVisibilityPreference { get; set; } = Visibility.Visible;
#endregion
#region Navigation properties
public virtual HashSet<Friendship> Friendships { get; set; }
public virtual HashSet<Post> Posts { get; set; }
public virtual HashSet<GroupUser> GroupUsers { get; set; }
public virtual HashSet<Comment> Comments { get; set; }
#endregion
}
Friendship entity:
public class Friendship
{
#region Primary keys
public int User1Id { get; set; }
public int User2Id { get; set; }
#endregion
[Required]
public DateTime FriendshipStart { get; set; }
#region Foreign keys
//defined using fluent api in MyDbContext:
//User1Id
//User2Id
//and
[ForeignKey("Chat")]
public int? ChatId { get; set; }
#endregion
#region Navigation properties
public virtual User User1 { get; set; }
public virtual User User2 { get; set; }
public virtual Chat Chat { get; set; }
#endregion
}
With this overrided function OnModelCreating in MyDbContext:
protected override void OnModelCreating(DbModelBuilder builder)
{
builder.Entity<Friendship>()
.HasKey(k => new { k.User1Id, k.User2Id });
builder.Entity<Friendship>()
.HasRequired(u => u.User1)
.WithMany()
.HasForeignKey(u => u.User1Id);
builder.Entity<Friendship>()
.HasRequired(u => u.User2)
.WithMany()
.HasForeignKey(u => u.User2Id)
.WillCascadeOnDelete(false);
}
Chat entity:
public class Chat
{
public int Id { get; set; }
#region Foreign keys
public int? FriendshipUser1Id { get; set; }
public int? FriendshipUser2Id { get; set; }
#endregion
#region Navigation properties
public virtual HashSet<Message> Messagges { get; set; }
[ForeignKey("FriendshipUser1Id, FriendshipUser2Id")]
public virtual Friendship Friendship { get; set; }
#endregion
}
If I understand your model right. 1 friendship has 1 user1 and 1 user2. Your settings are excluded.
builder.Entity<Friendship>()
.HasRequired(u => u.User1)
.WithMany()
.HasForeignKey(u => u.User1Id);
It means user1 has many... (many what?). Model doesn't have any list that could accept these settings. Your model has only single objects.
If you want it one to one/zero:
builder.Entity<Friendship>()
.HasRequired(u => u.User1)
.WithRequiredDependant(u => u.User2)
or
.WithRequiredPrincipal(u => u.User2)
or
.WithOptional(u => u.User2)
You can also try composite key for example: Creating Composite Key Entity Framework
Also I suggest you to use or Fluent API or Data Annotation convention. It will make your code more readable.
I have 2 model classes for Users and Organizations.
public class User : IdentityUser
{
[Required]
public string Name { get; set; }
[Required]
public string Surname { get; set; }
public int? OrganizationID { get; set; }
public virtual OrgList org { get; set; }
}
public class OrgList
{
public OrgList()
{
employees = new HashSet<User>();
}
public int id { get; set; }
public String name { get; set; }
public String ownerId { get; set; }
public virtual ICollection<User> employees { get; set; }
public virtual User ownerUser { get; set; }
}
User can be owner of some organization and also he is employee of the same organization (But other employees can't be owners of the organization).
First i've created a relationship for employees and it works OK
modelBuilder.Entity<OrgList>(entity =>
{
entity.HasMany(e => e.employees)
.WithOne(e => e.org)
.HasForeignKey(e => e.OrganizationID)
.OnDelete(DeleteBehavior.SetNull);
}
but when i try to add another relationship for owner
entity.HasOne(e => e.ownerUser)
.WithOne(e => e.org)
.HasForeignKey<OrgList>(e => e.ownerId)
.OnDelete(DeleteBehavior.Cascade);
i have an error on migration:
Cannot create a relationship between 'User.org' and
'OrgList.ownerUser', because there already is a relationship between
'OrgList.employees' and 'User.org'. Navigation properties can only
participate in a single relationship.
How can i fix it? I've found an answers for EF6 (not EF Core) with HasOptional() and WithOptionalPrincipal() methods that not exist in EF Core.
Can i do it without creating additional table for employees or without creating additional virtual OrgList on User class?
You're trying to create the owner relationship with the same property on the user that you are using for the employee relationship. Entity framework wouldn't know which relationship to assign the property. If you created another property on the user like
public int? OwnedOrganizationID { get; set; }
public virtual OrgList OwnedOrg { get; set; }
and change the statement to
entity.HasOne(e => e.ownerUser)
.WithOne(e => e.OwnedOrg)
.HasForeignKey<OrgList>(e => e.ownerId)
.OnDelete(DeleteBehavior.Cascade);
I imagine it should work.
I'm currently creating my first "complex" application with Entity Framework, and I'm encountering my first problem.
I have two entities: Users and Events, a defined User can either organize an Event or be a participant in an Event. I would like to implements relationship between these entities in a manner that, for a defined user, I can either retrieve all the events organized by him or retrieve all the events he subscribed for.
Event.cs
public virtual User Organizer { get; set; }
public List<User> Participants { get; set; }
User.cs
public virtual ICollection<Event> EventsOrganized { get; set; }
public virtual ICollection<Event> EventsSubscribedFor { get; set; }
How can I specify that the EventsOrganized should refer to the Organizer property, and the EventsSubscribedFor should refer to the Participants property?
I am going to assume you can you Fluent API.
In your DbContext class create or add to your OnModelCreating override
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<User>()
.HasMany(s => s.EventsOrganized)
.WithRequired(c => c.Organizer)
.WillCascadeOnDelete(false);
modelBuilder.Entity<User>()
.HasMany(s => s.EventsSubscribedFor)
.WithMany(c => c.Participants)
.Map(cs =>
{
cs.MapLeftKey("UserId");
cs.MapRightKey("EventId");
});
base.OnModelCreating(modelBuilder);
}
What is going on is I am telling the DbContext that the User Entity has many EventOrganized with a required Organizer, and then telling the DbContext to not cascade deletes.
Then I am telling the DbContext that the User Entity has many EventsSubscribedFor to Many Participants. Then I map the left and right keys. This creates a table called "UserEvents", you could name it something else by saying cs.ToTable("NameOfTable");
Also For reference EntityframeworkTutorials helped me learn about the Fluent API, and these are the entities I used to test.
public class User
{
public User()
{
EventsOrganized = new HashSet<Event>();
EventsSubscribedFor = new HashSet<Event>();
}
[Key]
public int Id { get; set; }
public string Name { get; set; }
public virtual ICollection<Event> EventsOrganized { get; set; }
public virtual ICollection<Event> EventsSubscribedFor { get; set; }
}
public class Event
{
public Event()
{
Participants = new HashSet<User>();
}
[Key]
public int Id { get; set; }
public string Name { get; set; }
public int OrganizerId { get; set; }
public virtual User Organizer { get; set; }
public ICollection<User> Participants { get; set; }
}
Hi I have following entities:
public class EntityOne
{
public int EntityOneID { get; set; }
public string Smth { get; set; }
public string User1Id { get; set; }
public string User2Id { get; set; }
public virtual ApplicationUser User1 { get; set; }
public virtual ApplicationUser User2 { get; set; }
}
public class ApplicationUser : IdentityUser
{
public virtual ICollection<EntityOne> EntityOnes { get; set; }
}
I configured Relationships
modelBuilder.Entity<EntityOne>()
.HasRequired<ApplicationUser>(s => s.User1)
.WithMany(s => s.EntityOnes)
.HasForeignKey(s => s.User1Id).WillCascadeOnDelete(false);
modelBuilder.Entity<EntityOne>()
.HasRequired<ApplicationUser>(s => s.User2)
.WithMany(s => s.EntityOnes)
.HasForeignKey(s => s.User2Id).WillCascadeOnDelete(false);
When I delete one of ApplcationUser from my entity everything is OK, but if I have 2 Application users, it gives me following error:
Schema specified is not valid. Errors: The relationship 'SomeProject.Models.EntityOne_User1' was not loaded because the type 'SomeProject.Models.ApplicationUser' is not available.
I want to 2 different users having relation with "EntityOne"
EDIT: Propably answer:
public class ApplicationUser : IdentityUser
{
public virtual ICollection<EntityOne> EntityOnes1 { get; set; }
public virtual ICollection<EntityOne> EntityOnes2 { get; set; }
}
When I created 2 Collections in ApplicationUser everything works.
You can't have two different relationships associated with same property.
Navigation properties provide a way to navigate an association between two entity types. Every object can have a navigation property for every relationship in which it participates.
https://msdn.microsoft.com/en-us/data/jj713564.aspx