Две проверки IValidatableObject в одной сущности
Это суть проекта, при создании которого необходимо проверить, существует ли уже объект с таким именем. При редактировании необходимо, например, проверка, но имейте в виду, что старое и новое имя объекта могут совпадать. Вам также необходимо отобразить сообщение об ошибке. Для этого я использую интерфейс IValidatableObject, но не знаю, как сообщить методу Validate, что объект в данный момент редактируется или создается
3 ответа
Проблема решается с помощью метода ModelState.AddModelError (string, string) в действиях Edit и Create.
[HttpPost]
[HandleError(View="AjaxError")]
public ActionResult Edit(ProjectsViewData data)
{
if (ModelState.IsValid)
{
if (!ContainsProject(data.CurrentObject.Name))
{
db.Projects.Attach(data.CurrentObject);
db.ObjectStateManager.ChangeObjectState(data.CurrentObject, EntityState.Modified);
db.SaveChanges();
return Projects(data);
}
else
{
int projectId = (from p in db.Projects
where p.Name == data.CurrentObject.Name
select p.ProjectID).FirstOrDefault();
if (projectId == data.CurrentObject.ProjectID)
{
db.Projects.Attach(data.CurrentObject);
db.ObjectStateManager.ChangeObjectState(data.CurrentObject, EntityState.Modified);
db.SaveChanges();
return Projects(data);
}
else
{
ModelState.AddModelError("Name", Localizer.ProjectAlreadyExists);
}
}
}
data.ObjectToEdit = data.CurrentObject;
return Projects(data);
}
[HttpPost]
[HandleError(View = "AjaxError")]
public ActionResult Create(ProjectsViewData data)
{
if (ModelState.IsValid)
{
if (!ContainsProject(data.CurrentObject.Name))
{
db.Projects.AddObject(data.CurrentObject);
db.SaveChanges();
return Projects(data);
}
else
{
ModelState.AddModelError("Name", Localizer.ProjectAlreadyExists);
}
}
data.ObjectToAdd = data.CurrentObject;
return Projects(data);
}
Вспомогательный метод:
private bool ContainsProject(string projectName)
{
if (projectName != null)
{
projectName = Regex.Replace(projectName.Trim(), "\\s+", " ");
List<string> projects = new List<string>();
var projectNames = (from p in db.Projects
select p.Name.Trim()).ToList();
foreach (string p in projectNames)
{
projects.Add(Regex.Replace(p, "\\s+", " "));
}
if (projects.Contains(projectName))
{
return true;
}
else
{
return false;
}
}
else
{
return false;
}
}
Предполагая, что вы обращаетесь, чтобы проверить, EF не может сделать для вас.
Это на самом деле сложно проверить. Вы проверяете сущность после того, как она была добавлена в контекст. Он не должен проверять себя и должен учитывать другие элементы в контексте, которые еще не сохранены. Как и БД. Есть несколько 3 комбинаций плюс самопознание. Запишите запись объекта в LOCAL, когда ID пуст / новый, т.е. несколько новых вставок требуют тщательного кодирования. (Рассмотрите возможность использования временных идентификаторов)
еще не сохраненные записи должны быть в контексте
Context.Set<TPoco>().Local
и получить данные из БД и сохранить в списке временных. НО не помещайте в контекст. Или используйте ВТОРОЙ контекст.
var matchingSet = Context.Set<TPoco>().AsNoTracking() // not into context...
.Where(t=>t.field == somevalue).ToList();
Так что насчет логических и реальных дубликатов на БД. Логические дубликаты являются дубликатами в поле без уникального индекса, который с точки зрения бизнеса должен быть уникальным.
Если вы хотите проверить эти...
Вам нужно прочитать DB.... НО, если эти записи в настоящее время изменяются, вы НЕ МОЖЕТЕ просто поместить их в контекст. Вы бы переписали их. Но что, если значения значений логического ключа изменились? Что-то вызвало логическое дублирование записи в БД, которая может больше не быть дублированием после сохранения или наоборот. Это все еще дуп или нет?
Таким образом, вам нужно решить, как вы будете сопоставлять LOCAL с загруженными записями. Т.е. проверять LOCAL и соответствующие записи в БД и решать, что делать, если запись находится в обоих, только локальных или только в дБ. ЛОКАЛЬНО ТОЛЬКО и БД только легко.
Но в обоих случаях... Это решение вашего бизнес-процесса.
DbContext.ValidateEntity принимает IDictionary<Object, Object> items
как второй параметр. Вы можете передать любые данные там, и данные, которые вы передаете, будут переданы IValidatableObject.Validate
в ValidationContext.Items