dotnet/efcore · error · InvalidOperationException

The property ' . ' is mapped to an output parameter of the…

Error message

The property '{entityType}.{property}' is mapped to an output parameter of the stored procedure '{sproc}', but it is also mapped to an output original value output parameter. A store-generated property can only be mapped to one output parameter.

What it means

Thrown when a single store-generated property is mapped to TWO different non-input parameters of an insert/update sproc — one a normal output parameter and one an output-original-value parameter (ForOriginalValue differs). The validator first tries to remove the property from the storeGeneratedProperties set; if it was already consumed by an earlier output parameter, it scans sproc.Parameters for any other parameter with the same property name, different ForOriginalValue flag, and non-Input direction, and reports the conflict. Store-generated values may only flow back through one channel.

Solutions

  1. Keep only one output mapping for the store-generated property: prefer HasOutputParameter(p => p.Foo) and delete the duplicate HasOutputParameter(... forOriginalValue: true) for the same property.
  2. If you genuinely need the original value too, that property is not store-generated in the sense EF needs — reconfigure it as a regular concurrency token with HasOriginalValueParameter, not an output parameter.
  3. Inspect every parameter on the sproc mapping whose PropertyName equals the offending one and remove all but one with Direction != Input.

Example fix

// before
entityType.InsertUsingStoredProcedure("InsertOrder",
    sp =>
    {
        sp.HasResultColumn(o => o.OrderId);
        sp.HasOutputParameter(o => o.OrderId); // conflict: two outputs for OrderId
    });

// after
entityType.InsertUsingStoredProcedure("InsertOrder",
    sp =>
    {
        sp.HasResultColumn(o => o.OrderId);
    });
Defensive patterns

Strategy: validation

Validate before calling

// Ensure each store-generated property has at most one non-input parameter mapping.
foreach (var et in model.GetEntityTypes())
{
    foreach (var sproc in new[] { et.GetInsertStoredProcedure(), et.GetUpdateStoredProcedure() })
    {
        if (sproc == null) continue;
        var outputsByProp = sproc.Parameters
            .Where(p => p.Direction != System.Data.ParameterDirection.Input && p.PropertyName != null)
            .GroupBy(p => p.PropertyName);
        foreach (var g in outputsByProp.Where(g => g.Select(p => p.ForOriginalValue).Distinct().Count() > 1))
            Console.WriteLine($"Property {g.Key} on {sproc.Name} has conflicting output mappings.");
    }
}

Prevention

When it happens

Trigger: On an Insert/UpdateUsingStoredProcedure, calling both .HasOutputParameter(p => p.Id) and .HasOutputParameter(p => p.Id, forOriginalValue: true) (or two parameters with the same PropertyName but different ForOriginalValue). Mixing a result column for the same property with an output parameter also trips related checks.

Common situations: Wanting EF to capture both current value and original value of a store-generated column; accidentally registering the same property twice via fluent calls in different locations; refactor that moved an output parameter without removing the old mapping.

Related errors


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

Appendix: source

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

            }

            if (parameter.PropertyName == null)
            {
                continue;
            }

            switch (storeObjectIdentifier.StoreObjectType)
            {
                case StoreObjectType.InsertStoredProcedure:
                case StoreObjectType.UpdateStoredProcedure:
                    if (parameter.Direction != ParameterDirection.Input
                        && !storeGeneratedProperties.Remove(property!.Name))
                    {
                        if (sproc.Parameters.Any(p => p.PropertyName == property.Name
                                && p.ForOriginalValue != parameter.ForOriginalValue
                                && p.Direction != ParameterDirection.Input))
                        {
                            throw new InvalidOperationException(
                                RelationalStrings.StoredProcedureOutputParameterConflict(
                                    entityType.DisplayName(), parameter.PropertyName, storeObjectIdentifier.DisplayName()));
                        }

                        throw new InvalidOperationException(
                            RelationalStrings.StoredProcedureOutputParameterNotGenerated(
                                entityType.DisplayName(), parameter.PropertyName, storeObjectIdentifier.DisplayName()));
                    }

                    break;
                case StoreObjectType.DeleteStoredProcedure:
                    if (!property!.IsPrimaryKey()
                        && !property.IsConcurrencyToken)
                    {
                        throw new InvalidOperationException(
                            RelationalStrings.StoredProcedureDeleteNonKeyProperty(
                                entityType.DisplayName(), parameter.PropertyName, storeObjectIdentifier.DisplayName()));
                    }

View on GitHub (pinned to 3a2006ef56)