-
-
Notifications
You must be signed in to change notification settings - Fork 158
fix/#445 #446
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
fix/#445 #446
Conversation
…rceType. updated DefaultEntityRepository to correctly attach relationship when creating a resource
@milosloub Good evening! This update solves your issue directly on the HasOne example. It adds a withEntityType value to the HasOne attribute that you should use when separating the resource and entity. I added a test to ResourceEntitySeparationExample AddTests that mimics your use case with Student and Department successfully. @jaredcnance /facepalm. that's what i get for not running tests locally first. |
…more than one property with that type
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for doing this! I suspect this issue also exists with HasMany
? This is a great first step. Maybe in the future we can determine this during construction of the ResourceGraph. But, maybe not.
if (_context.Entry(relationship.Value).State == EntityState.Detached && _context.EntityIsTracked(relationship.Value) == false) | ||
_context.Entry(relationship.Value).State = EntityState.Unchanged; | ||
{ | ||
if (relationship.Key.GetType() != typeof(HasOneAttribute)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Under what circumstances does this happen?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is totally just a guard clause since the dictionary key is RelationshipAttribute.
else | ||
{ | ||
if (_context.Entry(relationship.Value).State == EntityState.Detached && _context.EntityIsTracked(relationship.Value) == false) | ||
_context.Entry(relationship.Value).State = EntityState.Unchanged; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Let's refactor this a bit so it's clearer and we don't have separate definitions for how the EF ChangeTracker entry state is modified. The only thing that needs to be in the conditional is how we determine the relatedEntity
. so, maybe something like:
IIdentifiable relatedEntity = (hasOne.EntityPropertyName != null)
? entity.GetType().GetProperty(hasOne.EntityPropertyName)?.GetValue(entity) as IIdentifiable
: relationship.Value;
if(relatedEntity != null
&& _context.Entry(relatedEntity).State == EntityState.Detached
&& _context.EntityIsTracked(relatedEntity) == false) {
_context.Entry(relatedEntity).State = EntityState.Unchanged;
}
Also, please go ahead and bump to 3.1.0. I'm going to follow the 3.0.0 release with a 3.1.0-beta as soon as this PR lands. |
Hah. Yeah, I was thinking the same thing last night @jaredcnance |
…rceType. updated DefaultEntityRepository to correctly attach relationship when creating a resource
…more than one property with that type
So, after thinking about this a little bit more, I realized that this is probably the de-serializer's responsibility. In order to set these relationships using a pointer, we have to create an instance of the related type. So, what's happening right now (before this PR) is that the
So, this is where I think we need to instantiate the correct type. So, maybe something like: var relatedEntityType = relationshipAttr?.TargetEntityType ?? relationshipAttr.Type;
var relatedInstance = relatedEntityType.New<IIdentifiable>(); and then the [HasOne("department", targetEntity: typeof(Department))]
public DepartmentResource Department { get; set; } What do you think? |
@jaredcnance
|
That's actually an issue I ran into with the first pass! typeof won't work as the mapping because you could have multiple of the same type! That's why I switched it to the property name. Good catch! |
So I was thinking about this more last night. The problem is with Resource/Entity separation, which means that the Resource isn't necessarily the same thing as the Entity when it gets to the Repository. We would have to expose the IResourceMapper further up. But then we're already doing the resource mapping within the ResourceService so I started thinking about what we're trying to do with this whole pointers thing and I realized that, since the spec doesn't support creating the relationship entity on a POST, that all we really need to do it grab the ID from the Entity at the resource level, look up the affected relationship properties (that we can identify via the EntityPropertyName of the attribute) and detach them. The question that I really have is whether or not EF will even care. I didn't think it would update the navigation property attributes by default on a save. I'm going to do some testing outside of the project to confirm that, because if that's the case, we don't need this at all I don't think. |
@roblankey can you push a repo with your test and I can see what the difference is? But, generally EF will try to create both resources if I do: var article = new Article { Author = new Author { Id = 1 } };
dbContext.Articles.Add(article);
await dbContext.SaveChangesAsync(); |
Just had an "A-Ha" moment. My original tests were only populating the navigation property identifier. When I populate the the navigation property entity as well, with only that Id, EF does throw that exception. To that end, what do you think about the DeSerializer not serializing HasOne / HasMany inputs?, instead just utilizing the identifers? The thought process there being that the specification is NOT supposed to allow specification of attributes on the relationship object. |
The relevant pieces of the spec:
` Resource linkage MUST be represented as one of the following: null for empty to-one relationships. |
There's a reason we had to do it that way (which I can't remember right now -- will comment back later when I have a better answer and more time). But, how would that work with EF and HasMany relationships since the target resource is the independent side of the relationship (no FKs). |
Cool, would be good to know if we ran into other issues. Good question on the HasMany, hadn't thought of that yet. That may make us detach anyway. I'll run some more tests as well. |
Got the HasMany piece complete! Sorry it took so long! |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nice work @roblankey! Sorry for the delay. Feel free to ping me on our Gitter channel if you need to nag me a bit. I'm going to approve and merge this in. I'll probably release it as an alpha in the next couple days so you guys can start using it. But I want to spend some more time on this when I'm available before releasing it as a stable release.
Closes #445
BUG FIX