public abstract void Process(EfProcessorContext ctx, EfPropertyDefinition definition);
public abstract void Process(EfProcessorContext ctx, IReadOnlyCollection <EfPropertyDefinition> properties);
public static ModelBuilder BuildCustomAttributes(this ModelBuilder model, IEfCustomContext dbContext) { var entityTypes = model.Model.GetEntityTypes(); var modelChangeLock = new object(); Parallel.ForEach(entityTypes, entityType => { // matches, ctx and the tally are lazily loaded to avoid computing when no actual matching attributes // exist. MultiValueDictionary <EfIndirectProcessorBaseAttribute, EfPropertyDefinition> matches = null; EfProcessorContext ctx = null; ISet <Type> uniqueAttributeTally = null; foreach (var prop in entityType.ClrType.GetProperties()) { //TODO does this actually include types inheriting that type? var attrs = Attribute.GetCustomAttributes(prop, typeof(EfPropertyBaseAttribute)); // note to self: we don't restart the tally for every new property, that ruins the entire point, we // have AttributeUsage.AllowMultiple for that foreach (var attr in attrs) { // lazily initialize the context (so we don't do model.Entity when we don't need to) if (ctx == null) { ctx = new EfProcessorContext { Model = model, Entity = model.Entity(entityType.ClrType), EntityType = entityType, DatabaseContext = dbContext, }; } // this is better kept local var definition = new EfPropertyDefinition { Property = prop, Source = attr, }; // we need the EfPropertyBaseAttribute value, but we can't do that inside the switch block, so // we're using pattern matching here if (!(attr is EfPropertyBaseAttribute baseAttr)) { continue; } if (!baseAttr.CanHaveMultiple) { if (uniqueAttributeTally == null) { // lazily initialize the tally // if there is no tally, there is no need to check if it contains the type uniqueAttributeTally = new HashSet <Type> { baseAttr.GetType() }; } else if (!uniqueAttributeTally.Add(baseAttr.GetType())) { throw new EfAttributeException( $"The property {prop} contains multiple instances of {baseAttr.GetType()}"); } } switch (attr) { case EfPropertyProcessorBaseAttribute processor: lock (modelChangeLock) { processor.Process(ctx, definition); } break; case EfIndirectProcessorBaseAttribute indirectProcessor: if (indirectProcessor.PropertyMatches(ctx, definition)) { if (matches == null) { matches = new MultiValueDictionary <EfIndirectProcessorBaseAttribute, EfPropertyDefinition>(); } matches.Add(indirectProcessor, definition); } break; } }
public abstract bool PropertyMatches(EfProcessorContext ctx, EfPropertyDefinition definition);