Skip to content
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

GetDatabaseValues for owned type throws exception if the type is only used by one entity #18366

Closed
coder925 opened this issue Oct 14, 2019 · 5 comments · Fixed by #18434
Closed
Assignees
Labels
closed-fixed The issue has been fixed and is/will be included in the release indicated by the issue milestone. customer-reported type-bug
Milestone

Comments

@coder925
Copy link

coder925 commented Oct 14, 2019

Calling EntityEntry.GetDatabaseValues() on an owned type can raise an exception in certain circumstances.

Steps to reproduce

  1. Run this simplified project that is only using the Order and ShipmentAddress entities:
    https://github.com/coder925/EntityFramework.Docs/tree/owned-type-issue/samples/core/Modeling/OwnedEntities

Expected result: Only the changed properties of the owned entity is saved.
Actual result: An exception:

System.InvalidOperationException
  HResult=0x80131509
  Message=Cannot create a DbSet for 'StreetAddress' because it is configured as an owned entity type and should be accessed through the defining entities.
  Source=Microsoft.EntityFrameworkCore
  StackTrace:
   at Microsoft.EntityFrameworkCore.Internal.InternalDbSet`1.get_EntityType()
   at Microsoft.EntityFrameworkCore.Internal.InternalDbSet`1.CheckState()
   at Microsoft.EntityFrameworkCore.Internal.InternalDbSet`1.get_EntityQueryable()
   at Microsoft.EntityFrameworkCore.Internal.InternalDbSet`1.System.Linq.IQueryable.get_Provider()
   at Microsoft.EntityFrameworkCore.EntityFrameworkQueryableExtensions.AsNoTracking[TEntity](IQueryable`1 source)
   at Microsoft.EntityFrameworkCore.Internal.EntityFinder`1.GetDatabaseValuesQuery(InternalEntityEntry entry)
   at Microsoft.EntityFrameworkCore.Internal.EntityFinder`1.GetDatabaseValues(InternalEntityEntry entry)
   at Microsoft.EntityFrameworkCore.ChangeTracking.EntityEntry.GetDatabaseValues()
   at EFModeling.OwnedEntities.Program.Main(String[] args) in C:\repos-github\EntityFramework.Docs\samples\core\Modeling\OwnedEntities\Program.cs:line 46

Workaround:
Uncomment code in OwnedEntityContext:

        protected override void OnModelCreating(ModelBuilder modelBuilder)
        {
            #region OwnsOne
            modelBuilder.Entity<Order>().OwnsOne(p => p.ShippingAddress);
            #endregion

            // Uncomment following line and it starts working
            #region OwnsOne
            //// modelBuilder.Entity<Order2>().OwnsOne(p => p.ShippingAddress);
            #endregion


        }

It seems if more than one Entity is owning ShipmentAddress the excpetion is not occuring.

Use case

I am receiving a detached Order entity that needs to be updated in the database. However, I am only allowed to save actual changed properties and cannot use context.Update(order) .

Instead, I use this read-update pattern:

var orderEntry = context.Attach(order);
orderEntry.OriginalValues.SetValues(orderEntry.GetDatabaseValues());

and for each owned type owned by Order (i.e. value objects):

var shippingAddressEntry = context.Entry(order.ShippingAddress);
shippingAddressEntry.OriginalValues.SetValues(shippingAddressEntry.GetDatabaseValues());

Further technical details

EF Core version: 3.0.0
Database provider: Microsoft.EntityFrameworkCore.SqlServer
Target framework: .NET Core 3.0
Operating system: Windows 10 Enterprise
IDE: Visual Studio 2019 16.3.0

@smitpatel
Copy link
Contributor

var shippingAddressEntry = context.Entry(order.ShippingAddress);

This is invalid. We don't have a way to determine the IEntityType so it should throw. Further, given the "work-around", we are also processing owned & weak entity differently in that code path.

@coder925
Copy link
Author

Thank you @smitpatel for pointing me in the right direction. I was confused because I got it to work sometimes depending on how the model was configured as showcased.

Do you know the right way to get database values for an owned type so I can keep track on which properties actually been changed?

@smitpatel
Copy link
Contributor

Untested but try this

var shippingAddressEntry = orderEntry.Reference("ShippingAddress").TargetEntry;

@coder925
Copy link
Author

Thanks for your suggestion!
Unfortunately, it gives the same exception when trying to fetch the database values on the next line:

var dbValues = shippingAddressEntry.GetDatabaseValues();

@AndriySvyryd
Copy link
Member

This is related to #16186

@smitpatel smitpatel added this to the 3.1.0 milestone Oct 17, 2019
@smitpatel smitpatel self-assigned this Oct 17, 2019
smitpatel added a commit that referenced this issue Oct 17, 2019
Specifically for owned entities

Resolves #16186
Resolves #18366
@smitpatel smitpatel added the closed-fixed The issue has been fixed and is/will be included in the release indicated by the issue milestone. label Oct 17, 2019
smitpatel added a commit that referenced this issue Oct 17, 2019
Specifically for owned entities

Resolves #16186
Resolves #18366
smitpatel added a commit that referenced this issue Oct 18, 2019
Specifically for owned entities

Resolves #16186
Resolves #18366
@ajcvickers ajcvickers modified the milestones: 3.1.0, 3.1.0-preview2 Oct 24, 2019
@ajcvickers ajcvickers modified the milestones: 3.1.0-preview2, 3.1.0 Dec 2, 2019
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
closed-fixed The issue has been fixed and is/will be included in the release indicated by the issue milestone. customer-reported type-bug
Projects
None yet
Development

Successfully merging a pull request may close this issue.

4 participants