简体   繁体   English

如何指定类型参数,以便泛型基类将使用其调用的DAL方法所使用的相同模型类?

[英]How to specify a type parameter so that a generic base class will use the same model class that is used by the DAL method that it calls?

I am trying to create a generic base controller that would provide a single point of maintenance of the basic CRUD for a number of similar data base tables in my MVC EF application. 我试图创建一个通用的基本控制器,该控制器将为我的MVC EF应用程序中的许多相似数据库表提供基本CRUD的单点维护。 My design: 我的设计:

There is a base model class: 有一个基本模型类:

public class ModelBaseClass {}

Each database table has a model class. 每个数据库表都有一个模型类。 All model classes inherit ModelBaseClass: 所有模型类都继承ModelBaseClass:

public class ModelSomeDatabaseTable : ModelBaseClass {}

Each database table is associated with a data access (DAL) class. 每个数据库表都与一个数据访问(DAL)类相关联。 The class includes CRUD methods. 该类包括CRUD方法。 The DAL class uses the corresponding model class to pass data to the business layer: DAL类使用相应的模型类将数据传递到业务层:

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

There is a generic base controller with two type parameters: The first one is for the DAL class associated with the database table. 有一个具有两个类型参数的通用基本控制器:第一个用于与数据库表关联的DAL类。 The second one is for the database table's model class: 第二个是数据库表的模型类:

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

I have read what seemed to be relevant in the MSDN, as well as quite a number of blogs and responses to questions related to the use of type parameters. 我已经阅读了MSDN中似乎相关的内容,以及许多博客和对与使用类型参数有关的问题的回答。 In particular, I read several blogs describing the construction of generic base classes that provide a single point of maintenance for the basic CRUD logic. 特别是,我读了几篇博客,描述了通用基类的构造,这些基类为基本CRUD逻辑提供了单点维护。 Yet none of them seems to pass both type parameters, one for the DAL class of a certain table, and the other for the model class of the same table. 但是它们似乎都没有传递两个类型参数,一个传递给某个表的DAL类,另一个传递给同一表的模型类。

My problem is related to the second type parameter, the model class. 我的问题与第二类参数模型类有关。 The controller and the DAL class must use the same model when data is transferred between the two. 在两者之间传输数据时,控制器和DAL类必须使用相同的模型。 Of course, the identity of the model type will be revealed only at run time. 当然,仅在运行时才会显示模型类型的标识。 Still, the compiler needs the assurance that the same types are used by the controller and by the DAL, otherwise it raises a “ Cannot implicitly convert type '…<...ModelBaseClass>' to '…<...T2Model>' 尽管如此,编译器仍需要确保控制器和DAL使用相同的类型,否则会引发“ 无法将类型'... <... ModelBaseClass>'隐式转换为'... <... T2Model>'

BTW, when I replace the type parameter (T2Model) with the explicit name of the model base class (ModelBaseClass), then the compiler is happy. 顺便说一句,当我用模型基类(ModelBaseClass)的显式名称替换类型参数(T2Model)时,编译器很高兴。

In the generic base controller (ControllerBaseClass) below, T1Dal is constrained by the DalBaseClass. 在下面的通用基本控制器(ControllerBaseClass)中,T1Dal受DalBaseClass约束。 The DalBaseClass, in turn, has an abstract method, thus the compiler is happy when the generic base controller calls that method. DalBaseClass又具有一个抽象方法,因此,当通用基本控制器调用该方法时,编译器会感到满意。 Not only that, but also the generic base controller is aware of the inherited model's type, ModelBaseClass, as declared in the DalBaseClass. 不仅如此,通用基础控制器还知道继承的模型的类型ModelBaseClass,如DalBaseClass中声明的那样。 Here is the problem: T1Dal is constrained by the models' base class. 这是问题所在:T1Dal受模型的基类约束。 Therefore, I naively expected that here, again through inheritance, the compiler will appreciate the same model type. 因此,我天真地希望在这里,再次通过继承,编译器将欣赏相同的模型类型。 However, the compiler does not use this information in the assignment statement 但是,编译器不会在赋值语句中使用此信息

_List = _dal.ToList();

therefore, it raises the said cast error. 因此,它会引发上述转换错误。

How should I fix my code so that the generic base controller will recognize that the type parameter representing the model is the same type as the one used by the DAL? 我应该如何修正我的代码,以便通用基本控制器能够识别表示模型的类型参数与DAL使用的类型参数相同?

This is an extremely poor design and very inflexible. 这是一个非常糟糕的设计,而且非常不灵活。 The chief problem is that you're assuming each controller will only ever work with one entity type. 主要问题是您假设每个控制器只能使用一种实体类型。 That's actually not a real-world design and is pretty rare that you'd only need to work with just one thing in every controller. 那实际上不是真实世界的设计,而且非常罕见,您只需要在每个控制器中只处理一件事情即可。 It's like you're trying to create a repository, but it's not quite a repository. 就像您要创建存储库一样,但是它并不是一个存储库。 First, you should simply design your "DAL" class to be generic. 首先,您应该简单地将“ DAL”类设计为通用的。 EF actually has a generic DbSet accessor that you can utilize, which will make your work much easier. EF实际上具有可以使用的通用DbSet访问器,这将使您的工作更加轻松。 For example: 例如:

public class MyDalClass
{
    protected readonly DbContext context;

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

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

Then, throw away your generic controller and inject your DAL: 然后,扔掉您的通用控制器并注入DAL:

public class MyController : Controller
{
    protected readonly MyDalClass dal;

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

    ...
}

Then, in your actions, you can simply do something like: 然后,在操作中,您可以简单地执行以下操作:

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

To inject into your controller, you'll need to utilize a dependency injection container. 要注入到控制器中,您需要利用依赖注入容器。 There's many choices out there. 那里有很多选择。 My personal choice is Ninject, as I find it blends power and ease of use pretty well. 我个人选择的是Ninject,因为我发现它很好地融合了功能和易用性。 For example, with Ninject, you'd set this up like: 例如,使用Ninject,您可以将其设置为:

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

Obviously, with the scope of Stack Overflow, this is an extremely basic example. 显然,在堆栈溢出的范围内,这是一个非常基本的示例。 You probably utilize interfaces and create a much more robust DAL. 您可能会利用接口并创建更强大的DAL。 If you're interested, I have as eries of articles that present more advanced scenarios with a similar approach. 如果您有兴趣,我会在文章中以相似的方式介绍更高级的方案。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM