1

I want to be able to use an attribute on a Row Version property of a model to enable or disable Optimistic Concurrency in the OnModelCreating(ModelBuilder modelBuilder) method of my DbContext in EF Core 7.0.0. I was able to make it work in .NET Framework 4.8 but I need to do it in an EF Core project as well.

I have an interface for entities which have a Version property called IVersionedEntity and I want to use a custom attribute where I can define if I want to enable or disable optimistic concurrency.

The attribute:

[AttributeUsage(AttributeTargets.Property, Inherited = true)]
public class LockAttribute : Attribute
{
    public bool IsEnabled { get; private set; }

    public LockAttribute(bool isEnabled = true)
    {
        IsEnabled = isEnabled;
    }
}

And I know I can achieve the enable-disable behavior inside the OnModelCreating(DbModelBuilder modelBuilder) method with this extension method in ASP.NET (.NET Framework):

public static void ConfigureLocking(this DbModelBuilder modelBuilder)
{
    modelBuilder.Types<IVersionedEntity>().Configure(configuration =>
    {
        var versionPropertyInfo = configuration.ClrType.GetProperty("Version");
        var lockAttribute = versionPropertyInfo.GetCustomAttribute<LockAttribute>(true);
        if (lockAttribute != null)
        {
            configuration.Property(entity => entity.Version).IsRowVersion().IsConcurrencyToken(lockAttribute.IsEnabled);
        }
    });
}

My problem is, that the ModelBuilder class inside EF Core is quite different and I do not know how to configure something like this using that class.

Zserbinator
  • 365
  • 2
  • 10

1 Answers1

1

In EF Core 7.0+ it can be done relatively easy with a custom Model building convention. In fact all predefined EF Core data annotations are handled with convention classes similar to this:

public class LockAttributeConvention : PropertyAttributeConventionBase<LockAttribute>
{
    public LockAttributeConvention(ProviderConventionSetBuilderDependencies dependencies) : base(dependencies) { }

    protected override void ProcessPropertyAdded(IConventionPropertyBuilder propertyBuilder, LockAttribute attribute, MemberInfo clrMember, IConventionContext context)
    {
        if (attribute.IsEnabled)
        {
            propertyBuilder.ValueGenerated(ValueGenerated.OnAddOrUpdate, fromDataAnnotation: true);
            propertyBuilder.IsConcurrencyToken(true, fromDataAnnotation: true);
        }
    }
}

Helper method for registering it:


public static ModelConfigurationBuilder AddLockAttributeSupport(this ModelConfigurationBuilder configurationBuilder)
{
    configurationBuilder.Conventions.Add(sp => new LockAttributeConvention(
        sp.GetRequiredService<ProviderConventionSetBuilderDependencies>()));
    return configurationBuilder;
}

and finally call it from ConfigureConventions override:

configurationBuilder.AddLockAttributeSupport();
Ivan Stoev
  • 195,425
  • 15
  • 312
  • 343