dotnet/efcore · error · InvalidOperationException

The view ' ' cannot be used for entity type ' ' since it is…

Error message

The view '{view}' cannot be used for entity type '{entityType}' since it is being used for entity type '{otherEntityType}' and there is no relationship between their primary keys.

What it means

View-mapping analog of error 393/397. Thrown by ValidateSharedViewCompatibility when two or more entity types share a view but there is no PK-to-PK FK between them and no inheritance relationship. The message lists the view, the unlinked entity type, and the root (first type to claim the view).

Solutions

  1. If the entities really represent different rows of the same view, add a 1:1 PK FK: builder.Entity<B>().HasOne<A>().WithOne().HasForeignKey<B>(b => b.Id).
  2. Map one of them to a different view, or replace ToView with ToTable if writes are intended.
  3. Use keyless entities (HasNoKey) and ToView for genuine query-only types so they do not collide on the PK-share path.

Example fix

// before
builder.Entity<Customer>().ToView("CustomerView");
builder.Entity<Order>().ToView("CustomerView"); // unlinked

// after
builder.Entity<Order>(b =>
{
    b.ToView("CustomerView");
    b.HasOne<Customer>().WithOne().HasForeignKey<Order>(o => o.CustomerId);
});
Defensive patterns

Strategy: validation

Validate before calling

var byView = model.GetEntityTypes().Where(e => e.GetViewName() != null).GroupBy(e => e.GetViewName());
foreach (var grp in byView)
{
    var list = grp.ToList();
    for (int i = 1; i < list.Count; i++)
    {
        var et = list[i];
        bool linked = et.BaseType != null && list.Any(t => t.IsAssignableFrom(et))
            || et.FindForeignKeys(et.FindPrimaryKey()?.Properties ?? new List<Microsoft.EntityFrameworkCore.Metadata.IProperty>())
                .Any(fk => list.Contains(fk.PrincipalEntityType) && fk.PrincipalKey.IsPrimaryKey());
        if (!linked)
            Console.WriteLine($"{et.Name} shares view '{grp.Key}' with {list[0].Name} but has no link.");
    }
}

Prevention

When it happens

Trigger: builder.Entity<A>().ToView("V") and builder.Entity<B>().ToView("V") with no HasOne/WithOne PK FK between them and no inheritance link. ValidateSharedViewCompatibility finds root != null when processing the second type and throws.

Common situations: Read-only reporting views shared by multiple entity types without an explicit 1:1 relationship; refactor that renamed a view and caused a collision; using ToView for the wrong purpose (should be a query root, not a table-share).

Related errors


AI-assisted analysis of dotnet/efcore@3a2006ef56 (2026-08-11). Data as JSON: /api/errors/7abeaf5793ba16a9. Report an issue: GitHub.

Appendix: source

Thrown at src/EFCore.Relational/Infrastructure/RelationalModelValidator.cs:1360

                if (mappedType.BaseType != null)
                {
                    var principalType = mappedType.FindForeignKeys(mappedType.FindPrimaryKey()!.Properties)
                        .First(fk => fk.PrincipalKey.IsPrimaryKey()
                            && unvalidatedTypes.Contains(fk.PrincipalEntityType))
                        .PrincipalEntityType;
                    throw new InvalidOperationException(
                        RelationalStrings.IncompatibleViewDerivedRelationship(
                            storeObject.DisplayName(),
                            mappedType.DisplayName(),
                            principalType.DisplayName()));
                }

                continue;
            }

            if (root != null)
            {
                throw new InvalidOperationException(
                    RelationalStrings.IncompatibleViewNoRelationship(
                        storeObject.DisplayName(),
                        mappedType.DisplayName(),
                        root.DisplayName()));
            }

            root = mappedType;
        }

        Check.DebugAssert(root != null);
        unvalidatedTypes.Remove(root);
        var typesToValidate = new Queue<IEntityType>();
        typesToValidate.Enqueue(root);

        while (typesToValidate.Count > 0)
        {
            var entityType = typesToValidate.Dequeue();
            var typesToValidateLeft = typesToValidate.Count;

View on GitHub (pinned to 3a2006ef56)