13

ASP.NET MVC 4, EF5, Code First, SQL Server 2012 ЭкспрессASP.NET MVC 4, EF5, уникальное свойство в модели - лучшая практика?

Что лучше практика для обеспечения уникального значения в модели? У меня есть класс мест, у которого есть свойство url, которое должно быть уникальным для каждого места.

public class Place 
{ 
     [ScaffoldColumn(false)] 
     public virtual int PlaceID { get; set; } 

     [DisplayName("Date Added")] 
     public virtual DateTime DateAdded { get; set; } 

     [Required(ErrorMessage = "Place Name is required")] 
     [StringLength(100)] 
     public virtual string Name { get; set; } 

     public virtual string URL { get; set; } 
}; 

Почему нет только уникальной аннотации данных, которую вы можете разместить на ней?

Я видел 1 или 2 обсуждения этого вопроса, но не говорил о лучшей практике. Используя Code First, вы можете каким-то образом сообщить базе данных о том, чтобы установить уникальное ограничение в поле в базе данных?

Что является самым простым способом - и что является лучшей практикой?

ответ

23

Как сумасшедший, как может показаться, лучшая практика в настоящее время составляет не использовать встроенную проверку и вместо этого использовать FluentValidation. Тогда код будет очень прост в чтении и супер-ремонтопригодном, так как валидация будет управляться в отдельном классе, что означает меньше кода спагетти.

Псевдо-пример того, чего вы пытаетесь достичь.

[Validator(typeof(PlaceValidator))] 
class Place 
{ 
    public int Id { get; set; } 
    public DateTime DateAdded { get; set; } 
    public string Name { get; set; } 
    public string Url { get; set; } 
} 

public class PlaceValidator : AbstractValidator<Place> 
{ 
    public PlaceValidator() 
    { 
     RuleFor(x => x.Name).NotEmpty().WithMessage("Place Name is required").Length(0, 100); 
     RuleFor(x => x.Url).Must(BeUniqueUrl).WithMessage("Url already exists"); 
    } 

    private bool BeUniqueUrl(string url) 
    { 
     return new DataContext().Places.FirstOrDefault(x => x.Url == url) == null 
    } 
} 
+0

Интересно.Поэтому мне нужно будет использовать это только для любых уникальных свойств - все остальное может оставаться как есть, обычный код First right? Охватывает ли это все базы, условия гонки, 100% надежную уникальность в базе данных и т. Д.? – niico

+0

Мне также нужно установить пакет fluentvalidation nuget? – niico

+0

Этот пример будет работать с POCO без проблем, я использую это во всех своих проектах. Как вы можете видеть, он проверяет uniquness в базе данных на 'TryValidateModel()', после которого вы обычно вызываете '_db.SaveChanges();' поэтому в основном нет латентности вообще. Да, конечно, вам понадобится пакет nuget. – sed

4

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

public override void Up() { 
    // create table 
    CreateTable("dbo.MyTable", ...; 
    Sql("ALTER TABLE MyTable ADD CONSTRAINT U_MyUniqueColumn UNIQUE(MyUniqueColumn)"); 
} 
public override void Down() { 
    Sql("ALTER TABLE MyTable DROP CONSTRAINT U_MyUniqueColumn"); 
} 

Жесткий бит, тем не менее, устанавливает ограничение на уровне кода, прежде чем вы попадете в базу данных. Для этого вам может понадобиться репозиторий, который содержит полный список уникальных значений и гарантирует, что новые объекты не будут нарушать это с помощью фабричного метода.

// Repository for illustration only 
public class Repo { 
    SortedList<string, Entity1> uniqueKey1 = ...; // assuming a unique string column 
    public Entity1 NewEntity1(string keyValue) { 
    if (uniqueKey1.ContainsKey(keyValue) throw new ArgumentException ... ; 
    return new Entity1 { MyUniqueKeyValue = keyValue }; 
    } 
} 

Литература:

Footnot е:

Есть много запросов на [Unique] в коде первой, но, похоже, он даже не делает версии 6: http://entityframework.codeplex.com/wikipage?title=Roadmap

Вы можете попробовать голосовать за него здесь: http://data.uservoice.com/forums/72025-entity-framework-feature-suggestions/suggestions/1050579-unique-constraint-i-e-candidate-key-support

+0

Я никогда не использовал репозиторий или фабрику - какие-либо рекомендации по ссылкам/учебнику? Предполагаю, что я также могу «вручную» проверить ввод данных для новых мест, чтобы убедиться, что URL уникален? Может ли это быть применено на уровне объекта, если проверка неверна - например, установка уникального ограничения в SQL Server (возможно, ваше предложение охватывает это?) Кажется, что это может быть лучшим решением, так как это обычное требование? – niico

+0

Примечание. Это относительно небольшое приложение с одним разработчиком на данный момент, поэтому простое/быстрое решение было бы хорошим. Мне также необходимо обеспечить уникальное поле для пользователей - скажем, в их ручке Twitter или электронной почте. – niico

+0

@ пользователь2254951. Редактировать с помощью кода и предоставленных ссылок. Вам понадобится обычное место для проверки уникальности, поэтому мое предложение singleton, так как оно будет жить в памяти на протяжении всего вашего приложения в IIS, вам просто нужно его засеять при первом запросе, но это достаточно просто. –

3

Вы можете выполнить эту проверку на уровне кода перед сохранением данных в таблицах базы данных.

Вы можете попытаться использовать аннотацию данных Remote на вашей модели просмотра, чтобы выполнить асинхронную проверку, чтобы сделать пользовательский интерфейс более отзывчивым.

public class CreatePlaceVM 
{ 
    [Required] 
    public string PlaceName { set;get;} 

    [Required] 
    [Remote("IsExist", "Place", ErrorMessage = "URL exist!") 
    public virtual string URL { get; set; } 
} 

Убедитесь, что метод IsExists действия в вашем Placecontroller, который принимает URL paramtere и проверить его againist вашей таблицы и возвращает истину или ложь.

В этом msdn link есть пример программы, показывающей, как реализовать атрибут Remote, чтобы выполнить мгновенную проверку.

Кроме того, если вы используете хранимую процедуру (По какой-то причине), вы можете сделать EXISTS галку перед INSERT запроса.

+0

Это выглядит здорово - мне нужно что-то реализовать, чтобы заставить его работать? Что делать, если другой пользователь выполняет один и тот же запрос одновременно - есть ли способ предотвратить дублирование на более глубоком/уровне базы данных, скажем? – niico

+0

Сделайте это дважды (один раз с указанным выше способом (чтобы указать пользователю изменить дублируемое значение) и второй в вашем методе действия HttpPost непосредственно перед тем, как вы сохраните (проверьте, есть ли у db, есть ли у вас предмет или нет). Если у вас есть какие-то проблемы с производительностью (я думаю, что у вас их нет) – Shyju

+0

Yeh, проверка должна быть в транзакции с кодом, который вставляет новый URL-адрес, чтобы не было условий гонки. транзакция, есть ?, вставить, совершить – AaronLS

6

Эта ссылка может помочь: https://github.com/fatihBulbul/UniqueAttribute

[Table("TestModels")] 
public class TestModel 
{ 

    [Key] 
    public int Id { get; set; } 

    [Display(Name = "Some", Description = "desc")] 
    [Unique(ErrorMessage = "This already exist !!")] 
    public string SomeThing { get; set; } 
} 
1

Я решил общую задачу включения инъекции конструктора в вашем потоке Validation, интегрируя в механизм нормальной DataAnnotations, не прибегая к формам в this answer, что позволяет одному написать:

class MyModel 
{ 
    ... 
    [Required, StringLength(42)] 
    [ValidatorService(typeof(MyDiDependentValidator), ErrorMessage = "It's simply unacceptable")] 
    public string MyProperty { get; set; } 
    .... 
} 

public class MyDiDependentValidator : Validator<MyModel> 
{ 
    readonly IUnitOfWork _iLoveWrappingStuff; 

    public MyDiDependentValidator(IUnitOfWork iLoveWrappingStuff) 
    { 
     _iLoveWrappingStuff = iLoveWrappingStuff; 
    } 

    protected override bool IsValid(MyModel instance, object value) 
    { 
     var attempted = (string)value; 
     return _iLoveWrappingStuff.SaysCanHazCheez(instance, attempted); 
    } 
} 

С некоторыми вспомогательными классами (смотрите там), вы подключаете его u p. в ASP.NET MVC, например, в Global.asax: -

DataAnnotationsModelValidatorProvider.RegisterAdapterFactory(
    typeof(ValidatorServiceAttribute), 
    (metadata, context, attribute) => 
     new DataAnnotationsModelValidatorEx(metadata, context, attribute, true)); 
Смежные вопросы