EF4 - Adding a blog entry complains about Profile? - entity-framework

I must admin this is kind of funny even though I think I understand why :) I created a UnitTest to add a blog entry to try Alex suggestion about my inheritance problems. Now I come across another one.
[TestMethod]
public void UserCanAddBlogEntry()
{
var context = new EntityContext(Options.LazyLoading);
var user = (from u in context.Users
.Include("Blog.BlogEntries")
where u.Id == 1
select u).FirstOrDefault();
BlogEntry entry = new BlogEntry();
entry.Header = "Test Entry";
entry.Text = "Test Text blah blah blah";
entry.CreatedAt = DateTime.Now;
entry.Blog = user.Blog;
user.Blog.BlogEntries.Add(entry);
context.SaveChanges();
Assert.IsTrue(user.Blog.BlogEntries.Count > 0);
}
Causes the exception:
Failed UserCanAddBlogEntry Zirzle.UnitTests
Test method UserCanAddBlogEntry threw
exception:
System.InvalidOperationException:
Invalid relationship fixup detected in
the navigation property 'User' of the
entity of the type 'Profile'.
Not sure what is wrong with this picture. If I add .Include("Profile") in the get query then save changes doesnt complain any more. I tried adding a 0.1 relation end for profile but that didn't work out either. Any suggestions? I suppose stack overflows personal EF expert might have an explanation :)

Well this one is interesting.
I'm assuming that Blog.BlogEntries is the inverse of BlogEntry.Blog
I'm also assuming that all the properties on all classes are virtual, so the EF can proxy the classes.
Given these assumptions some objects will be proxied (user and user.Blog) because the ObjectContext constructed them and some won't be proxied (entry) because you created them yourself.
Proxied classes automatically do fix-up, i.e. keep both ends of a relationship in sync
So doing this on a proxied user.Blog:
user.Blog.BlogEntries.Add(entry)
will automatically set entry.Blog to user.Blog at the same time to keep both relationships in sync for you.
On the other hand because entry isn't proxied this:
entry.Blog = user.Blog
won't do fixup.
So in this code you are essentially doing one half of the fix-up twice. We should probably gracefully handle this situation, but obviously we aren't, I will talk this through with the team.
Having said all that I suggest you do something a little simplier:
// notice there is no need to get the User just the users blog
var blog = (from u in context.Users
where u.Id == 1
select u.Blog).Single();
and just do this:
BlogEntry entry = new BlogEntry();
entry.Header = "Test Entry";
entry.Text = "Test Text blah blah blah";
entry.CreatedAt = DateTime.Now;
entry.Blog = blog;
//because BlogEntry inherits from Post if I remember your model
context.Posts.Add(entry);
context.SaveChanges();
This should work.
Alex

Related

OneToMany relationsip of entity in persistence context is not updated

There is 3 entities in MxN relationship, B being association entity. We create them in single TX, persist all of them, and fetch entity with OneToMany association. This association is not initialized after fetch.
Source: https://github.com/alfonz19/springboot222demo/commits/what
#Transactional
#Test
void contextLoads() {
// for(int i = 0; i < 3; i++) {
UUID aId = UUID.randomUUID();
AEntity aEntity = aRepository.save(new AEntity().setId(aId));
UUID bId = UUID.randomUUID();
CEntity cEntity = cRepository.save(new CEntity().setId(bId));
em.flush();
bRepository.save(new BEntity().setAEntity(aEntity).setCEntity(cEntity));
// }
em.flush();
// em.clear();
Iterable<CEntity> centities = cRepository.findAll();
List<BEntity> bEntities =
iterableToStream(centities).flatMap(e -> e.getBEntities().stream()).collect(Collectors.toList());
Assert.assertThat(centities, Matchers.iterableWithSize(1));
Assert.assertThat(bRepository.findAll(), Matchers.iterableWithSize(1));
Assert.assertThat(bEntities.size(), CoreMatchers.is(1));
...
}
Ok, I understand, that when creating BEntity I do not update AEntity and CEntity leaving them corrupted. Calling cRepository.findAll() then does call select on db to get all Cs (even without any evict/flush/clear) but leaves #OneToMany uninitialized. I don't get it. I would understand, if there woulndn't be no call to db at all, but if I fetch Cs anyway to refresh it, why not refresh also the association table. Why's that?
Even more suprisingly aRepository.save(new AEntity().setId(aId)) when doing em.merge (entity has assigned id) the hibernate does load whole MxN structure using 2 left outer joins, even if #OneToMany is lazy. Why's that?? EDIT: ok, that's not surprising at all, that's implication of cascade merge. Compeletely ok.
I'm little bit surprised by this behavior, as there are select issued where they shouldn't be (IIUC), and there aren't ones, where they easily could be.
And to keep the best to the end. With small change: uncommenting for loop and clear, I'm getting full nondeterministic behavior.
source: https://github.com/alfonz19/springboot222demo/tree/nondeterministic
Tests will either work, or produces exception like:
array out of bounds
collection with cascade="all-delete-orphan" was no longer referenced by the owning entity instance:
java.lang.NullPointerException
but if I put breakpoint on bEntities variable declaration, cEntities are always correctly created and test then pass. I have no idea what can cause this.
I have answer to non-deterministic behavior problem bonus-question.
One more randomly generated exceptions to the list is org.springframework.orm.jpa.JpaSystemException: Found shared references to a collection and all this behavior just disappers with removal of flatMap. Ie replace:
List<BEntity> bEntities =
StreamSupport.stream(centities.spliterator(), true).flatMap(e -> e.getBEntities().stream()).collect(Collectors.toList());
with
List<BEntity> bEntities = new LinkedList<>();
centities.forEach(e->bEntities.addAll(e.getBEntities()));
and test in (not anymore) "nondeterministic" branch will pass 100%. Not sure why, however it seems, that stream-api is not that safe with hibernate-managed collections.

Having a hard time with Entity Framework detached POCO objects

I want to use EF DbContext/POCO entities in a detached manner, i.e. retrieve a hierarchy of entities from my business tier, make some changes, then send the entire hierarchy back to the business tier to persist back to the database. Each BLL call uses a different instance of the DbContext. To test this I wrote some code to simulate such an environment.
First I retrieve a Customer plus related Orders and OrderLines:-
Customer customer;
using (var context = new TestContext())
{
customer = context.Customers.Include("Orders.OrderLines").SingleOrDefault(o => o.Id == 1);
}
Next I add a new Order with two OrderLines:-
var newOrder = new Order { OrderDate = DateTime.Now, OrderDescription = "Test" };
newOrder.OrderLines.Add(new OrderLine { ProductName = "foo", Order = newOrder, OrderId = newOrder.Id });
newOrder.OrderLines.Add(new OrderLine { ProductName = "bar", Order = newOrder, OrderId = newOrder.Id });
customer.Orders.Add(newOrder);
newOrder.Customer = customer;
newOrder.CustomerId = customer.Id;
Finally I persist the changes (using a new context):-
using (var context = new TestContext())
{
context.Customers.Attach(customer);
context.SaveChanges();
}
I realise this last part is incomplete, as no doubt I'll need to change the state of the new entities before calling SaveChanges(). Do I Add or Attach the customer? Which entities states will I have to change?
Before I can get to this stage, running the above code throws an Exception:
An object with the same key already exists in the ObjectStateManager.
It seems to stem from not explicitly setting the ID of the two OrderLine entities, so both default to 0. I thought it was fine to do this as EF would handle things automatically. Am I doing something wrong?
Also, working in this "detached" manner, there seems to be an lot of work required to set up the relationships - I have to add the new order entity to the customer.Orders collection, set the new order's Customer property, and its CustomerId property. Is this the correct approach or is there a simpler way?
Would I be better off looking at self-tracking entities? I'd read somewhere that they are being deprecated, or at least being discouraged in favour of POCOs.
You basically have 2 options:
A) Optimistic.
You can proceed pretty close to the way you're proceeding now, and just attach everything as Modified and hope. The code you're looking for instead of .Attach() is:
context.Entry(customer).State = EntityState.Modified;
Definitely not intuitive. This weird looking call attaches the detached (or newly constructed by you) object, as Modified. Source: http://blogs.msdn.com/b/adonet/archive/2011/01/29/using-dbcontext-in-ef-feature-ctp5-part-4-add-attach-and-entity-states.aspx
If you're unsure whether an object has been added or modified you can use the last segment's example:
context.Entry(customer).State = customer.Id == 0 ?
EntityState.Added :
EntityState.Modified;
You need to take these actions on all of the objects being added/modified, so if this object is complex and has other objects that need to be updated in the DB via FK relationships, you need to set their EntityState as well.
Depending on your scenario you can make these kinds of don't-care writes cheaper by using a different Context variation:
public class MyDb : DbContext
{
. . .
public static MyDb CheapWrites()
{
var db = new MyDb();
db.Configuration.AutoDetectChangesEnabled = false;
db.Configuration.ValidateOnSaveEnabled = false;
return db;
}
}
using(var db = MyDb.CheapWrites())
{
db.Entry(customer).State = customer.Id == 0 ?
EntityState.Added :
EntityState.Modified;
db.SaveChanges();
}
You're basically just disabling some extra calls EF makes on your behalf that you're ignoring the results of anyway.
B) Pessimistic. You can actually query the DB to verify the data hasn't changed/been added since you last picked it up, then update it if it's safe.
var existing = db.Customers.Find(customer.Id);
// Some logic here to decide whether updating is a good idea, like
// verifying selected values haven't changed, then
db.Entry(existing).CurrentValues.SetValues(customer);

Loading particular peoperties for entity and assigning it as reference to another entity

Example:
I am entering a new invoice. For this invoice I need to enter a customer. Lets assume that we retrieved a list of customers:
var list = Context.Set<Customer>().ToList();
Here I see two issues:
1) I do not need to bring all information for customer, I only need Id, Code and Name
2) Customer in current DbContext is read-only, so it would be nice if it is possible to tell DbContext not to monitor their states, to improve performance.
Questions:
1) Can we load only partial data for customer, but still be able to assign it to Invoice (see code bellow)?
2) Can we tell DbContext not to monitor Customers for changes, and still be able to do this:
Invoice.Customer = CustomerList[10];
There's not a direct way to do exactly what you want, but you might be able to achieve your goals with some compromise.
I do not need to bring all information for customer, I only need Id,
Code and Name
There isn't a way for EF to create a partially loaded entity, but you could create an anonymous type:
Context.Customers.Select(c => new {Id = c.CustomerId, Code = c.Code, Name = c.Name}).Tolist()
If you could live with the new anonymous type then use that, or you could then iterate through that list, creating actual customer objects.
Customer in current DbContext is read-only, so it would be nice if it
is possible to tell DbContext not to monitor their states, to improve
performance.
EF provides an Extension of AsNoTracking() which will do exactly what you're looking for:
var list = Context.Set<Customer>().AsNoTracking().ToList();
Depending on what you choose from above, the following code may change, but this code does achieve what you're looking for. Partially loads the customer, but still allows you to attach the customer to the invoice.
Note: You'll need to attach the customer to your context before you can use it, and then setting it to a state of Unchanged will prevent it from overwriting exiting data.
m = new Model();
var list = m.Customers.Select(c => new {Id = c.CustomerId, Code = c.Code, Name = c.Name});
List<Customer> customerList = new List<Customer>();
foreach (var item in list)
{
customerList.Add(new Customer()
{
CustomerId = item.Id,
Code = item.Code,
Name = item.Name
});
}
Invoice i = new Invoice();
var customer = customerList.First();
m.Customers.Attach(customer);
m.Entry(customer).State = EntityState.Unchanged;
i.Customer = customer;
m.Invoices.Add(i);
m.SaveChanges();

Entity framework code first, some advice needed

I'm new to entity frame work code first.
I have simple class called Cat and a list of cats, when i'm doing the following :
mAllAnimals.Add(new Cat() { Father = null , Name = "Father Kitten", NickName = "Shmil" });
mAllAnimals.Add(new Cat() { Father = mAllAnimals.First(a => a.Name == "Father Kitten") , Name = "Son Kitten" , NickName = "son" });
i get an exception,because he couldn't find "Father kitten", but when i put between the to statements "SaveChanges()" it works perfectly.
This is very strange for me, do i actually need to save every step of the way ? can't he search on the local copy and on the db, i thought that part of the fun in entity framework is that i can work "normally" with my class and doesn't have to save my changes every step of the way.
Can i make him "auto save" every step i do, so i won't have to write all the time "SaveChanges"
One more question, i worked previously with NHibrnate and all the mapping where made using simple XML files. i don't see any files here, were is the mapping ? can i change it ?
Thanks in advance
On the lack of an .edmx, see:
http://blogs.msdn.com/b/adonet/archive/2011/03/07/when-is-code-first-not-code-first.aspx
Code First does not use an .edmx file, and it is also called Code Only for that reason. You do the mapping using attributes or fluent API. See the first two posts in the 12-part series on the Entity Framework blog:
http://blogs.msdn.com/b/adonet/archive/2011/01/27/using-dbcontext-in-ef-feature-ctp5-part-1-introduction-and-model.aspx
This should work:
Cat father = new Cat() { Father = null , Name = "Father Kitten", NickName = "Shmil" };
Cat son = new Cat() { Father = father , Name = "Son Kitten" , NickName = "son" };
mAllAnimals.Add(father);
mAllAnimals.Add(son);
context.SaveChanges();
The reason your version doesn't work is that you are looking for the father entity in the database - but you will only add it to the DB when you call SaveChanges()
As for the mapping - your project should have an .edmx file - double clicking that will bring up a designer which also allows you to modify the mapping.

How to save child relationship entities in the Entity Framework

I have an Entity Framework v1 project. I have two entities (Roles and Permissions), which have a many-to-many relationship with each other. I pass in a object to be saved (through a WCF call, I do not create it from a context myself), which has new entries in the many-to-many relationship.
I use "context.ApplyPropertyChanges" to update the record with the new properties. I know that this does not update relationships though. I attempt to either do a ChildCollection.Add(relatedObject); or ChildCollection.Attach(relatedObject).
When I use the "Add" method, I get the error that: The object cannot be added to the ObjectStateManager because it already has an EntityKey. Use ObjectContext.Attach to attach an object that has an existing key.
When I use the "Attach" method, I get the error that: The object cannot be added to the ObjectStateManager because it already has an EntityKey. Use ObjectContext.Attach to attach an object that has an existing key.
I am getting quite frustrated, and I think I can hear the Entity Framework laughing at me.
Does anyone know how I can resolve this?
MyRole x = context.Roles.FirstOrDefault(a => a.RoleId == this.RoleId);
context.ApplyPropertyChanges("Roles", this);
foreach (MyPermission p in this.Permissions)
{
x.Permissions.Add(p);
// ^ or v
x.Permissions.Attach(p);
}
context.SaveChanges();
Thanks.
Wow. After 20 or so straight hours on this problem, I'm starting to hate the Entity Framework. Here is the code that appears to be working currently. I would appreciate any advice on how to make this more streamlined.
I did rework the WCF service so that there is only the one data context. Thanks Craig.
Then I had to change the code to the following:
MyRole x = context.Roles.FirstOrDefault(a => a.RoleId == this.RoleId);
if (x == null) // inserting
{
MyApplication t = this.Application;
this.Application = null;
context.Attach(t);
this.Application = t;
}
else // updating
{
context.ApplyPropertyChanges("Roles", this);
x.Permissions.Load();
IEnumerable<Guid> oldPerms = x.Permissions.Select(y => y.PermissionId);
List<MyPermission> newPerms = this.Permissions.Where(y => !oldPerms.Contains(y.PermissionId)).ToList();
IEnumerable<Guid> curPerms = this.Permissions.Select(y => y.PermissionId);
List<MyPermission> deletedPerms = x.Permissions.Where(y => !curPerms.Contains(y.PermissionId)).ToList();
// new
foreach (MyPermission p in newPerms)
{
x.Permissions.Add(context.Permissions.First(z => z.PermissionId == p.PermissionId));
}
// deleted
foreach (MyPermission p in deletedPerms)
{
x.Permissions.Remove(context.Permissions.First(z => z.PermissionId == p.PermissionId));
}
}
You are using multiple ObjectContexts concurrently (the variable context and whereever this came from). Don't do that. It will only make things very difficult for you. Use one ObjectContext at a time.
I can give more specific advice if you show more code.
I suspect you are getting the errors because the ObjectContext thinks you are trying to add a new entity but finds it already has a EntityKey. I use the AttachTo method of the ObjectContext to attach my already existing entities to their EntitySet. I have had results generating my entities from stubs or hitting the database. This way when you add the entity to the navigation property on your entity, the ObjectContext finds the entity in it's EntitySet and knows it is an existing entity and not a new one. I don't know if this is clear. I could post some code if it would help. As Mr Stuntz said in his answer, posting more of your code would help.