【问题标题】:Assembly.GetAssembly().GetTypes() returns duplicatesAssembly.GetAssembly().GetTypes() 返回重复项
【发布时间】:2019-03-25 03:01:01
【问题描述】:

这是对之前提出的问题的扩展:The type arguments cannot be inferred from the usage. Try specifying the type arguments explicitly. Missing potential exception handling

我正在尝试调试现有的 ASP.NET Web 应用程序并在登录时遇到异常:

类型参数不能从用法中推断出来。尝试明确指定类型参数。缺少潜在的异常处理

据我所知,发生错误是因为在运行以下代码时,varTypesToRegister 中加载了 duplicate 程序集:

 var typesToRegister = Assembly.GetAssembly(assemblyClassType).GetTypes()
          .Where(type => type.Namespace != null)

结果视图中有 91 个元素,最后 10 个元素似乎是重复的,因为它们已经存在于数组/列表的前 0 - 80 个项目中。第一个异常在元素 81 上引发(见下面的屏幕截图)。如您所见,元素 81 已作为元素 24 存在。因此,当尝试将现有的装配添加到模型构建器时会引发异常。

注意:assemblyClassType 只是传入的一个程序集。此代码似乎获取了所有项目程序集,尽管我不确定这是如何发生的或为什么会发生(我是该项目的新手,原始开发人员不可用)。

问题:有没有办法防止重复的程序集被加载到typesToRegister 中?或者,有没有办法阻止代码尝试将副本加载到模型构建器中:

  foreach (Type type in typesToRegister)
    {
        dynamic configurationInstance = Activator.CreateInstance(type);
        modelBuilder.Configurations.Add(configurationInstance); // Exception thrown here
    }

GroupMap.cs

public class GroupMap : EntityTypeConfiguration<Group>
{
    public GroupMap()
    {
        Property(group => group.Name).IsRequired();
        HasMany(group => group.Roles)
            .WithMany(role => role.Groups)
            .Map(m =>
            {
                m.MapLeftKey("GroupId");
                m.MapRightKey("RoleId");
                m.ToTable("GroupRoles");
            });
        HasMany(group => group.Members)
            .WithMany(user => user.Groups)
            .Map(m =>
            {
                m.MapLeftKey("GroupId");
                m.MapRightKey("PersonId");
                m.ToTable("GroupMembers");
            });

    }
}

Group.cs

public class Group : BaseEntity
{
    private ICollection<Person> _members;
    private ICollection<Role> _roles;

    public Group()
    {
        _members = new HashSet<Person>();
        _roles = new HashSet<Role>();
    }
    [Display(Name = "Group Name")]
    [Required(ErrorMessage = "Group Name is required.")]
    [MaxLength(100, ErrorMessage = "Group Name allows only 100 characters.")]
    public string Name { get; set; }

    public virtual ICollection<Person> Members
    {
        get { return _members;  } 
        set { _members = value;  }
    }
    public virtual ICollection<Role> Roles
    {
        get { return _roles; }
        set { _roles = value; }
    }
}

BaseDbContext.cs

public class BaseDbContext<TContext> : DbContext where TContext : DbContext, IDbContext, IObjectContextAdapter
{
    static BaseDbContext()
    {
        Database.SetInitializer<TContext>(null);
    }

    public void RunConventions(DbModelBuilder modelBuilder, Type assemblyClassType)
    {
        // Change default conventions for cascade deletes
        modelBuilder.Conventions.Remove<OneToManyCascadeDeleteConvention>();

        // Id property in everyclass is named class + Id  (i.e CustomerId, JobId, VendorId)
        // Id is always first column in table
        // Could also explicitly determine as key using .Configure(p => p.IsKey() but EF already looks for property of name Id as primary key
        modelBuilder.Properties()
          .Where(p => p.Name == "Id")
          .Configure(p => p.HasColumnOrder(0).HasColumnName((p.ClrPropertyInfo.ReflectedType == null ? "" : p.ClrPropertyInfo.ReflectedType.Name) + "Id"));

        // Add Domain Entity Mapping Configurations
        //var typesToRegister = Assembly.GetAssembly(assemblyClassType).GetTypes()
        //  .Where(type => type.Namespace != null);

        var typesToRegister = Assembly.GetAssembly(typeof(DbContext)).GetTypes()
            .Where(type => type.Namespace != null && type.Namespace.Equals(typeof(BaseDbContext<TContext>).Namespace))
            .Where(type => type.BaseType.IsGenericType && type.BaseType.GetGenericTypeDefinition() == typeof(EntityTypeConfiguration<>));
        foreach (Type type in typesToRegister)
        {
            dynamic configurationInstance = Activator.CreateInstance(type);
            modelBuilder.Configurations.Add(configurationInstance);
        }
    }
}

【问题讨论】:

  • 能否提供准确的错误信息?
  • @KirillPolishchuk 完成。
  • 异常发生时正在执行的行是什么?
  • @StevePy 在第二个块中添加了代码注释。 modelBuilder.Configurations.Add(configurationInstance)。我正在准备一个屏幕截图来显示 ResultsView。
  • @JWeezy 我认为您应该检查 typesToRegister 每种类型是否属于 IEntityTypeConfiguration。

标签: c# asp.net asp.net-mvc entity-framework


【解决方案1】:

如果你想添加所有配置,那么,我认为使用EF 4.5.x,你可以使用AddFromAssembly

modelBuilder.Configurations.AddFromAssembly(GetType().Assembly);
//OR typeof(DBContext).Assembly

关于这个问题,你可以这样尝试吗:

var typesToRegister = Assembly.GetAssembly(typeof(DbContext)).GetTypes()
.Where(type => type.Namespace != null
       && type.Namespace.Equals(typeof(DBContext).Namespace))
      .Where(type => type.BaseType.IsGenericType
      && type.BaseType.GetGenericTypeDefinition() == 
         typeof(EntityTypeConfiguration<>));

foreach (var type in typesToRegister)
{
  dynamic configurationInstance = Activator.CreateInstance(type);
  modelBuilder.Configurations.Add(configurationInstance);
}

【讨论】:

  • 我尝试进行更改,但这也不起作用。结果视图中有零个元素。
【解决方案2】:

我认为问题在于:

var typesToRegister = Assembly.GetAssembly(assemblyClassType).GetTypes()
    .Where(type => type.Namespace != null)

这将提取所有类定义,而不仅仅是 IEntityTypeConfiguration 实现。

尝试将其更新为:

var typesToRegister = Assembly.GetAssembly(assemblyClassType).GetTypes()
    .Where(t => t.GetInterfaces().Any(i => i.IsGenericType && i.GetGenericTypeDefinition() == typeof(IEntityTypeConfiguration<>))).ToList();

这应该只是尝试注册实体类型配置。

另一个小细节是您可能还应该添加一个Ignore(galaxyUser =&gt; galaxyUser.IsSysAdmin);

编辑:抱歉,对于 EF 6,您不需要显式指定要注册的类型,这是 EF Core 限制。对于 EF 6:

    protected override void OnModelCreating(DbModelBuilder modelBuilder)
    {
        base.OnModelCreating(modelBuilder);
        modelBuilder.Configurations.AddFromAssembly(Assembly.GetAssembly(assemblyClassType));
    }

【讨论】:

  • 我收到以下错误:找不到类型或命名空间名称“IEntityTypeConfiguration”(您是否缺少 using 指令或程序集引用?)
  • 我在网上做了一些搜索,IEntityTypeConfiguration 在 Microsoft.EntityFrameworkCore 中被引用。请注意,这不是 .NET CORE 项目 - 它是 ASP.NET MVC。请问,您能想到任何其他潜在的解决方案吗?
  • 啊,我的错...我把它误认为是 EF Core.. 我会针对 EF 6 进行调整。(更简单)
  • 这似乎可行,但在对 OnModelCreating 的调用完成后,我又遇到了另一个异常。我明天会看看这个,看看它是否相关。如果不是,那是一个新问题。感谢您的帮助。
  • TL DR:看来我需要 RunConvections() 方法。 RunConventions() 方法似乎将程序集名称附加到 EF 为所有表假定的默认 ID 列。因此,如果 EF 将 ID 定义为所有表的默认主键列,则实际项目将主键定义为 Table + Id。在 cmets 中,表 = 程序集。因此,如果没有它,后续查询生成器会触发另一个错误。
【解决方案3】:

我找到了解决方案。在结果视图中四处挖掘之后,在朋友的帮助下,我们发现重复项实际上并不是重复项——它们具有不同的 GUID。两者的区别在于一个是嵌套的,另一个是不嵌套的。未加载的副本具有 IsNested = True。因此,解决方案是过滤掉 IsNested == false 的位置

    var typesToRegister = Assembly.GetAssembly(assemblyClassType).GetTypes()
      .Where(type => type.Namespace != null && type.IsNested == false);

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2016-05-08
    • 2016-05-15
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-10-04
    • 2013-08-22
    • 1970-01-01
    相关资源
    最近更新 更多