Assembly.GetAssembly()。GetTypes()返回重复项

时间:2019-03-25 03:01:02

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

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

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

  

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

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

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

“结果”视图中有91个元素,而最后10个元素似乎是重复的,因为它们已存在于数组/列表的前0-80个项目中。第一个异常引发在元素81上(请参见下面的屏幕截图)。如您所见,元素81已经作为元素24存在。因此,当试图将已经存在的程序集添加到modelBuilder时,将引发异常。

enter image description here

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

问题:是否有办法防止将重复的程序集加载到typesToRegister中?或者,有没有一种方法可以防止代码尝试将重复代码加载到modelBuilder中:

  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);
        }
    }
}

3 个答案:

答案 0 :(得分:1)

我认为问题在于:

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 => galaxyUser.IsSysAdmin);

编辑:对不起,对于EF 6,您无需显式指定要注册的类型,即EF核心限制。对于EF 6:

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

答案 1 :(得分: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 :(得分:0)

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

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