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

Shadow-state entity exception caused by generic base class? #13227

Closed
sjb-sjb opened this issue Sep 6, 2018 · 9 comments
Closed

Shadow-state entity exception caused by generic base class? #13227

sjb-sjb opened this issue Sep 6, 2018 · 9 comments

Comments

@sjb-sjb
Copy link

sjb-sjb commented Sep 6, 2018

I get this exception:
System.InvalidOperationException
HResult=0x80131509
Message=Entity type 'TreeNode`1[[Q5.Content.Settings.Instrument, Q5U, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null]]' is in shadow-state. A valid model requires all entity types to have corresponding CLR type.
Source=Microsoft.EntityFrameworkCore
StackTrace:
at Microsoft.EntityFrameworkCore.Infrastructure.ModelValidator.ValidateNoShadowEntities(IModel model)
at Microsoft.EntityFrameworkCore.Infrastructure.ModelValidator.Validate(IModel model)

This exception occurs after OnModelCreating and, it seems, before the DbContext constructor is complete. It arose after I created two classes:

    public class TreeNode<TLeafEntity> : ValidatableEntity, ITreeNode
    where TLeafEntity : class, INamedEntity, new()
    {
        private string _folderName;

        private long? _leafId; 
        [ForeignKey(nameof(LeafId))] public TLeafEntity Leaf { get; internal set; } 

        private long? _parentId;
        [ForeignKey(nameof(ParentId))] public TreeNode<TLeafEntity> Parent { get; set; }
        [InverseProperty(nameof(Parent))] public IObservableCollection<TreeNode<TLeafEntity>> Children { get; private set; }

        ...

        public string FolderName { 
            get { return this._folderName; }
            set { this.SetFolderName(value, false, false); }
        }
        ... similar for other properties
    } 

    public InstrumentTreeNode: TreeNode<Instrument>
    {// not much in here really, just a few methods forwarded into TreeNode<Instrument> methods.
    }

The ValidatableEntity class is too complicated to describe here but I use it in all my other code with no problems. It implements notification tracking for the entity.

I set a break point in OnModelCreating( ModelBuilder mb) and found out the following:

mb.Entity<InstrumentTreeNode>()
{Microsoft.EntityFrameworkCore.Metadata.Builders.EntityTypeBuilder`1[Q5.Content.Settings.InstrumentTreeNode]}
Metadata: {EntityType: InstrumentTreeNode Base: TreeNode}

mb.Entity<TreeNode<Instrument>>()
{Microsoft.EntityFrameworkCore.Metadata.Builders.EntityTypeBuilder1[Q5.Framework.TreeNode1[Q5.Content.Settings.Instrument]]}
Metadata: {EntityType: TreeNode}

mb.Entity<InstrumentTreeNode>().Metadata.GetProperties()
{System.Linq.Enumerable.Concat2Iterator<Microsoft.EntityFrameworkCore.Metadata.Internal.Property>}
Current: null
Results View: Expanding the Results View will enumerate the IEnumerable

mb.Entity<TreeNode<Instrument>>().Metadata.GetProperties()
Count = 6
[0]: {Property: TreeNode.Id (_id, long) Required PK AfterSave:Throw ValueGenerated.OnAdd 0 0 0 -1 0}
[1]: {Property: TreeNode.ConcurrencyVersion (_concurrencyVersion, byte[]) Concurrency BeforeSave:Ignore AfterSave:Ignore ValueGenerated.OnAddOrUpdate 1 1 -1 -1 1}
[2]: {Property: TreeNode.Discriminator (no field, string) Shadow Required AfterSave:Throw 2 2 -1 0 -1}
[3]: {Property: TreeNode.FolderName (_folderName, string) Required 3 3 -1 -1 -1}
[4]: {Property: TreeNode.LeafId (_leafId, Nullable) FK Index 4 4 1 -1 2}
[5]: {Property: TreeNode.ParentId (_parentId, Nullable) FK Index 5 5 2 -1 3}

I only included DbSet<InstrumentTreeNode> in the model, not anything like DbSet<TreeNode<Instrument>>. Since the documentation says that Type Per Hierarchy is only triggered when multiple classes are included, the presence of both entities in the model is a surprise. However I assume this is somehow related to the presence of the generic type.

I see the shadow Discriminator property and I wonder if this is triggering the exception.

My main question is, obviously, how do I get it to work?

@ajcvickers
Copy link
Member

@sjb-sjb
EF Team Triage: This issue is lacking enough information for us to be able to effectively triage it. In particular, it is missing the following information requested in the new issue template. Can you please provide this information?

Steps to reproduce

Ideally include a complete code listing that we can run to reproduce the issue.
Alternatively, you can provide a project/solution that we can run.

BTW we're not just doing this to be mean 😄... we get a lot traffic on this project and it takes time to attempt to reproduce an issue based on fragments of information. In addition, our attempt is often unsuccessful as the exact conditions required to hit the issue are often not explicitly included in the code provided. To ensure we maximize the time we have to work on fixing bugs, implementing new features, etc. we ask that folks give us a self-contained way to reproduce an issue.

For a guide on submitting good bug reports, read Painless Bug Tracking.

BTW this is a canned response and may have info or details that do not directly apply to this particular issue. While we'd like to spend the time to uniquely address every incoming issue, we get a lot traffic on the EF projects and that is not practical. To ensure we maximize the time we have to work on fixing bugs, implementing new features, etc. we use canned responses for common triage decisions.

@sjb-sjb
Copy link
Author

sjb-sjb commented Sep 9, 2018

I tried building an example based on the EF Blogging.UWP sample program. However when I put the offending code into that context, it worked.

Then I rebuilt my project by creating a new UWP project and importing my code. I also updated VS by a minor version. And … it worked. So, it seems like a problem connected to the specific project state.

I imagine it is not something that can really be figured out or fixed in any meaningful way. Just a typical SNAFU.

@sjb-sjb sjb-sjb closed this as completed Sep 9, 2018
@sjb-sjb
Copy link
Author

sjb-sjb commented Sep 9, 2018

Well, it worked … until it didn't. I'll keep you posted.

@sjb-sjb sjb-sjb reopened this Sep 9, 2018
@sjb-sjb
Copy link
Author

sjb-sjb commented Sep 14, 2018

I'm still struggling with this. Overall the objective is to have a base class that contains all of the functionality, while having the concrete derived classes stored in the database. The challenge is how to define the navigation relationships in the base class (Parent and Children navigations) and specifically what the type of these should be.

  1. One approach is to use the base class type for the navigations. When I tried this, either I left the base type mapped, in which case I got the exception discussed above saying that the base class entity was in shadow state; or else I used [NotMapped] on the base class in which case DbSet.LoadAsync threw a task cancelled exception.

  2. An alternative is to use an interface type for the navigations. This leads to an exception saying that one should manually configure the relationship by casting it to a mapped entity type. That would mean for example doing .HasMany(tn => (ICollection<InstrumentTreeNode>)h.Children in the model builder. However that bit of configuration requires the derived type information (InstrumentTreeNode).

Overall it seems that EF requires the derived type information in order to create navigation relationships. Is that correct, is that the case?

@sjb-sjb
Copy link
Author

sjb-sjb commented Sep 17, 2018

@ajcvickers, any comment on whether the design of EF allows eager loading related entities using a navigation whose dependant type is the base class rather than the concrete derived class? Noting that the cast notation requires the derived type so it cannot be used in a software design where that type is not available.

@sjb-sjb sjb-sjb closed this as completed Sep 17, 2018
@sjb-sjb sjb-sjb reopened this Sep 17, 2018
@ajcvickers
Copy link
Member

@sjb-sjb That's not something that EF Core currently supports.

@sjb-sjb
Copy link
Author

sjb-sjb commented Sep 17, 2018

OK!! I would certainly vote for it.

@ajcvickers
Copy link
Member

@sjb-sjb I filed #13344 to cover what we have been thinking of in this area. Note that at some point EF will need to be given configuration that tells it how to construct instances of the derived class, although that could come in the form of a factory.

@sjb-sjb
Copy link
Author

sjb-sjb commented Sep 18, 2018

I will add some comments there.

@ajcvickers ajcvickers reopened this Oct 16, 2022
@ajcvickers ajcvickers closed this as not planned Won't fix, can't repro, duplicate, stale Oct 16, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

2 participants