I have a product that I am trying to associate categories to. The list of categories is static. I have set up a bi-directional many-to-many relationship up between Product and Category using Set<?> properties like so:
class Product {
#ManyToMany
public Set<Category> categories;
}
class Category {
#ManyToMany(mappedBy = "categories")
public Set<Product> products;
}
I would like certain users to maintain this relationship, but the only previous way I have seen is to just use a List<Long> to pass back to the controller and add appropriately. This works fine until the user needs to edit these mappings. I have tried clearing the relationship, but that doesn't prove to be simple either.
Is there a decent way to maintain this relationship? If my only option is to "loop and delete" the references, can someone point me in the right direction how to do so appropriately? So far my failed attempts look like this:
for(Category category : product.categories) {
category.products.remove(product);
}
and
Category.delete("categories.id = ?", product.id)
Maintaining the relationship: Yes, passing the IDs to the controller and fetching the entities there is okay.
The relationship proper, there are some things to note:
First, you need to set the cascade annotation, without it nothing in the assocation will get deleted:
#ManyToMany(cascade=CascadeType.ALL)
public Set<Category> categories;
Second, one Entity is the owner of the relation. In your case it's correctly set as the Product class (as the Categoryclass uses mappedBy). Updates only reflect when done on the owner, so to remove all categories from a product you would do
products.categories = new Set<Product>();
if you want to remove a single categorie, just remove it from the products.categories.
Related
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.
In Grails if there is a many to many relation ships for example book has many authors
Lets says book1 has author1,autho2,author3,author4 values .Now a PUT request is sent saying that book1 has only author1,author3 then the other two values are deleted from the table.
Now for the same scenario with one to many relation ship lets say book1 has author1,autho2,author3,author4 values now if a PUT request is done with only author1,author3
Is it suppose to delete the other two i.e, author2 and author4 values?? i expect the behavior to be so..
Below is my model for book and author
class Author {
String name;
static hasMany = [books: Book]
static mapping = {
books cascade: 'all-delete-orphan'
}
}
class Book{
String name
static belongsTo = [author: Author]
}
EDIT:
When i emplement the all-delete-orphan i get the followinh error
A collection with cascade="all-delete-orphan" was no longer referenced by the owning entity instance
It depends on the cascade options set. If you are using belongsTo on the Author table grails uses cascade all by default. This means that the Author objects are removed if they are not used by any other Book. You can customize this behaviour using the cascade option (all-delete-orphan should be the value you are interested in).
I have a legacy database with a many-to-many relationship like the following:
public class Post
{
public ICollection<Tag> Tags { get; set; }
...
}
public class Tag
{
public ICollection<Post> Posts { get; set; }
...
}
with the many-to-many relationship tracked in a 'PostTagLink' table.
Normally it is easy to use Code First to express the many-to-many relationship more or less implicitly, i.e. update the 'PostTagLink' table when a relationship is added or removed, but without actually having a 'PostTagLink' entity explicitly defined.
Audit fields on Tags and Posts can be updated easily by the DbContext when changes are saved:
public abstract class MyAuditableEntityContext : DbContext
{
public override int SaveChanges()
{
string currentUser = Thread.CurrentPrincipal.Identity.Name;
foreach (DbEntityEntry<IAuditableEntity> changeEntry in base.ChangeTracker.Entries<IAuditableEntity>())
{
if (changeEntry.State == EntityState.Added)
{
changeEntry.Entity.CreatedBy = currentUser;
changeEntry.Entity.RevisedBy = currentUser;
}
else if (changeEntry.State == EntityState.Modified)
{
changeEntry.Entity.RevisedBy = currentUser;
}
}
return base.SaveChanges();
}
}
But what if the 'PostTagLink' table also includes audit fields?
The only solution I can see it to include a PostTagLink entity in the model (with many-to-one relationships back to Tag and Post) so I can access the audit fields in the DbContext SaveChanges method.
But adding these extra entities makes working with the model awkward. Clients and queries have to work the the 'link' entities directly instead of Entity Framework handling the relationship changes automatically.
The question: Is there some Entity-Framework-ninja technique where I could intercept the changes to many-to-many relationships and update the link table audit fields as necessary, without having to explicitly include 'link' entities in the model?
(Again - this is a legacy database and there is little I can do to change it, so I'd like to avoid adding stored procedures or any other logic to the database.)
Thanks for your time!
So if I understand this correctly, when you add an item to one of the collections and save the entity, you need to set a CreatedBy field in the many-to-many table. You can execute raw sql using DbContext.Database.SqlCommand to update the link table audit fields. How to execute raw sql.
So how to intercept the changes?
These answers might help: EF4 Audit changes of many to many relationships and Entity Framework: Tracking changes to FK associations
EDIT:
For reference here is the original example I posted that may have led you to believe that you had to put sql into your model
Patient: "Doctor, it hurts when I do this. What's the cure?"
Doctor: "Don't do that."
I'm going to answer my own question as 'there is no answer'. As I've seen others advise in comments on related StackOverflow questions - I think it will ultimately be better to just include the 'link' entities explicitly in the model.
I have a common Repository with Add, Update, Delete.
We'll name it CustomerRepository.
I have a entity (POCO) named Customer, which is an aggregate root, with Addresses.
public class Customer
{
public Address Addresses { get; set; }
}
I am in a detached entity framework 5 scenario.
Now, let's say that after getting the customer, I choose to delete a client address.
I submit the Customer aggregate root to the repository, by the Update method.
How can I save the modifications made on the addresses ?
If the address id is 0, I can suppose that the address is new.
For the rest of the address, I can chose to attach all the addresses, and mark it as updated no matter what.
For deleted addresses I can see no workaround...
We could say this solution is incomplete and inefficient.
So how the updates of aggregate root childs should be done ?
Do I have to complete the CustomerRepository with methods like AddAddress, UpdateAddress, DeleteAddress ?
It seems like it would kind of break the pattern though...
Do I put a Persistence state on each POCO:
public enum PersistanceState
{
Unchanged,
New,
Updated,
Deleted
}
And then have only one method in my CustomerRepository, Save ?
In this case it seems that I am reinventing the Entity "Non-POCO" objects, and adding data access related attribute to a business object...
First, you should keep your repository with Add, Update, and Delete methods, although I personally prefer Add, indexer set, and Remove so that the repository looks like an in memory collection to the application code.
Secondly, the repository should be responsible for tracking persistence states. I don't even clutter up my domain objects with
object ID { get; }
like some people do. Instead, my repositories look like this:
public class ConcreteRepository : List<AggregateRootDataModel>, IAggregateRootRepository
The AggregateRootDataModel class is what I use to track the IDs of my in-memory objects as well as track any persistence information. In your case, I would put a property of
List<AddressDataModel> Addresses { get; }
on my CustomerDataModel class which would also hold the Customer domain object as well as the database ID for the customer. Then, when a customer is updated, I would have code like:
public class ConcreteRepository : List<AggregateRootDataModel>, IAggregateRootRepository
{
public Customer this[int index]
{
set
{
//Lookup the data model
AggregateRootDataModel model = (from AggregateRootDataModel dm in this
where dm.Customer == value
select dm).SingleOrDefault();
//Inside the setter for this property, run your comparison
//and mark addresses as needing to be added, updated, or deleted.
model.Customer = value;
SaveModel(model); //Run your EF code to save the model back to the database.
}
}
}
The main caveat with this approach is that your Domain Model must be a reference type and you shouldn't be overriding GetHashCode(). The main reason for this is that when you perform the lookup for the matching data model, the hash code can't be dependent upon the values of any changeable properties because it needs to remain the same even if the application code has modified the values of properties on the instance of the domain model. Using this approach, the application code becomes:
IAggregateRootRepository rep = new ConcreteRepository([arguments that load the repository from the db]);
Customer customer = rep[0]; //or however you choose to select your Customer.
customer.Addresses = newAddresses; //change the addresses
rep[0] = customer;
The easy way is using Self Tracking entities What is the purpose of self tracking entities? (I don't like it, because tracking is different responsability).
The hard way, you take the original collection and you compare :-/
Update relationships when saving changes of EF4 POCO objects
Other way may be, event tracking ?
I am developing a web application using JSF and JPA(Eclipse link). I have two entities with bidirectional OneToMany relationship. The owner entity is contact and target entity is customer. Single customer can have multiple contacts, like email, phone, etc. When the end user is adding a new customer, he also adds the contacts straight away. There is a need to cancel the saving of a new customer, even after adding contacts to that customer. I tried to add that functionality, but failed in the following way.
Can that senario be achieved directly by persistence?
Contact Entity
....
public class Contact implements Serializable {
....
#ManyToOne
Customer customer;
....
Customer Entity
....
public class Customer implements Serializable {
....
#OneToMany(mappedBy = "customer")
private List<Contact> contacts;
....
Adding a new contact to Customer (current is an object of Customer class)
Contact contact = new Contact();
contact.setCustomer(current);
....
current.getInstitutionContacts().add(contact);
This works when the current is already a persisted one. If I tried to add a contact to yet to persist one, there is a java.lang.NullPointerException.
I can work around to achieve the functionality, but is there any way we can just collect the contacts to the array and persist them only when (and if only) the customer is persisted? By using cascade persist or lazy fetch, etc?
Sounds like you want Contacts to be Components, not Entities.
The difference is that an entity has it's own lifecycle; it lives outside the scope of its association, and deleting the parent does NOT necessarily have to delete the child. Also, if a child is an Entity, other classes can also have relationships with that child.
Components are completely bound to the parent. They automatically go away if the parent goes away. They cannot be referenced by other associations or by other Entities. It's like they are simple properties of the parent class.
The only caveat is that I don't know if all JPA implementations support having a collection of components.
See this documentation. Particularly the part that says: "You can also use association annotations in an embeddable object (ie #OneToOne, #ManyToOne, #OneToMany or #ManyToMany). To override the association columns you can use #AssociationOverride."
If the JPA implementation you are using does, you can use the #Embeddable annotation and #OneToMany
Edit: -- I also found info here http://en.wikibooks.org/wiki/Java_Persistence/Embeddables#Collections.