#c# #asp.net-mvc-5
#c# #asp.net-mvc-5
Вопрос:
У меня есть решение, которое содержит только один проект (MVC 5 с идентификатором и EF). Контекст БД представляет собой ApplicationDbContext (подкласс IdentityDbContext, который сам по себе является подклассом EF DbContext). У меня также есть пользовательский атрибут проверки, который должен использовать контекст БД для выполнения своих задач:
public class RequiredIfRol : ValidationAttribute, IClientValidatable {
//...
protected override ValidationResult IsValid(object value, ValidationContext validationContext) {
// ...
string nomRol = "";
//instance and use the ApplicationDbContext
using (ApplicationDbContext db = new ApplicationDbContext()) {
var roldb = db.Roles.Find(rolId);
if (roldb == null) return ValidationResult.Success;
nomRol = db.Roles.Find(rolId).Name;
}
// more code here
}
}
Это работает нормально.
Теперь, прочитав об этом, я пытаюсь разделить этот проект MVC на несколько проектов:
- библиотека классов для DAL (она будет содержать модели EF и ApplicationDbContext),
- еще один для бизнес-логики,
- еще один с общими вещами
- а затем проект MVC.
Я новичок в этом, поскольку у меня всегда все было внутри одного и того же проекта MVC, поэтому я в замешательстве: я думаю, что атрибут должен находиться в общем проекте, чтобы на него можно было ссылаться из DAL (для украшения моделей), а также из MVC (для украшения viewmodels).
Поскольку на общий проект будут ссылаться все другие проекты, я думаю, я не могу ссылаться ни на один из этих проектов из общей (циклической ссылки?). и, поскольку ApplicationDbContext (который должен использовать атрибут) будет находиться в проекте DAL, у меня здесь проблема…
Я почти уверен, что я плохо проектирую это, но не могу найти правильный способ сделать это.. Любая помощь?
ДОБАВЛЕНО:
это то, что я пробовал:
1. — в общей библиотеке определен интерфейс:
public interface IMyInterface {
System.Data.Entity.IDbSet<CustomRole> Roles { get; set; }
}
2.- в ApplicationDbContext изменил его объявление, чтобы оно реализовывало интерфейс:
public class ApplicationDbContext : IdentityDbContext<ApplicationUser, CustomRole, int, CustomUserLogin, CustomUserRole, CustomUserClaim>, IMyInterface {...}
3.- затем в атрибуте попытайтесь получить реализацию интерфейса, добавив свойство getter:
private IMyInterface _dbcontext { get { return DependencyResolver.Current.GetService<IMyInterface>(); } }
а затем в методе isValid():
var roldb = _dbcontext.Roles.Find(rolId);
if (roldb == null) return ValidationResult.Success;
nomRol = _dbcontext.Roles.Find(rolId).Name;
но это не сработало…
Ответ №1:
Одним из способов обойти это было бы создать абстракцию для вашего кода с использованием контекста — IRoleFinder
и поместить в общий проект вместе с вашим RequiredIfRol
. Затем реализуйте его на уровне бизнес-логики и внедряйте его в проект MVC. Таким образом, вы сможете отделить свой атрибут от контекста.
ValidationAttribute.IsValid()
имеет ValidationContext
параметр, который вы можете использовать для разрешения зависимости с помощью ValidationContext.GetService
метода.
UPD
По вашему запросу в комментарии:
Для упрощения предположим, что у меня есть только 2 проекта (библиотека классов с атрибутом и проект MVC со всем остальным).
В библиотеке у вас будет что-то вроде этого:
interface IRoleFinder
{
CustomRole Find(int id);
}
public class RequiredIfRol : ValidationAttribute, IClientValidatable {
//...
protected override ValidationResult IsValid(object value, ValidationContext validationContext) {
// ...
var roleFinder = (IRoleFinder) validationContext.GetService(typeof(IRoleFinder));
}
}
В вашем проекте MVC:
public class RoleFinder : IRoleFinder
{
public CustomRole Find(int id)
{
// use context here
}
}
И при запуске (это для ASP.NET Ядро, для MVC 5 вы должны найти другой способ):
services.AddTransient<IRoleFinder, RoleFinder>()
Комментарии:
1. Это звучит многообещающе, но я не думаю, что я это полностью понимаю … не могли бы вы подробнее рассказать об этом с помощью какого-нибудь примера кода? (кстати, я забыл упомянуть, что я не использую какую-либо платформу DI) для упрощения, допустим, у меня есть только 2 проекта (библиотека классов с атрибутом и проект MVC со всем остальным). Я расширил вопрос, чтобы добавить то, что я пробовал…
2. @patsy2k, если вы используете стандартный ASP.NET Затем вы автоматически используете встроенный DI-контейнер.
3. @patsy2k там все еще должен быть доступен DI. Также вы можете сделать то же самое без DI, но с наследованием.
4. @patsy2k какую ошибку выдал вам ваш код? TBH Я работал с MVC 5 очень давно, и на моей машине он недоступен.
5. спасибо, я, наконец, заставил это работать, используя ваш ответ с некоторыми незначительными изменениями, а также установив Unity в проект в качестве DI framework.