Assembly.GetAssembly (). GetTypes () возвращает дубликаты - PullRequest
1 голос
/ 25 марта 2019

Это расширение ранее заданного вопроса: Аргументы типа не могут быть выведены из использования. Попробуйте указать аргументы типа явно. Отсутствует потенциальная обработка исключений

Я пытаюсь отладить существующее веб-приложение ASP.NET и получаю исключение при входе в систему:

Аргументы типа не могут быть выведены из использования. Попробуйте указать аргументы типа явно. Отсутствует потенциальная обработка исключений

Насколько я могу судить, ошибка возникает из-за того, что дубликаты сборок загружаются в 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 ]

1 голос
/ 25 марта 2019

Если вы хотите добавить все конфигурации, то, я думаю, с 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);
}
1 голос
/ 25 марта 2019

Я думаю, что проблема будет с:

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 Core. Для EF 6:

    protected override void OnModelCreating(DbModelBuilder modelBuilder)
    {
        base.OnModelCreating(modelBuilder);
        modelBuilder.Configurations.AddFromAssembly(Assembly.GetAssembly(assemblyClassType));
    }
0 голосов
/ 30 марта 2019

Я нашел решение. Покопавшись в представлении результатов и с помощью друга мы обнаружили, что дубликаты на самом деле не являются дубликатами - у них разные GUID. То, что отличало два, - то, что один был вложен, в то время как другой не был вложен. Дубликат, который не был загружен, имел IsNested = True. Таким образом, решение было отфильтровать, где IsNested == false

    var typesToRegister = Assembly.GetAssembly(assemblyClassType).GetTypes()
      .Where(type => type.Namespace != null && type.IsNested == false);
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...