ASP.NET MVC 4, EF5, уникальное свойство в модели - лучшая практика?
ASP.NET MVC 4, EF5, Первый код, SQL Server 2012 Express
Какова наилучшая практика для обеспечения уникальной ценности в модели? У меня есть класс мест, у которого есть свойство 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 вы каким-то образом можете указать базе данных установить уникальное ограничение для поля в базе данных?
Что является самым простым способом - и что лучше всего?
Ответы
Ответ 1
Как сумасшедший, как может показаться, лучшей практикой в настоящее время является не использовать встроенную проверку и вместо этого использовать 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
}
}
Ответ 2
Эта ссылка может помочь:
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; }
}
Ответ 3
Единственный способ - обновить миграцию после ее создания, предполагая, что вы используете их, чтобы он принудительно применял уникальное ограничение для столбца.
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");
}
Жесткий бит, тем не менее, принуждает ограничение на уровне кода до того, как вы попадете в базу данных. Для этого вам может понадобиться репозиторий, который содержит полный список уникальных значений и гарантирует, что новые объекты не будут нарушать это с помощью метода factory.
// 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 };
}
}
Литература:
Сноска:
В коде есть много запросов для [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
Ответ 4
Вы можете выполнить эту проверку на уровне кода перед сохранением данных в таблицах базы данных.
Вы можете попробовать использовать аннотацию данных 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
и проверяет его снова в вашей таблице и возвращает true или false.
В этой ссылке msdn есть пример программы, показывающей, как реализовать атрибут Remote для мгновенной проверки.
Кроме того, если вы используете хранимую процедуру (По какой-либо причине), вы можете выполнить проверку EXISTS
там перед запросом INSERT
.
Ответ 5
Я решил общую проблему включения инсталляции конструктора в вашем потоке проверки, интегрируя его в обычный механизм DataAnnotations, не прибегая к фреймворкам в этом ответе, позволяя писать:
class MyModel
{
...
[Required, StringLength(42)]
[ValidatorService(typeof(MyDiDependentValidator), ErrorMessage = "It 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);
}
}
С некоторыми вспомогательными классами (смотрите там), вы подключаете его, например. в ASP.NET MVC, как в Global.asax
: -
DataAnnotationsModelValidatorProvider.RegisterAdapterFactory(
typeof(ValidatorServiceAttribute),
(metadata, context, attribute) =>
new DataAnnotationsModelValidatorEx(metadata, context, attribute, true));