EF Migrations: Add additional indirection inbetween an existing 1-to-n relationship - c#

Imagine this scenario: A person owns a set of cars. Currently I have:
User (1) -- (N) Cars
Now I want to modify my tables to have a user own garages instead of cars and the garages own a subset of cars, i.e. add a simple additional indirection:
User (1) -- (N) Garages (1) -- (N) Cars
The actual data can be dropped that is okay. It is simply that theres other parts to that database that contain data that should be kept. This part is complety isolated and can be erased. The example is made up, because I am unsure what I can tell but it is really that simple.
I have no clue about migrations and struggle hard with this. Thanks a lot for any help, I appreciate it!
Edit: What I have is:
public class OwnerEntity
{
[Key]
public Guid Id { get; set; }
[Required]
public string Name { get; set;}
public List<CarEntity> OwnedCars { get; set; }
// Rather would have:
// public List<GarageEntity> OwnedGarages { get; set; }
}
public class CarEntity
{
[Key]
public Guid Id { get; set; }
[Required]
public OwnerEntity Owner { get; set; }
public Guid OwnerId { get; set; }
// Would not be directly owned anymore but would have a garage as an owner
[Required]
public string ModelDescr { get; set; }
}
// In the db context
public DbSet<OwnerEntity> Owners { get; set; }
public DbSet<CarEntity> Cars { get; set; }
protected override void OnModelCreating(ModelBuilder model)
{
// This would simply duplicate. Once for Car <-> Garage and once for Garage <-> Owner entitities
model.Entity<CarEntity>()
.HasOne(x => x.Owner)
.WithMany(x => x.OwnedCars)
.HasForeignKey(x => x.OwnerId)
.OnDelete(DeleteBehavior.Cascade);
}
The main problem I have when I try to change something here is: The database still keeps the old tables and schema. Without dropping it there appear coinflicts (kind of expected).
There are also more entities following attached to Car (to use my example) that I want to keep (their schema). I tried to comment out the DBSets to have EF drop the tables by itself and then do my changes and have it do the actual migration but that isn't really working it's to much work and just stupid..
Hope that helps more!

I realized the issue was that there were still existing elements in the tables, where I was trying to change foreign key relationships.
The solution was very simple: I have created an empty migration and simply erased all the content of those tables manually (due to cascading delete behaviour I only had to delete one table).
After this I could alter any columns the way I wanted because there was no data left to cause conflicts. Quite a stupid problem but it, I couldn't wrap my head around it for quite a while.

Related

Entity Framework Core: one-to-one configuration

I updated the title to be more relevant.
I have two objects and wish to use EF Core to map them to a database using code first.
I have shortened them a bit for brevity.
WeddingDetails
{
VenueDetails ReceptionDetails { get; set; }
VenueDeails WeddingDetails { get; set; }
}
VenueDetails
{
string StreetAddress { get; set; }
string PostCode { get; set; }
public int WeddingDetailsId { get; private set; }
public WeddingDetails WeddingDetails { get; private set; }
}
However when attempting to do the initial migration I am getting the following error from Entity Framework Core:
Unable to determine the relationship represented by navigation 'WeddingDetails.ReceptionDetails' of type 'VenueDetails'. Either manually configure the relationship, or ignore this property using the '[NotMapped]' attribute or by using 'EntityTypeBuilder.Ignore' in 'OnModelCreating'.
The issue is that both receptiondetails and weddingdetails are the same object and this is causing a conflict when Entity Framework Core tries to convert this to a migration, I believe.
Could anyone please help me understand what configuration options I must add to the OnModelCreating method using Fluent API/changes I need to make to the objects to smooth out this conflict, thanks.
Edit
I actually resolved this by creating a parent class that both reception and wedding derived from, entity framework was then able to differentiate the two objects and kept them in the table using a discriminator.
So I updated it like below
VenueDetails
{
string StreetAddress { get; set; }
string PostCode { get; set; }
}
WeddingVenueDetails : VenueDetails
{
}
ReceptionVenueDetails : VenueDetails
{
}
EF was then able to parse this. The reason I have given the answer to Steve, instead of answering this myself, is that this was not the approach I have decided to go with, but more what he suggested, of actually breaking both of the classes into their own objects to allow for future flexibility and decoupling
The relationships here are a little bit backwards in terms of where the FK in the relationship will be located. One-to-One relationships are normally associated by PKs on both sides, however EF can be configured to establish a one -to-one relationship using either a one-to-many db-side relationship (FK on Right-side table) or a many-to-one db-side relationships (FK on Left-side table)
For example, given your intended structure /w Wedding and Venues, you want a single Wedding to point to a WeddingVenue and a ReceptionVenue. The next question would be where the FKs should reside. In the case of the WeddingVenue you have set this up as a one-to-many FK with the WeddingID in the Venue table. However, this leaves the ball hanging for how the database is going to figure out the FK for the ReceptionVenue.
You can solve this by swapping to a many-to-one FK where the WeddingVenueId and ReceptionVenueId are part of the Wedding table/entity. If we expose the FK properties as an example in the entity:
public class WeddingDetails
{
[Key]
public int WeddingDetailsId { get; set; }
// ... wedding fields...
public int WeddingVenueId { get; set; }
public int ReceptionVenueId { get; set; }
[ForeignKey("WeddingVenueId")]
public virtual VenueDetails WeddingVenueDetails { get; set; }
[ForeignKey("ReceptionVenueId")]
public virtual VenueDetails ReceptionVenueDetails { get; set; }
}
Now this technically sets up two many-to-one relationships where the model configuration would look something like:
modelBuilder.Entity<WeddingDetails>()
.HasOne(x => x.WeddingVenueDetails)
.WithMany();
However, we want a WeddingDetails instance on VenueDetails. There is another consideration in that ReceptionVenueDetails is a VenueDetails, that venue will not have a WeddingDetails reference, so it probably makes sense to leave this relationship as a many-to-one.
The issue here is that there is nothing stopping the same venue record from:
Being used for both the Wedding venue and Reception venue.
Being used as the Wedding and/or Reception venue for a completely different wedding.
The implications of this is that if two weddings get linked to the same venue record, updates on one wedding's venue would be reflected on any other weddings linked to that same record. Even if we were to configure EF to expect these to be a one-to-one relationship (which we can using EF Core 5/6 using HasPrincipalKey) the fact is that the database FK constraints will always allow two weddings to reference the same Venue record. This forms a Faux one-to-one relationship.
The way to enforce this as a proper one-to-one would be to either have the venue details (for both wedding and reception) embedded in the Wedding table (as an same-table owned type in EF Core) or, establish a WeddingVenueDetails as a one-to-one relationship table. (Either using a standard one-to-one PK relationship or separate table owned type)
What this would look like from entities:
public class WeddingDetails
{
[Key]
public int WeddingDetailsId { get; set; }
// ... wedding fields...
public virtual WeddingVenueDetails WeddingVenueDetails { get; set; }
}
public class WeddingVenueDetails
{
[Key]
public int WeddingDetailsId { get; set; }
string WeddingStreetAddress { get; set; }
string WeddingPostCode { get; set; }
string ReceptionStreetAddress { get; set; }
string ReceptionPostCode { get; set; }
public virtual WeddingDetails WeddingDetails { get; set; }
}
Here WeddingVenueDetails is responsible for both the wedding and reception venue details for a single, specific wedding. We can set up a proper one-to-one relationship between the two:
modelBuilder.Entity<WeddingDetails>()
.HasOne(x => x.WeddingVenueDetails) // or OwnsOne()
.WithOne(x => x.WeddingDetails);
In the database table, the WeddingDetailsId serves as the unique PK between both tables and the FK constraint. This enforces a pure one-to-one relationship where any Wedding's venue details can be safely edited independently of possibly affecting any other wedding's venue.
Similarly if you want to separate the WeddingVenue and ReceptionVenue, you can do this, however each would need its own separate table with a WeddingDetailsID as the PK and set up with the same HasOne/WithOne. (WeddingVenueDetails and ReceptionVenueDetails tables) They cannot be the same VenueDetails table/entity to enforce the pure one-to-one relationship with a WeddingDetails. (There is no such thing as a One-to-Two or One-to-Zero-or-Two relationship.) This may seem non-ideal since these two tables would effectively share the same columns, but from a storage POV it's essentially the same whether 2 records get stored in 1 table, or 1 record gets stored in each of two tables. It also accommodates future flexibility where you may have venue fields that apply only to Weddings or Receptions which can be added to their appropriate table without resorting to conditional Null-able columns. Worst case you end up having to resort to null-able columns that are implied to be required by one or the other with nothing to actually enforce that in the database. Separate tables are also better if something like the Reception is optional to avoid rows with a bunch of empty columns if in a single Venues table.

Is it possible in EF Core to make a one-way navigation property required?

I am working on a basic group chat system, for which I created these classes:
public class Role
{
public Guid Id { get; set; };
public string Username { get; set; }
}
public class Message
{
public Guid Id { get; set; };
public Role Author { get; set; }
public Conversation Conversation { get; set; }
public DateTime Date { get; set; }
public string Text { get; set; }
}
public class Conversation
{
public Guid Id { get; set; };
public IList<ConversationParticipant> ConversationParticipants { get; set; };
public IList<Message> Messages { get; set; };
}
public class ConversationParticipant
{
public Conversation Conversation { get; set; }
public Role Role { get; set; }
}
We are using EF Core 3.1 Code-First with migrations.
I am looking for a way to make Message.Author a required property, which should lead to a column in table Message that is created as AuthorId NOT NULL.
I tried:
public static void Map(this EntityTypeBuilder<Message> builder)
{
builder.HasOne(m => m.Author);
}
As this is applied using Add-Migration and Update-Database, the database column AuthorId is created, but with NULLs allowed.
There does not seem to be a method IsRequired() that I can add after HasOne().
I also tried:
public static void Map(this EntityTypeBuilder<Message> builder)
{
builder.Property(m => m.Author).IsRequired();
}
but that fails saying
The property 'Message.Author' is of type 'Role' which is not supported by current database provider. Either change the property CLR type or ignore the property using the '[NotMapped]' attribute or by using 'EntityTypeBuilder.Ignore' in 'OnModelCreating'.
Doing .HasOne(...) followed by .Property(...).IsRequired() also does not work:
'Author' cannot be used as a property on entity type 'Message' because it is configured as a navigation.
I managed to make Message.Conversation required through this:
public static void Map(this EntityTypeBuilder<Conversation> builder)
{
builder.HasMany(c => c.Messages) // A conversation can have many messages
.WithOne(e => e.Conversation) // Each message belongs to at most 1 conversation
.IsRequired(); // A message always has a conversation
}
However I'd rather not make Role aware of Messages, as I will never want to retrieve Messages directly from a Role (this will happen through Conversations and Participants).
My ultimate question is: Is there a way to make Message.Author required (NOT NULL), without linking Message and Role together in a full 1-to-many relationship with a Messages property in Role?
What about adding Role's foreign key to Message and then requiring that property to not be null? Something like:
// MessageConfiguration.cs
builder.Property(b => b.RoleId).IsRequired()
While the answer by #Ben Sampica was helpful and got me where I needed to be, the comments by #Ivan Stoev provided details and clarity that made me think that a more comprehensive answer would be useful.
There are multiple ways to make a foreign key column required (NOT NULL) in the generated table.
The simplest is to put [Required] on the navigation property:
public class Message
{
// ...
[Required] public Role Author { get; set; }
// ...
}
This will cause EF to create a shadow property AuthorId of type Guid because Message.Author is a Role and Role.Id is of type Guid. This leads to UNIQUEIDENTIFIER NOT NULL in case of SQL Server.
If you omit [Required] then EF will use Guid?, which leads to UNIQUEIDENTIFIER NULL, unless you apply one of the other options.
You can use an explicit Id property with a type that can't be null:
public class Message
{
// ...
public Guid AuthorId { get; set; }
public Role Author { get; set; }
// ...
}
Note (i) - This only works if you follow EF Core shadow property naming rules, which in this case means you must name the Id property nameof(Author) + nameof(Role.Id) == AuthorId.
Note (ii) - This will break if one day you decide to rename Author or Role.Id but forget to rename AuthorId accordingly.
If you can't or don't want to change the Model class, then you can tell EF Core that it needs to treat the shadow property as required:
builder.Property("AuthorId").IsRequired();
The same Notes apply as listed at 2, with the addition that you could now use nameof() to reduce the effort and the risks.
In the end I decided to use the [Required] approach, because
It is simple and descriptive,
No effort needed to think of which shadow property name to use,
No risk of breaking the shadow property name later on.
This may apply sometimes, not always:
Input forms may use the Model class attribute to check if a property is required. However it may be a better approach to build your forms around DTO classes, and then an attribute on an EF Model class may provide no worth for your forms.

Merge properties from mapping table to single class

I have a website that is using EF Core 3.1 to access its data. The primary table it uses is [Story] Each user can store some metadata about each story [StoryUserMapping]. What I would like to do is when I read in a Story object, for EF to automatically load in the metadata (if it exists) for that story.
Classes:
public class Story
{
[Key]
public int StoryId { get; set; }
public long Words { get; set; }
...
}
public class StoryUserMapping
{
public string UserId { get; set; }
public int StoryId { get; set; }
public bool ToRead { get; set; }
public bool Read { get; set; }
public bool WontRead { get; set; }
public bool NotInterested { get; set; }
public byte Rating { get; set; }
}
public class User
{
[Key]
public string UserId { get; set; }
...
}
StoryUserMapping has composite key ([UserId], [StoryId]).
What I would like to see is:
public class Story
{
[Key]
public int StoryId { get; set; }
public bool ToRead { get; set; } //From user mapping table for currently logged in user
public bool Read { get; set; } //From user mapping table for currently logged in user
public bool WontRead { get; set; } //From user mapping table for currently logged in user
public bool NotInterested { get; set; } //From user mapping table for currently logged in user
public byte Rating { get; set; } //From user mapping table for currently logged in user
...
}
Is there a way to do this in EF Core? My current system is to load the StoryUserMapping object as a property of the Story object, then have Non-Mapped property accessors on the Story object that read into the StoryUserMapping object if it exists. This generally feels like something EF probably handles more elegantly.
Use Cases
Setup: I have 1 million stories, 1000 users, Worst-case scenario I have a StoryUserMapping for each: 1 billion records.
Use case 1: I want to see all of the stories that I (logged in user) have marked as "to read" with more than 100,000 words
Use case 2: I want to see all stories where I have NOT marked them NotInterested or WontRead
I am not concerned with querying multiple StoryUserMappings per story, e.g. I will not be asking the question: What stories have been marked as read by more than n users. I would rather not restrict against this if that changes in future, but if I need to that would be fine.
Create yourself an aggregate view model object that you can use to display the data in your view, similar to what you've ended up with under the Story entity at the moment:
public class UserStoryViewModel
{
public int StoryId { get; set; }
public bool ToRead { get; set; }
public bool Read { get; set; }
public bool WontRead { get; set; }
public bool NotInterested { get; set; }
public byte Rating { get; set; }
...
}
This view model is concerned only about aggregating the data to display in the view. This way, you don't need to skew your existing entities to fit how you would display the data elsewhere.
Your database entity models should be as close to "dumb" objects as possible (apart from navigation properties) - they look very sensible as they are the moment.
In this case, remove the unnecessary [NotMapped] properties from your existing Story that you'd added previously.
In your controller/service, you can then query your data as per your use cases you mentioned. Once you've got the results of the query, you can then map your result(s) to your aggregate view model to use in the view.
Here's an example for the use case of getting all Storys for the current user:
public class UserStoryService
{
private readonly YourDbContext _dbContext;
public UserStoryService(YourDbContext dbContext)
{
_dbContext = dbContext;
}
public Task<IEnumerable<UserStoryViewModel>> GetAllForUser(string currentUserId)
{
// at this point you're not executing any queries, you're just creating a query to execute later
var allUserStoriesForUser = _dbContext.StoryUserMappings
.Where(mapping => mapping.UserId == currentUserId)
.Select(mapping => new
{
story = _dbContext.Stories.Single(story => story.StoryId == mapping.StoryId),
mapping
})
.Select(x => new UserStoryViewModel
{
// use the projected properties from previous to map to your UserStoryViewModel aggregate
...
});
// calling .ToList()/.ToListAsync() will then execute the query and return the results
return allUserStoriesForUser.ToListAsync();
}
}
You can then create a similar method to get only the current user's Storys that aren't marked NotInterested or WontRead.
It's virtually the same as before, but with the filter in the Where to ensure you don't retrieve the ones that are NotInterested or WontRead:
public Task<IEnumerable<UserStoryViewModel>> GetForUserThatMightRead(string currentUserId)
{
var storiesUserMightRead = _dbContext.StoryUserMappings
.Where(mapping => mapping.UserId == currentUserId && !mapping.NotInterested && !mapping.WontRead)
.Select(mapping => new
{
story = _dbContext.Stories.Single(story => story.StoryId == mapping.StoryId),
mapping
})
.Select(x => new UserStoryViewModel
{
// use the projected properties from previous to map to your UserStoryViewModel aggregate
...
});
return storiesUserMightRead.ToListAsync();
}
Then all you will need to do is to update your View's #model to use your new aggregate UserStoryViewModel instead of your entity.
It's always good practice to keep a good level of separation between what is "domain" or database code/entities from what will be used in your view.
I would recommend on having a good read up on this and keep practicing so you can get into the right habits and thinking as you go forward.
NOTE:
Whilst the above suggestions should work absolutely fine (I haven't tested locally, so you may need to improvise/fix, but you get the general gist) - I would also recommend a couple of other things to supplement the approach above.
I would look at introducing a navigation property on the UserStoryMapping entity (unless you already have this in; can't tell from your question's code). This will eliminate the step from above where we're .Selecting into an anonymous object and adding to the query to get the Storys from the database, by the mapping's StoryId. You'd be able to reference the stories belonging to the mapping simply by it being a child navigation property.
Then, you should also be able to look into some kind of mapping library, rather than mapping each individual property yourself for every call. Something like AutoMapper will do the trick (I'm sure other mappers are available). You could set up the mappings to do all the heavy lifting between your database entities and view models. There's a nifty .ProjectTo<T>() which will project your queried results to the desired type using those mappings you've specified.

Entity Framework selectively save tracked entities from context and bulk insert others

I'm fairly new to Entity Framework. I've come across a scenario where I have my entities:
public class Company{
public int Id { get; set; }
public string Name { get; set; }
public IList<Project> Projects{ get; set; }
}
public class Project
{
public int Id { get; set; }
public List<ProjectRecord> ProjectRecords { get; set; }
public DateTime ProjectDate { get; set; }
}
public class ProjectRecord
{
public int Id { get; set; }
public virtual Project Project{ get; set; }
public string Status { get; set; }
}
I create my context, load the Employer then pass the employer object to another class which adds a Project to my employer entity, and then I load approximately 300,000 project records from a file and add this list of project records to the project.
Entity Framework is tracking all this, and although it isn't particularly fast it isn't too bad.
The slow part is when I call SaveChanges() on the context. I wanted to be able to detach the list of project Records from my context and save the Project without them, and then use the bulk insert extension provided from https://efbulkinsert.codeplex.com/ to bulk insert the ProjectRecords seperately. However I could not find a way to detach ProjectRecords.
I therefore tried bulk inserting the entire Project entity:
context.BulkInsert(context,
context.ChangeTracker.Entries()
.Where(x => x.State == EntityState.Added)
.Select(x => x.Entity)
.OfType<Project>()
.ToList());
context.SaveChanges();
However, this seems to take a long time to do nothing to the database. I therefore think I'm going about it completely wrong.
I don't have the context available when I add the Project to the Employer entity and I don't want to make it available if I can avoid it, as I'm trying to keep them separate.
Is there any way to improve performance, use the bulk insert extension or sql bulk copy from where I'm calling SaveChanges on my context?
Any help is greatly appreciated.
A suggestion would be to not attach the ProjectRecords to the context in the first place when loading them from if that's possible. You could use something like a
Dictionary<Project, List<ProjectRecord>>
to hold them and to help in retrieving the Project and setting ProjectRecord.Project when you save.

CTP5 EF Code First Question

You can find the source code demonstrating this issue # http://code.google.com/p/contactsctp5/
I have three model objects. Contact,ContactInfo,ContactInfoType. Where a contact has many contactinfo's and each contactinfo is a contactinfotype. Fairly simple I guess. The problem I'm running into is when I go to edit the contact object. I pulled it from my contact repository. Then I run "UpdateModel(contact);" and it updates the object with all the values from my form. (monitoring with debug) When I save the changes though, I get the following error:
The operation failed: The relationship
could not be changed because one or
more of the foreign-key properties is
non-nullable. When a change is made to
a relationship, the related
foreign-key property is set to a null
value. If the foreign-key does not
support null values, a new
relationship must be defined, the
foreign-key property must be assigned
another non-null value, or the
unrelated object must be deleted.
It seems like after I call update model it nulls out my references and this seems to break everything? Any thoughts on how to remedy would be greatly appreciated. Thanks.
Here are my models:
public partial class Contact {
public Contact() {
this.ContactInformation = new HashSet<ContactInformation>();
}
public int ContactId { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public virtual ICollection<ContactInformation> ContactInformation { get; set; }
}
public partial class ContactInformation {
public int ContactInformationId { get; set; }
public int ContactId { get; set; }
public int ContactInfoTypeId { get; set; }
public string Information { get; set; }
public virtual Contact Contact { get; set; }
public virtual ContactInfoType ContactInfoType { get; set; }
}
public partial class ContactInfoType {
public ContactInfoType() {
this.ContactInformation = new HashSet<ContactInformation>();
}
public int ContactInfoTypeId { get; set; }
public string Type { get; set; }
public virtual ICollection<ContactInformation> ContactInformation { get; set; }
}
My Controller Action:
[AcceptVerbs(HttpVerbs.Post)]
public ActionResult Edit(Contact person) {
if (this.ModelState.IsValid) {
var contact = this.contactRepository.GetById(person.ContactId);
UpdateModel(contact);
this.contactRepository.Save();
TempData["message"] = "Contact Saved.";
return PartialView("Details", contact);
} else {
return PartialView(person);
}
}
Context Code:
protected override void OnModelCreating(System.Data.Entity.ModelConfiguration.ModelBuilder modelBuilder) {
modelBuilder.Entity<Contact>()
.HasMany(c => c.ContactInformation)
.WithRequired()
.HasForeignKey(c => c.ContactId);
modelBuilder.Entity<ContactInfoType>()
.HasMany(c => c.ContactInformation)
.WithRequired()
.HasForeignKey(c => c.ContactInfoTypeId);
}
There's a few things going on here.
1 If you are set up for lazy loading child objects are only loaded if you tell them to load. This can be done with the following in your query.
..
context.Contacts.Include(c => c.ContactInfos).Include(c => c.ContactInfos.ContactInfoType)
see this article for full details on making sure objects are loaded as you want.
2 If you don't want to save contactinfo and contactinfotype (because they are not loaded or you just don't want to), you will need to tell the context not to save child objects that shouldn't be updated. This can be done using:
..
context.StateManager.ChangeObjectState(entity.ContactInfos.ContactInfoType, EntityState.Unchanged);
I find I need to do that when changing/using a country object to user data. I definitely never want that to be updated by a user.
In the middle of writing a bit of a guide to all this, but could be weeks until it's done on my blog
3 MVC won't store/send back anything you don't put into the form. If you send an object heirarchy to the form and the values aren't represented in hidden inputs, they will come back empty on your model. For this reason, I generally make viewmodels that are editable only versions of the entities with a ToEntity and a ToModel method on them. This also covers me for security as I don't want all sorts of user ids in hidden inputs, just so my entities map straight into MVC (see this article on overposting).
I WOULD have thought that the fact you have your contactinfo properties set to virtual, the UpdateModel wouldn't mind if they didn't exist on the return, but I could well be wrong as I haven't tried it.
I figured this question out thanks to Morteza Manavi on the entity framework website. My issue was caused by my ContactInformation model properties, 'contactid' & 'contacttypeid' not being nullable. Once I fixed this everything with UpdateModel() worked correctly. Thank you very much!

Categories