如何指定一个类型参数,以便通用基类将使用它调用的DAL方法使用的相同模型类?

时间:2017-03-06 09:21:34

标签: c# asp.net-mvc generics

我正在尝试创建一个通用的基本控制器,它可以为我的MVC EF应用程序中的许多类似数据库表提供基本CRUD的单点维护。我的设计:

有一个基础模型类:

public class ModelBaseClass {}

每个数据库表都有一个模型类。所有模型类都继承ModelBaseClass:

public class ModelSomeDatabaseTable : ModelBaseClass {}

每个数据库表都与数据访问(DAL)类相关联。该课程包括CRUD方法。 DAL类使用相应的模型类将数据传递到业务层:

public abstract class DalBaseClass
{
    public abstract List<ModelBaseClass> ToList();
}

有一个带有两个类型参数的通用基本控制器:第一个用于与数据库表关联的DAL类。第二个是数据库表的模型类:

 public class ControllerBaseClass<T1Dal, T2Model> : Controller
    where T1Dal : DalBaseClass, new()
    where T2Model : ModelBaseClass
{
    T1Dal _dal = new T1Dal();

    public ControllerBaseClass() { }

    //----------------------------------------------------------------------------------------
    // GET: List all entries in the table
    //----------------------------------------------------------------------------------------
    public ActionResult Index()
    {
        List<T2Model> _List = new List<T2Model>();
        _List = _dal.ToList(); //Compiler error: "Cannot implicitly convert type ‘…<...ModelBaseClass>' to '…<...T2Model>'"
        return View(_List);
    }
}

我已经阅读了MSDN中似乎相关的内容,以及相关的许多博客以及与使用类型参数相关的问题的回复。特别是,我阅读了几篇描述通用基类构造的博客,这些基类为基本的CRUD逻辑提供单点维护。然而,它们似乎都没有传递两个类型参数,一个用于某个表的DAL类,另一个用于同一个表的模型类。

我的问题与第二个类型参数(模型类)有关。当数据在两者之间传输时,控制器和DAL类必须使用相同的模型。当然,模型类型的标识仅在运行时显示。仍然,编译器需要保证控制器和DAL使用相同的类型,否则它会引发“无法隐式转换类型”......&lt; ... ModelBaseClass&gt;'到'...&lt; ... T2Model&gt;'

BTW,当我用类型参数(T2Model)替换模型基类(ModelBaseClass)的显式名称时,编译器很高兴。

在下面的通用基本控制器(ControllerBaseClass)中,T1Dal受DalBaseClass约束。反过来,DalBaseClass有一个抽象方法,因此当通用基本控制器调用该方法时,编译器很高兴。不仅如此,通用基本控制器还知道继承的模型的类型ModelBaseClass,如DalBaseClass中声明的那样。问题在于:T1Dal受模型基类的约束。因此,我天真地期望在这里,再次通过继承,编译器将欣赏相同的模型类型。但是,编译器不在赋值语句

中使用此信息
_List = _dal.ToList();
因此,它引发了上述施法错误。

我应该如何修复我的代码,以便通用基本控制器能够识别出代表模型的类型参数与DAL使用的类型相同?

1 个答案:

答案 0 :(得分:0)

这是一个非常糟糕的设计,非常不灵活。主要问题是你假设每个控制器只能使用一种实体类型。这实际上并不是真实世界的设计,并且很少见,你只需要在每个控制器中只使用一件事。它就像你在尝试创建一个存储库一样,但它并不是一个存储库。首先,你应该简单地设计你的&#34; DAL&#34;类是通用的。 EF实际上有一个可以使用的通用DbSet访问器,这将使您的工作更容易。例如:

public class MyDalClass
{
    protected readonly DbContext context;

    public MyDalClass(DbContext context)
    {
        this.context = context;
    }

    public List<TEntity> GetAll<TEntity>()
    {
        return context.Set<TEntity>().ToList();
    }
}

然后,扔掉你的通用控制器并注入你的DAL:

public class MyController : Controller
{
    protected readonly MyDalClass dal;

    public MyController(MyDalClass dal)
    {
        this.dal = dal;
    }

    ...
}

然后,在您的行动中,您可以简单地执行以下操作:

var foos = dal.GetAll<Foo>();

要注入控制器,您需要使用依赖注入容器。那里有很多选择。我个人的选择是Ninject,因为我发现它很好地融合了功能和易用性。例如,使用Ninject,您可以将其设置为:

kernel.Bind<DbContext>().To<ApplicationDbContext>().InRequestScope();
kernel.Bind<MyDalClass>().ToSelf().InRequestScope();

显然,在Stack Overflow的范围内,这是基本示例。您可能利用接口并创建更强大的DAL。如果您对此感兴趣,我会使用eries of articles来提供类似方法的更高级方案。