public class User
{
public int UserId { get; set; }
public string Name { get; set; }
public int BillingAddressId { get; set; }
public int DeliveryAddressId { get; set; }
public Address BillingAddress { get; set; }
public Address DeliveryAddress { get; set; }
}
public class Address
{
public int AddressId { get; set; }
public string Street { get; set; }
public string City { get; set; }
public string ZipCode { get; set; }
}
public class Context : DbContext
{
public DbSet<User> Users { get; set; }
public DbSet<Address> Addresses {);
}
public class User
{
public int UserId { get; set; }
public string Name { get; set; }
public int BillingAddressId { get; set; }
public int DeliveryAddressId { get; set; }
[ForeignKey("BillingAddressId")]
public Address BillingAddress { get; set; }
[ForeignKey("DeliveryAddressId")]
public Address DeliveryAddress {).WillCascadeOnDelete(false);
}
protected override void Seed(Context context)
{
context.Database.ExecuteSqlCommand("ALTER TABLE Users ADD CONSTRAINT uc_Billing UNIQUE(BillingAddressId)");
context.Database.ExecuteSqlCommand("ALTER TABLE Users ADD CONSTRAINT uc_Delivery UNIQUE(DeliveryAddressId)");
}
Nice article.
But not able insert records.
Getting error that u should provide values of parent table which is not null.
Can you please tell me why this is happening.?
POCOs:
public class User{ public Guid UserId { get; set; } //have to use Guid as it maps to aspnet db public int CampaignId { get; set; } public virtual ICollection<UserImage> { get; set; }}
public class UserImage{ public int UserImageId { get; set; } public Guid UserId { get; set; } public int CampaignId { get; set; }}
public class Campaign{ public int CampaignId { get; set; } public virtual ICollection<User> Users { get; set; }}
User Mapping:
HasMany(u => u.UserImages) .WithOptional() .WillCascadeOnDelete();
HasMany(u => u.UserCampaigns) .WithOptional() .WillCascadeOnDelete();
Campaign Mapping:
HasKey(c => c.CampaignId) .HasMany(c => c.Users) .WithMany(u => u.Campaigns) .Map(m => m.MapLeftKey("UserId"));
No mapping for UserImage
Morteza,
So the user object has a campaigns collection and in my case, it will contain one campaign in the collection. The UserImage has a property called CamapaignId. Would I have to set that manually? If so, any other way I can remodel my classes so the CampaignId gets set automatically when adding a UserImage?
Thank you for taking the time. I got you and it makes sense assuming you only have 1:1 relationship. What I was trying to say (unsuccessfully) is that there could be possibilities of multiple campaigns. So it's actually many-to-many between user and campaign. So I think I'm left to setting the CampaignId manually. I even tried creating a dynamic property called ActiveCampaign which always returns the current campaign but it's useless and is not forcing the CampaignId to be set.
I welcome any other thoughts.
Hello,
I'm trying to use this approach to achieve a one-to-one association. In my case there's an association between a User and a Team, and I need a navigation property in each of them.
I bumped into a problem when adding data.
This is what the models look like:
public class Team{ public int ID { get; set; } public string Name { get; set; } public int OwnerID { get; set; }
public virtual User Owner { get; set; }}
public class User{ public int ID { get; set; } public string UserName { get; set; } public int TeamID { get; set; }
public virtual Team Team { get; set; }}
I added these bits to the DBContext:
modelBuilder.Entity<User>() .HasRequired(u => u.Team) .WithMany() .HasForeignKey(u => u.TeamID);
modelBuilder.Entity<Team>() .HasRequired(t => t.Owner) .WithMany() .HasForeignKey(t => t.OwnerID) .WillCascadeOnDelete(false);
And now when adding data like this:
u = new User();u.UserName = "farinha"; t = new Team("Flour Power");u.Team = t;t.Owner = u;
context.Users.Add(u);context.Teams.Add(t);context.SaveChanges();
or even like this:
u = new User();u.UserName = "farinha";u.Team = new Team("Flour Power");
context.Users.Add(u);context.SaveChanges();
I'm getting the following error:
Unable to determine a valid ordering for dependent operations. Dependencies may exist due to foreign key constraints, model requirements, or store-generated values.
Any idea of how to solve this? Or should I be adding the data in some different way?
Thanks in advance
Thanks for the reply. I believe I managed to solve this by making a few changes:
stackoverflow.com/.../entity-framework-saving-data-in-one-to-one-associations
These one-to-one associations are somewhat weird and uncommon, and that's why I'm a bit lost as to how to implement this one. According to the sample of the model I described, do you reckon a shared primary key association would be a better option? Thanks.
public class User{ public int ID { get; set; } public string UserName { get; set; }
[NotMapped] public Team Team { get; set; }}
public class Context : DbContext{ public Context() { ((IObjectContextAdapter) this).ObjectContext.ObjectMaterialized += OnObjectMaterialized; }
public DbSet<Team> Teams { get; set; } public DbSet<User> Users { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder) { modelBuilder.Entity<Team>() .HasRequired(t => t.Owner) .WithMany() .HasForeignKey(t => t.OwnerID); }
private void OnObjectMaterialized(object sender, ObjectMaterializedEventArgs e) { if (e.Entity is User) { User user = (User) e.Entity; user.Team = this.Teams.Single(t => t.OwnerID == user.ID); } }}
Uff... I'm getting even more confused as to what option should I follow.
In practice, both the User and the Team entities will be created at the same time, never deleted, and a Team won't ever change user. There will be exactly one Team per User and exactly one User per team. I'll need to have a way to navigate to the Team from the User and vice-versa.
I'm almost leaning towards having it all in the same table as the same entity, but it doesn't make a lot of sense from a OO point of view, if you're thinking about each object as a "real-world" Entity.
And thinking forwards, maybe a User will be allowed to have more than one Team in the future (maybe). So having them in separate tables would be good if I ever decide to change.
Great to see you finish this second series on the 4.1 release. Extremely helpful!
I'm wondering how you might enforce a 1 to 0..1 relationship on the DB though? Consider a uni-directional relationship between Person and User, where a Person has public int? UserId and public User User (you cannot navigate from User to Person).
EF maps this as a one-to-many, where technically in the DB a User can have 0..n People. In this case putting a unique index constraint on Person.UserId won't work, as the DB will throw a DbException as soon as you try and insert a second Person row with null UserId.
Triggers? Or is there something easier?
public class Person{ public int PersonId { get; set; } public string Name { get; set; } public int? UserId { get; set; }
public User User { get; set; }}
public class Context : DbContext{ public DbSet<User> Users { get; set; } public DbSet<Person> Persons { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder) { modelBuilder.Entity<Person>() .HasOptional(p => p.User) .WithMany() .HasForeignKey(u => u.UserId); }}
And here is how we can create a filtered index to enforce an optional one-to-one relationship between User and Person:
protected override void Seed(Context context){ context.Database.ExecuteSqlCommand("CREATE UNIQUE NONCLUSTERED INDEX idx_UserId_NotNULL ON People(UserId) WHERE UserId IS NOT NULL"); }
Now you can have as many Persons as you want with a NULL UserId while it still disallows Persons with a same UserId. Thanks for your comment and hope this helps :)
OK I see now. The the "shared" 1:1 scenario is where you have PK of one table related to PK of another table.
This scenario is also 1:1 but PK of one table relates to FK of another table. And the approach is to model n:1 and then convert it to 1:1 in the db, using a unique constraint.
This is what I don't get: I can model it as 1:1 and it still works for me? Unless I am doing something wrong. I can do this:
1) .HasRequired(q => q.SomeEntity)
.WithRequiredPrincipal() <-- note I didn't use .WithMany()
.Map(m => m.MapKey("SomeOtherEntityID"))
.WillCascadeOnDelete(true);
2) set unique constraint in db
So I model a 1:1, and I exactly that, and I also get cascade deletes without using triggers. I trust your code, so I don't understand why mine is working? I am using EF4.1.
All of this is confusing. I guess its because EF doesnt really support 1:1 FK relations.
In my scenario i have a main class that has many related 1:1 classes.
A 1-1 B
A 1-1 C
A 1-1 D
C 1-* E
what i need is to be able to have A.B .. and B.A .. so that i can do B.A.C.SomeProp += someval etc.
At this point, i'm not 100% sure which direction i should take.
Oh, and i'm working off an existing database and getting relations done properly is a nightmare when it should be simple. What i don't understand is why FK isnt enough. (I'm also thinking of using lists and having a property that takes the first element of the list for the many side which i want as a 1 side)
There also seems to be some importance into how the entities are created
A = new A() { B = new B() }
context.AList.Add(A)
may work but
B = new B() { A = new A() }
context.BList.Add(B)
could fail.
So there seems to be a logical way to set members that makes sense depending on how the relations are set which is not explained anywhere. At least i havent found somewhere explaining it.
Is this something you plan on explaining at some point ?
This helped me tackle the fluent api configuration for my model. Thank you very much!
Thanks and "Damet Garm" ;)
You are always doing optional your foreign keys via fluent api but why don't you mention that we can also achieve it via Nullable types. I feel that it is much better to make it nullable because model define itself more well without any configuration. Is there any downside to prefer nullable type for making foreign key optional ?
Yakup you want to read Fred's question and Morteza's answer. This 2 posts only could make another blog serie. Morteza thank you for the effort.
Awesome post. Thanks a ton!!!
I found this post very useful in understanding one-to-one relationships in EF CF. I too have an instance where Class X has two properties of type Class Y and I have followed the example here closely. At first, it all seemed to work - I get my not null on the columns, I get the correct Ids and these are correctly set. However, when performing an integration test to check that both properties have been set, I leave one as null and expect it to throw an exception. But it doesn't. Instead, it copies the values and id of the not-null version. So I end up with, for instance, the billing address and the delivery address the same if one is null which is not the intended behaviour.
Has anyone else noticed this behaviour at all?
Regards