如何让EF Core 9忽略特定模型属性以避免依赖缺失异常?
解决EF Core 9中PrivateAssets属性包导致的程序集加载异常问题
问题根源
你的数据库类库用PrivateAssets="all"引入了PumlGenerator属性包,下游项目不会继承这个依赖。但EF Core的默认属性扫描约定会在模型创建时反射读取实体类上的属性,当它尝试加载Puml属性的类型时,因为下游项目没有该程序集,直接抛出FileNotFoundException。你之前写的约定因为执行顺序晚于默认属性约定,还没来得及运行就报错了。
可行解决方案
方案1:自定义属性约定,提前拦截异常
创建一个自定义约定,替换默认的属性扫描逻辑,在读取属性时捕获Puml程序集的加载异常并跳过处理:
internal class SkipPumlAttributesConvention : TypeAttributeConventionBase<Attribute> { public SkipPumlAttributesConvention(ProviderConventionSetBuilderDependencies dependencies) : base(dependencies) { } // 重写获取属性的方法,拦截Puml程序集的加载异常 protected override IEnumerable<Attribute> GetAttributes(Type type) { try { return base.GetAttributes(type); } catch (FileNotFoundException ex) { // 仅忽略PumlGenerator相关的加载异常 if (ex.FileName.StartsWith("PumlGenerator.Attributes")) { return Enumerable.Empty<Attribute>(); } // 其他异常正常抛出 throw; } } protected override IEnumerable<Attribute> GetAttributes(MemberInfo memberInfo, bool inherit) { try { return base.GetAttributes(memberInfo, inherit); } catch (FileNotFoundException ex) { if (ex.FileName.StartsWith("PumlGenerator.Attributes")) { return Enumerable.Empty<Attribute>(); } throw; } } // 空实现处理逻辑,因为我们只需要拦截异常 protected override void ProcessEntityTypeAdded(IConventionEntityTypeBuilder entityTypeBuilder, Attribute attribute, IConventionContext<IConventionEntityTypeBuilder> context) { } }
然后在ConfigureConventions中替换默认约定:
protected override void ConfigureConventions(ModelConfigurationBuilder configurationBuilder) { base.ConfigureConventions(configurationBuilder); var conventionSet = configurationBuilder.CreateConventionSet(); // 移除默认的属性扫描约定 var defaultTypeConvention = conventionSet.EntityTypeAddedConventions .OfType<TypeAttributeConvention>() .FirstOrDefault(); if (defaultTypeConvention != null) { conventionSet.EntityTypeAddedConventions.Remove(defaultTypeConvention); } // 插入自定义约定到最前面 conventionSet.EntityTypeAddedConventions.Insert(0, new SkipPumlAttributesConvention(configurationBuilder.Dependencies)); configurationBuilder.ReplaceConventionSet(conventionSet); }
方案2:自定义元数据读取器,过滤属性
通过替换EF的IMetadataReader服务,在元数据读取阶段就跳过Puml属性的加载:
public class FilteredMetadataReader : IMetadataReader { private readonly IMetadataReader _innerReader; public FilteredMetadataReader(IMetadataReader innerReader) { _innerReader = innerReader; } public IEnumerable<IAnnotation> GetAnnotations(MemberInfo memberInfo) => _innerReader.GetAnnotations(memberInfo); public IEnumerable<IAnnotation> GetAnnotations(Type type) => _innerReader.GetAnnotations(type); public IEnumerable<IPropertyBase> GetProperties(Type type) => _innerReader.GetProperties(type); public IEnumerable<Attribute> GetAttributes(Type type, bool inherit) { try { return _innerReader.GetAttributes(type, inherit); } catch (FileNotFoundException ex) { return ex.FileName.StartsWith("PumlGenerator.Attributes") ? Enumerable.Empty<Attribute>() : throw; } } public IEnumerable<Attribute> GetAttributes(MemberInfo memberInfo, bool inherit) { try { return _innerReader.GetAttributes(memberInfo, inherit); } catch (FileNotFoundException ex) { return ex.FileName.StartsWith("PumlGenerator.Attributes") ? Enumerable.Empty<Attribute>() : throw; } } }
在DbContext的配置中替换服务:
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) { base.OnConfiguring(optionsBuilder); optionsBuilder.ReplaceService<IMetadataReader>(sp => { var defaultReader = sp.GetRequiredService<IMetadataReader>(); return new FilteredMetadataReader(defaultReader); }); }
方案3:编译时移除属性引用(如果属性包支持)
如果PumlGenerator属性仅用于编译时生成PUML文件,可给属性添加[Conditional("PUML_GENERATION")]特性,只在指定编译符号下保留属性。这样下游项目编译时没有该符号,实体类上就不会有这些属性的引用,从根源避免问题。
内容的提问来源于stack exchange,提问作者Gargoyle
相关产品推荐
相关产品推荐

