Как игнорировать вложенные перечисления из сопоставления

#c# #entity-framework #ef-code-first #entity-framework-6 #ef-fluent-api

#c# #entity-framework #ef-code-first #entity-framework-6 #ef-fluent-api

Вопрос:

Для удобства у меня есть несколько ссылочных объектов с вложенными перечислениями. Например:

 public class StatusA
{
    public enum Values
    {
        Active = 1,
        Inactive = 2,
        InProgress = 3
    }
}

public class StatusB
{
    public enum Values
    {
        Sent = 1,
        Accepted = 2,
        Expired = 3
    }
}

public class EntityA
{
    public StatusA.Values Status {get; set;}
}

public class EntityB
{
    public StatusB.Values Status {get; set;}
}
  

Во время настройки модели я получаю следующее исключение: тип ‘StatusA Values’ и тип ‘StatusB Values’ имеют одно и то же простое имя ‘Values’ и поэтому не могут использоваться в одной и той же модели. Все типы в данной модели должны иметь уникальные простые имена. Используйте ‘NotMappedAttribute’ или вызовите Ignore в Code First fluent API, чтобы явно исключить свойство или тип из модели.

Пытаясь это исправить, я обнаружил, что NotMappedAttribute неприменим к перечислениям. Я также пробовал fluent API .Ignore<T> (который также требует ref type, а не enum) и .Ignore(IEnumerable<Type>) , но не повезло. Поиск в Google также не очень помог.

Есть ли какой-либо другой способ исключить эти перечисления из модели?

Комментарии:

1. пробовал private, а не public?

2. Спасибо, но они должны быть общедоступными.

Ответ №1:

Вы должны получать тип перечисления в своей модели только в том случае, если вы действительно включаете свойство этого типа. Если вы это сделаете, не может быть никакого способа игнорировать тип, не игнорируя также свойство. И тестирование показывает, что игнорирования свойства достаточно, чтобы также игнорировать тип.

Вот минимальная полная тестовая программа для получения исключения, которое вы получаете:

 using System.Data.Entity;
using System.Data.Entity.Infrastructure;

public class A
{
    public int Id { get; set; }

    public E P { get; set; } // #1

    public enum E { }
}

public class B
{
    public int Id { get; set; }

    public E P { get; set; } // #2

    public enum E { }
}

static class Program
{
    static void Main()
    {
        var modelBuilder = new DbModelBuilder(DbModelBuilderVersion.Latest);
        modelBuilder.Entity<A>();
        modelBuilder.Entity<B>();
        var model = modelBuilder.Build(new DbProviderInfo("System.Data.SqlClient", "2012"));
    }
}
  

Должно быть очевидно, что до тех пор, пока # 1 и # 2 являются частью модели, вы не можете избежать сопоставления типов перечисления. И если вы удалите #1 или # 2 (или оба) или пометите их NotMapped атрибутом, вы увидите, что вы больше не получаете исключение, которое получаете сейчас.

Возможно, вам удастся избежать переименования типа перечисления, если вы можете избежать сопоставления свойства этого типа, например:

 public class A
{
    public int Id { get; set; }

    public int PAsInt { get; set; }

    [NotMapped]
    public E P {
        get { return (E) PAsInt; }
        set { PAsInt = (int) value; }
    }

    public enum E { }
}
  

Это был старый подход, который требовался еще тогда, когда EF вообще не поддерживал типы перечислений. К сожалению, этот подход означает, что запрос context.As.Where(a => a.P == E.C) не будет работать, потому что модель не знает об P этом свойстве. Это должно быть записано как context.As.Where(a => a.PAsInt == (int)E.C) . В зависимости от ваших потребностей, это может быть достаточно хорошо.

Комментарии:

1. Спасибо за ваш ответ. Вы правы, у меня есть эти свойства (типы перечислений) в моей модели. Я также обновил вопрос, чтобы отразить это. К сожалению, мне нужно сопоставить эти свойства, они должны сохраняться в БД. Пока единственное решение, которое я вижу, — дать этим перечислениям разные имена. Есть ли другой вариант?

2. 1 Я не знал об этом использовании DbModelBuilder . Хорошо для демонстрационных целей!

3. @Jk_xp Есть, и я обновил свой ответ. Иногда это будет хорошим выбором, а иногда и плохим выбором, поэтому я не могу сказать, будет ли это приемлемо для вас.

4. @GertArnold Спасибо. Если вам интересно, вы можете продолжить его с var compiledModel = model.Compile(); var objectContext = compiledModel.CreateObjectContext<ObjectContext>(new SqlConnection()); помощью to test SQL generation: Console.WriteLine(((ObjectQuery)objectContext.CreateObjectSet<A>().Where(a => a.Id == 3)).ToTraceString()); (Возможно, я где-то допустил ошибку в синтаксисе, но вы должны понять идею.)

5. @hvd Спасибо, я назвал сохраняемое свойство «StatusID», а фактическое перечисление «Status», и я думаю, что это сработает для меня.