Пользовательское ограничение C # Entity Framework

#c# #entity-framework

#c# #entity-framework

Вопрос:

В настоящее время я пытаюсь создать пользовательское ограничение с помощью C # Entity Framework. Более подробно:

У меня есть числовое поле, которое может иметь только определенные значения (например, 1, 2 и 3). Как мне достичь этого ограничения в среде code first?

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

1. Что со всеми тегами Entity Framework?

2. Извините, удалил дубликаты

Ответ №1:

Entity Framework автоматически проверяет любые проверки, которые вы добавляете в свою модель с помощью ValidationAttribute s. RequiredAttribute или RangeAttribute — это два примера встроенных подклассов этого атрибута.

Если вам нужна какая-то пользовательская проверка, самый удобный способ — воспользоваться этим механизмом и создать свой собственный ValidationAttribute подкласс.

Если вы хотите проверить несмежный диапазон значений, который вы не можете использовать RangeAttribute , но вы могли бы создать собственный атрибут, например, такой:

 public class AllowedValuesAttribute : ValidationAttribute
{
    private readonly ICollection<object> _validValues;
    private readonly Type _type;

    public AllowedValuesAttribute(Type type, object[] validValues)
    {
        _type = type;
        _validValues = validValues.Where(v => v != null amp;amp; v.GetType() == type).ToList();
    }

    public override bool IsValid(object value)
    {
        return value.GetType() == _type amp;amp; _validValues.Contains(value);
    }

    public override string FormatErrorMessage(string name)
    {
        return string.Format("Value for '{0}' is not an allowed value", name);
    }
}
  

Использование:

 [AllowedValues(typeof(int), new object[] { 1, 2, 4, 8, 16 })]
public int Base { get; set; }
  

Обратите внимание, что здесь мы должны использовать фиксированные значения, потому что содержимое атрибута должно быть известно во время компиляции. Кроме того, мы должны использовать object , потому что (в настоящее время) C # не поддерживает универсальные атрибуты. Помимо этого, существует множество вариантов. Например, у атрибута также может быть метод, который находит разрешенные значения во время выполнения, возможно, из именованного источника, поэтому вы можете указать это имя в его конструкторе.

Я не вижу никакой проблемы в украшении классов сущностей атрибутами проверки. Модель сущности не является моделью предметной области, это часть уровня доступа к данным. Его основной целью является (и должно быть) облегчение доступа приложения к данным. Если модель сущности также поддерживает бизнес-логику, это просто бонус.

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

1. подумав об этом, я все еще понятия не имею, зачем s.o. использовать это. Атрибут для этого просто неверный. Это видно из вашего примера: здесь это было бы Flag Enum или простое Enum , если бы значения были, например, 1,3,7,20,... . Если данные имеют определенное значение, вы бы создали таблицу и использовали PK как FK. Если вам действительно нужно использовать int (потому что, не знаю почему), вы бы обработали это в контроллере или использовали аннотацию в viewmodel, поскольку другой контроллер, возможно, мог бы ввести другой int — (если нет, перечислите или FK это).. Извините, но я все еще не вижу никаких причин для его использования в классе сущностей.

2. @MatthiasBurger В приложении вы хотите иметь единую точку проверки . Я бы никогда не стал использовать средство установки свойств, которое выдает исключение. Это вводит множество потенциальных мест кода, где должны обрабатываться исключения. И когда запускается проверка EF, свойство не проверяется. Преимущество атрибута в том, что он проверяется в нужное время и правильным образом, т. Е. так же, как и другие проверки EF. Любые нарушения красиво перечислены среди других ошибок проверки. Вы получаете многое бесплатно.

3. @MatthiasBurger Конечно, я бы не стал использовать его для проверки FK и да, я бы рекомендовал перечисления. В относительно простых случаях может быть достаточно атрибута.

4. проблема с проверкой верна, и я согласен с этим пунктом. Но imo entity-class слишком поздно для такого рода проверки. Поскольку это проверка представления, я думаю, она уже должна быть проверена в viewmodel (если только вы не используете viewmodels и не используете классы сущностей в своих представлениях). Viewmodel также может быть базовым классом для viewmodels, так что у вас есть эта единственная точка проверки. Например. an AccountViewModel является базовым для LoginViewModel и RegisterViewModel . Я бы проверил почтовый адрес в AccountViewModel и отправил действительные данные в entity-class Applicationuser . Или я ошибаюсь?

5. @MatthiasBurger, с которым я полностью согласен 🙂 Мне не нравится перебрасывать полные объекты entity по проводам. Но модели представления также могут иметь атрибуты. Все зависит от предпочтений. Например, с ASP.Net MVC, работа с атрибутами проверки (в любом классе модели) очень удобна. Тем не менее, атрибуты проверки (или реализации IValidatableObject — мое предпочтение) в классах сущностей обеспечивают хорошую окончательную защиту от попадания мусора в базу данных.

Ответ №2:

Добавлять примечания к данным в domain-model — очень плохая практика, как писал Д.Мак. Так что насчет того, чтобы сделать это более приятным способом?

 public MyClass
{    
    private int myNumberField;
    public int MyNumberField 
    { 
        get { return myNumberField; } 
        set
        { 
            if (value >= 1 amp;amp; value <=3)
                myNumberField = value;
            else
                // throw exception?
                // set default-value (maybe 1)?
                // do nothing?
        }
    }

}
  

вы могли бы делать все, что хотите, в установщике вашего свойства

и ограничивать его только во внешнем интерфейсе — не лучшее решение, поскольку вы всегда можете изменить javascript / html — но вы должны показать пользователю, что он может вставлять значения только 1, 2 или 3. Также ограничьте его в viewmodel аннотациями данных.

или:

вы также можете переопределить EntityFrameworks SaveChanges и добавить свою businesslogic:

 public override int SaveChanges(SaveOptions options)
{

    foreach (ObjectStateEntry entry in
        ObjectStateManager.GetObjectStateEntries(
        EntityState.Added | EntityState.Modified))
    {
        // Validate the objects in the Added and Modified state
        // if the validation fails, e.g. throw an exeption.
    }
    return base.SaveChanges(options);
}