I'm banging my head on the keyboard with this one, basically what I'm trying to do is rather simple.
I reversed engineered an existing database with the EF Power Tools, I did that mostly because the database in question is quite old and missing a lot of foreign keys, or even primary key for that matter. The goal was to be able to add navigation properties to the generated model, mostly to have a cleaner code when querying the model.
Now I have 2 classes User and CostCentre, mapped to their respective tables Users and CostCentres with proper primary keys, PK of Users is Username named Userid in CostCentres, theoretical relation between those two is 1 to M (one User can have multiple CostCentre).
I added a navigation property to User like so
public virtual ICollection<CostCentre> CostCentres { get; set; }
I'm initializing the list in the default constructor with a simple
public User()
{
CostCentres = new List<CostCentre>();
}
To CostCentre I added a User property like so
public virtual User User { get; set; }
In the generated CostCentreMap class, I mapped the navigation property like so
this.HasRequired(cc => cc.User)
.WithMany(u => u.CostCentres)
.HasForeignKey(cc => cc.Userid);
Now whenever I query db.Users I can't get the property to fill, I know the navigation property kind of work, because when I do
db.Users
.Include(u => u.CostCentres);
The resulting query does the join properly on the proper columns in each table, however if I do
User user = db.Users
.Include("CostCentres")
.SingleOrDefault(u => u.Username == userName);
The property CostCentres stays empty.
I'm pretty sure I'm missing something quite obvious but for the love of Barnaby Jones, I can't get my hands on what.
Any help would be greatly appreciated.
Related
I'm facing a problem with EF Core and collections; I have persons who read books, the books can be read by multiple people and people can read multiple books (it's a many-to-many relationship). My EF generates the 3 tables Books, Persons and BookPersons.
When I insert new persons with a set of books they read, there is no problem. Still when I recreate one of the persons outside the db context (so same id, but mutated collection of read books) and I try to save it, it fails on the many-to-many relation. Because the relation between the existing already exists (not unique constraint)
I've tried:
to attach the book collection to the context (same error)
the person (no error but no change either)
only change person details not the collection (the untracked entity is saved but my books read is not saved)
I'm not very fond of managing the BookPersons table or doing queries first to get existing entities. My goal is to do an update of a person and its read books in one go. I do know how to write it in SQL but it seems EF is quite a challenge.
If you want to view my code, visit: https://github.com/CasperCBroeren/EfCollectionsProblem/blob/master/Program.cs
Thanks for explaining what I'm missing or not getting
I would create PersonBooks model to handle that,
Book model
public class Book
{
[DatabaseGenerated(DatabaseGeneratedOption.None)]
public int Id { get; set; }
public string Title { get; set; }
[ForeignKey("BookId")]
public virtual ICollection<PersonBook> PersonBooks { get; set; }
}
Person Model
public class Person
{
[DatabaseGenerated(DatabaseGeneratedOption.None)]
public int Id { get; set; }
public string Name { get; set; }
[ForeignKey("PersonId")]
public virtual ICollection<PersonBook> PersonBooks { get; set; }
}
PersonBook Model
public class PersonBook
{
public int Id { get; set; }
public int PersonId { get; set; }
public int BookId { get; set; }
public virtual Person Person { get; set; }
public virtual Book Book { get; set; }
}
then you can get all book Id's readen by person by using
var personId = 15; // what ever you want
db.PersonBooks.Where(a=> a.PersonId == personId);
or get all persons Id's who read a book by id
var bookId= 11; // what ever you want
db.PersonBooks.Where(a=> a.BookId== bookId);
Note:
you can reach the Book entity by using for example
db.PersonBooks.Where(a=> a.PersonId == personId).FirstOrDefault().Book;
A key factor in EF is dealing with object references. Any reference that a DbContext isn't tracking will be treated as a new entity. The Update method on DbSets should actually be avoided as it can lead to inefficient and potentially dangerous data changes.
This option: "to attach the book collection to the context" works with singular references, but doesn't work with collections. The trouble is that what you want to say is "add any book the person isn't already associated with" however, the DbContext has no knowledge of what books that person is already associated with unless you fetch that information first.
... or doing queries first to get existing entities.
This is actually what you should do in most cases. In the case of a simple console application to test out ideas and learn how EF works it may look like overkill, but in real-world systems this is the recommended approach for a number of reasons.
Keeping payloads small. Take an API or web site where you allow a user to associate books to people. Sending entire representation of people, their books, etc. back and forth between server and client can get potentially expensive in terms of data size. If I have an API that allows me to associate books to a person, if those books already reflect known data state (already exist in the db) then all I need to pass are IDs. When passing data to views the idea is to only pass what the view needs rather than entire entity graphs.
Keeping payloads safe. Passing entire entities around and using methods like Update can make your systems prone to tampering. Update will update all columns in an entity whether you expect, or allow them to change or not. By minimizing the data coming back you ensure only the expected details can change, and you by definition validate that the provided values are safe.
For example, if I have a service that wanted to update books associated to a person. In the UI I had loaded that John had "Jungle Book (ID: 1)", and I wanted to update the associations so John now had "Jungle Book" and "Tom Sawyer". While my UI might now allow it, it is certainly possible that the client browser can intercept the call to my controller / web service, and seeing a Book { ID: 1, Name: "Jungle Book" }, tamper with that data to send Book { ID: 1, Name: "Hitchhiker's Guide to the Galaxy"}. Provided you did solve this issue in a way that resulted in attaching entities and doing an Update or such, the consequence of this tampering would be that an attacker could rename a book. That would have a flow-on effect to every Person that referenced Book ID #1.
Instead if I want to have something like an "UpdateBooks" method that can reassign books for a person, I would have a method something like this:
private void UpdateBooks(int personId, params int[] bookIds)
{
using (var context = new AppDbContext())
{
var person = context.Persons
.Include(x => x.Books)
.Single(x => x.PersonId == personId);
var existingBookIds = person.Books.Select(x => x.BookId).ToList();
var bookIdsToAdd = bookIds.Except(existingBookIds).ToList();
var bookIdsToRemove = existingBookIds.Except(bookIds).ToList();
foreach(var bookId in bookIdsToRemove)
{
var book = person.Books.Single(x => x.BookId == bookId);
person.Books.Remove(book);
}
if (bookIdsToAdd.Any())
{
var booksToAdd = context.Books
.Where(x => bookIdsToAdd.Contains(x.BookId))
.ToList();
if(booksToAdd.Count != bookIdsToAdd.Count)
{
// Handle scenario where one or more book IDs provided weren't found.
}
person.Books.AddRange(booksToAdd);
}
context.SaveChanges();
}
}
This assumes that EF is handling PersonBooks entirely behind the scenes where PersonBook consists of just PersonId and BookId so-as Person can have a collecton of Books rather than PersonBooks.
This example runs up to two SELECT queries. One to get the Person and it's current books, and one to get any new books if any need to be added. There is no risk of tampering with books, and we can easily validate scenarios such as passing an unknown book ID. The temptation might be to avoid querying, seeing it as expensive, but in most cases EF can provide data quite quickly and efficiently. It is the exception rather than the norm that you might need to get creative to get around possible performance bottlenecks with data access.
A third consideration is to focus on keeping operations atomic, especially for things like web services / web applications. This doesn't apply when just getting familiar with the workings of EF, entities, and such, but a consideration for more real-world applications. Rather than having more complex methods like UpdateBooks(), using actions like "AddBook" and "RemoveBook" can keep operations faster and simpler. One argument for a larger method is that you might expect all of the operations to be committed (or not) as one operation, such as UpdateBooks gets called as part of one big "SavePerson" method reflecting changes to the person and all of it's associated details. In these cases having atomic actions is still recommended, except instead of updating data state, they can update server (session) state waiting for a "Save" call to come through to persist the changes as one operation, or discarding the changes. Add/Remove methods can still provide the validation checks ultimately setting things up for entities to be loaded, modified, and persisted.
I'm trying to use DDD with EFCore and I am struggling to find a way to map 2 POCOs from different context that represent the same entity to the same table.
I have a User class inside UserContext, with all the properties needed to create a new user to my application.
And I have either a User class inside my OrderContext, in this class I only have the Id and Email properties, cause it's all that is needed in OrderContext to work.
So I have something like this:
modelBuilder.Entity<Domain.UserContext.User>(u =>
{
u.ToTable("User").HasKey(e => e.Id);
u.OwnsOne(e => e.Name);
u.OwnsOne(b => b.HomeAddress);
});
modelBuilder.Entity<Domain.OrderContext.User>(u =>
{
u.ToTable("User").HasKey(e => e.Id);
});
modelBuilder.Entity<Domain.OrderContext.Order>(p =>
{
p.ToTable("Order").HasKey(b => b.Id);
p.HasOne(x => x.User); // this is OrderContext.User
});
I can't seem to find a way to map both User classes to the same table. Is there a way to do it?
Edit1: Both contexts are bounded context DDD's concept not DbContext.
I just need both classes to be maped as the same table. The Add-Migration command return a message telling me that it cannot map 'OrderContext.User' to table 'User' since it is already mapped to 'UserContext.User'.
The main cause of issue is, EF Core cannot figure out how to use same table for 2 different entities. There is lack of data in mapping, once you fill that in, it works as expected.
First you will need to define how are they related to each other. Sharing same table with same PK does not have Foreign Key defined on server side but there is still intrinsic relationship between both entities which is one-to-one and using PK as FK. Once you define relationship, you will see that it works and both entities are mapped to same table. (Just like how owned entities are mapped to same table). This may not be end of mapping for your case though. Since from EF perspective they are still 2 different entities, except for Id (or PK property), they will have own columns to store data. But what if you have columns which are common in both the context (like Email in your scenario). In such case, you would need to provide mapping for those additional column too. If you map them to same column explicitly, they will start sharing the column in database. Overall the code would look like this.
namespace UserContext
{
public class User
{
public int Id { get; set; }
public string Email { get; set; }
// Other properties
}
}
namespace OrderContext
{
public class User
{
public int Id { get; set; }
public string Email { get; set; }
}
}
// In OnModelCreating method
modelBuilder.Entity<UserContext.User>(u =>
{
u.ToTable("User");
u.Property(e => e.Email).HasColumnName("Email");
// Configuration for other properties
});
modelBuilder.Entity<OrderContext.User>(u =>
{
u.ToTable("User");
u.Property(e => e.Email).HasColumnName("Email");
u.HasOne<UserContext.User>().WithOne().HasForeignKey<OrderContext.User>(e => e.Id);
});
Above code creates single table with shared columns and should work as expected. You can add more entities in the same table if you want by following same configuration. Here, I used User from UserContext as principal side but you can use any side. The main reasoning for me was, UserContext.User will be the entity which will be added when adding new User. Entities sharing the table do not have to be subset either. But there will be columns for additional properties which are not shared.
#Smit provided solution should work, but it is not ideal for isolated bounded contexts, where each of them is not aware of one another and each is taking care of it's own configurations.
I have solved this problem by adding separate DbContexts for each bounded context. Each of these contexts inherit base DbContext, where I have my shared logic (like auditing, etc.), and each DbContext inside bounded contexts has it's own DbSets and Fluent Api configurations. This way I have entities which point to the same table, but from different DbContexts.
I am looking at this problem myself. I noticed, that if you specify schema name for one of the tables then EF will not complain.
For example in your case:
modelBuilder.Entity<Domain.UserContext.User>(u =>
{
u.ToTable("User", "dbo").HasKey(e => e.Id);
u.OwnsOne(e => e.Name);
u.OwnsOne(b => b.HomeAddress);
});
modelBuilder.Entity<Domain.OrderContext.User>(u =>
{
u.ToTable("User").HasKey(e => e.Id);
});
Of course this is not a full solution and even not a workaround, since you can not have more than 2 mentions of "User" table (that is, in more than 2 contexts).
Also i found https://data.uservoice.com/forums/72025-entity-framework-core-feature-suggestions/suggestions/1872001-map-multiple-entities-to-same-table which makes me think that this in general is not possible.
Regarding DDD in general
Most sources say that your bounded contexts should be isolated not only by code, but also by data. This in theory means, that your User table should be duplicated in each bounded context. This is ideal way, but is unnecessarily complex (imho) for more simple scenarios, since it involves data synchronization across all duplicated tables.
I am not sure I am approaching wrong way or it is a default behaviour but it is not working the way I am expecting ...
Here are two sample classes ...
public class Person
{
public string FirstName { get; set; }
public string LastName { get; set; }
public Department Department { get; set; }
}
Second one is Department
public class Department
{
public string Name { get; set; }
public List<Person> People { get; set; }
}
Context Configuration
public MyDbContext() : base("DefaultConnection")
{
this.Configuration.ProxyCreationEnabled = false;
this.Configuration.LazyLoadingEnabled = false;
}
public DbSet<Person> People { get; set; }
public DbSet<Department> Departments { get; set; }
I am try to load people where last name is from 'Smith'
var foundPeople
= context
.people
.Where(p => p.LastName == "Smith");
Above query load foundPeople with just FirstName and LastName no Department object. It is a correct behaviour as my LazyLoading is off. And that was expected as well.
Now in another query with Eager loading Department,
var foundPeople
= context
.people
.Where(p => p.LastName == "Smith")
.Include(p => p.Department);
Above query loads foundPeople with FirstName, LastName, Department with Department->Name as well as Deparment->People (all people in that department, which I dont want, I just want to load first level of the Included property.
I dont know is this an intended behaviour or I have made some mistake.
Is there any way to just load first level of Included property rather then complete graph or all levels of included property.
Using Include() to achieve eager loading only works if lazy loading is enabled on your objects--that is, your navigation properties must be declared as virtual, so that the EF proxies can override them with the lazy-loading behavior. Otherwise, they will eagerly load automatically and the Include() will have no effect.
Once you declare Person.Department and Department.People as virtual properties, your code should work as expected.
Very sorry, my original answer was wholly incorrect in the main. I didn't read your question closely enough and was incorrect in fact on the eager behavior. Not sure what I was thinking (or who upvoted?). Real answer below the fold:
Using the example model you posted (with necessary modifications: keys for the entities and removed "this" from context constructor) I was unable to exactly reproduce your issue. But I don't think it's doing what you think it's doing.
When you eagerly load the Department (or explicitly load, using context.Entry(...).Reference(...).Load()) inspect your results more closely: there are elements in the Department.People collections, but not all the Persons, only the Persons that were loaded in the query itself. I think you'll find, on your last snippet, that !foundPeople.SelectMany(p => p.Department.People).Any(p => p.LastName != "Smith") == true. That is, none of them are not "Smith".
I don't think there's any way around this. Entity Framework isn't explicitly or eagerly loading People collections (you could Include(p => p.Department.People) for that). It's just linking the ones that were loaded to their related object, because of the circular relationship in the model. Further, if there are multiple queries on the same context that load other Persons, they will also be linked into the object graph.
(An aside: in this simplified case, the proxy-creation and lazy-loading configurations are superfluous--neither are enabled on the entities by virtue of the fact that neither have lazy or proxy-able (virtual) properties--the one thing I did get right the first time around.)
By desing, DbContext does what it's called "relationship fix-up". As your model has information on which are the relations between your entities, whenever an entity is attached, or modified, in the context, EF will try to "fix-up" the relations between entities.
For example, if you load in the context an entity with a FK that indicates that it's a children of another entity already attached to the context, it will be added to the children collection of the existing entity. If you make any chages (change FK, delete entity, etc.) the relationships will be automatically fixed up. That's what the other answer explains: even if you load the related entities separatedly, with a different query, they'll be attached to the children collection they belong to.
This functionality cannot be disabled. See other questions related to this:
AsNoTracking and Relationship Fix-Up
Is it possible to enable relationship fixup when change tracking is disabled but proxies are generated
How to get rid of the related entities
I don't know what you need to do, but with the current version of EF you have to detach the entity from the context and manually remove the related entities.
Another option is to map using AutoMapper or ValueInjecter, to get rid of the relationship fix-up.
You could try using a LINQ query so you can select only the fields that you need. I hope that helps.
This question keeps popping up, but I couldn't find anyone doing exactly what I am, so here goes:
I've got a table that can have a relationship to one of many tables. Some have many, and some have one. In all cases it would be optional.
So we have a table of Pictures.
We have Products, Vendors, Users.
Products have a one to many relationship with Pictures (they can upload multiple photos of each product).
Users have a one to one (optional on both sides) with pictures. (If the user uploads a photo, great, if not, no problem)
Vendors have a one to one, similar to Users, except it is relating to a different table.
This works fine with the many relationships, but the one to one doesn't work properly.
I want to have a virtual on both sides but define the nullable ForeignKey only in the Pictures table.
public virtual Picture UserPhoto { get; set; }
And then on the other side:
public virtual User User{ get; set; }
[ForeignKey("User")]
public int? UserID { get; set; }
I like to have the keys available in the code, not just in the database, so I don't want to configure the relationship solely in fluent. But If I add a fluent configuration on top, it will add extra foreign keys.
Without a fluent configuration, we get the dreaded
Multiplicity is not valid in Role 'Picture_User_Source' in relationship 'Picture_User'. Because the Dependent Role properties are not the key properties, the upper bound of the multiplicity of the Dependent Role must be '*'.
Starting out on an Entity Framework project.
Imported the Db I am going to use and right away noticed that many table fields were made into EntityKey types and the source fields are not even Keys. Doesn't seem to be a pattern as to which fields were made EntityKeys and which were not.
Is this normal? There were no options for this in the wizard. I don;t want to have to go through and remove this property for all the fields where it was added.
Thanks for your advice!
Each entity on your model requires a unique key, so EF can track and retrieve/persist these entities based on their unique identifier.
If your tables in your database don't have primary keys, then your database is not relational and therefore should not be used by an ORM like EF which is predominantly designed for RDBMS.
If you had an entity like this:
public class Orders
{
public string Name { get; set; }
public double Price { get; set; }
}
How would you retrieve a single order? How would you save a single order?
Crucial LINQ methods such as SingleOrDefault() would be useless, as there is no guarantee that this won't throw an exception:
var singleOrder = ctx.Orders.SingleOrDefault(x => x.Name == "Foo");
Whilst if you had an EntityKey and PK called "OrderId", this is guaranteed to not throw an exception:
var singleOrder = ctx.Orders.SingleOrDefault(x => x.OrderId == 1);
http://msdn.microsoft.com/en-us/library/dd283139.aspx
I think as soon as you read the first paragraph you will understand the role of entity keys in Entity Framework.