.NET MVC 5 分成不同的项目:避免循环引用

.NET MVC 5 separate into different projects: avoid circular reference

我有一个解决方案只包含一个项目(MVC 5 with Identity and EF). 数据库上下文是一个 ApplicationDbContext(IdentityDbContext 的子class,它本身是 EF DbContext 的子class)。 我还有一个自定义验证属性,需要使用数据库上下文来执行它的操作:

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 项目分成几个项目:

我对此一窍不通,因为我一直把所有东西都放在同一个 MVC 项目中,所以我很困惑: 我认为该属性应该驻留在 Common 项目中,以便可以从 DAL(装饰模型)和 MVC(装饰视图模型)中引用它。

由于 Common 项目将从所有其他项目中引用,我想我不能从 Common 中引用任何这些项目(循环引用?)。并且,由于 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;

但这没有用...

解决它的一种方法是使用上下文为您的代码创建一个抽象 - IRoleFinder 并将公共项目与您的 RequiredIfRol 一起放入。然后在业务逻辑层实现,inject在MVC项目中实现。这样你应该能够将你的属性与上下文分离。

ValidationAttribute.IsValid() 有一个 ValidationContext 参数,您可以使用该参数通过 ValidationContext.GetService 方法解决依赖关系。

UPD

根据您在评论中的要求:

For simplification, lets say I have only 2 projects (the class library with the attribute, and the MVC project with everything else).

在图书馆你会有这样的东西:

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
    }
}

并且在 Startup 中(这是针对 ASP.NET Core,对于 MVC 5 你应该找到另一种方法):

   services.AddTransient<IRoleFinder, RoleFinder>()