【问题标题】:T must be a non-abstract type with a public parameterless constructor in order to use it as parameter 'TModel' in the generic type or methodT 必须是具有公共无参数构造函数的非抽象类型,以便在泛型类型或方法中将其用作参数“TModel”
【发布时间】:2016-01-20 14:23:39
【问题描述】:

我已经尝试搜索 SO 来寻找答案并偶然发现了类似的问题,但我无法使用它们来解决我的问题,所以请尽量不要将其标记为重复。让我们继续进行真正的交易:

我有一个用于标准化实体框架数据库优先模型的通用库。 这些是我创建的通用类:

public abstract class GenericLookupModel : IActive, ICreated, IModified, IIdentity, IStringValue
{
    public bool is_active { get; set; }
    public string value { get; set; }
    public string description { get; set; }
    public DateTime created_on { get; set; }
    public string created_by { get; set; }
    public DateTime modified_on { get; set; }
    public string modified_by { get; set; }
    public int id {get;set;}

    public void SetCreated(string creator = "SYSTEM")
    {
        created_by = creator;
        created_on = DateTime.Now;
    }

    public void SetModified(string modifier = "SYSTEM")
    {
        modified_by = modifier;
        modified_on = DateTime.Now;
    }
}

还有一个具有预设 MVC 属性的 ViewModel 类

public abstract class GenericLookupViewModel
{
    [Key]
    public int ID { get; set; }

    [Required]
    [StringLength(300)]
    public string Name { get; set; }

    [StringLength(4000)]
    public string Description { get; set; }

    [Required]
    public bool Active { get; set; }

    [StringLength(50)]
    [DisplayName("Record last modified by")]
    public string ModifiedBy { get; set; }

    [DisplayName("Record last modified Date")]
    [DataType(DataType.Date)]
    [DisplayFormat(DataFormatString = "{0:dd/MM/yyyy}", ApplyFormatInEditMode = true)]
    public DateTime ModifiedOn { get; set; }

    [StringLength(50)]
    [DisplayName("Record created by")]
    public string CreatedBy { get; set; }

    [DisplayName("Record creation Date")]
    [DataType(DataType.Date)]
    [DisplayFormat(DataFormatString = "{0:dd/MM/yyyy}", ApplyFormatInEditMode = true)]
    public DateTime CreatedOn { get; set; }
}

另外,我创建了一个服务类,打算在控制器内部使用它来获取数据:

public abstract class GenericLookupModelDataService<TModel, TViewModel> : object 
    where TModel : GenericLookupModel, new()
    where TViewModel : GenericLookupViewModel, new()
{
    private readonly DbContext _db;

    private DbContext entities
    {
        get { return _db; }
    }

    public GenericLookupModelDataService()
    {
        _db =
            new DbContext(
                System.Configuration.ConfigurationManager.ConnectionStrings["DefaultConnectionString"].ConnectionString);
    }

    public virtual IEnumerable<TViewModel> ReadAllActive()
    {
        return entities.Set<TModel>().Where(x => x.is_active).Select(product => new TViewModel
        {
            ID = product.id,
            Active = product.is_active,
            Description = product.description,
            Name = product.value,
            CreatedBy = product.created_by,
            CreatedOn = product.created_on,
            ModifiedBy = product.modified_by,
            ModifiedOn = product.modified_on
        });
    }

    public virtual IEnumerable<TViewModel> Read()
    {
        return entities.Set<TModel>().Select(product => new TViewModel
        {
            ID = product.id,
            Active = product.is_active,
            Description = product.description,
            Name = product.value,
            CreatedBy = product.created_by,
            CreatedOn = product.created_on,
            ModifiedBy = product.modified_by,
            ModifiedOn = product.modified_on
        });
    }

    public virtual void Create(TViewModel product, string username = "SYSTEM")
    {
        var entity = new TModel
        {
            is_active = product.Active,
            description = product.Description,
            value = product.Name,
        };

        entity.SetCreated();
        entity.SetModified();

        _db.Set<TModel>().Add(entity);
        _db.SaveChanges();
    }

    public virtual void Update(TViewModel product, string username = "SYSTEM")
    {
        var entity = new TModel
        {
            id = product.ID,
            is_active = product.Active,
            description = product.Description,
            value = product.Name
        };
        entity.SetModified();


        _db.Set<TModel>().Attach(entity);
        entities.Entry(entity).State = EntityState.Modified;
        entities.SaveChanges();
    }

    public virtual void Destroy(TViewModel product)
    {
        var entity = new TModel {id = product.ID};

        entities.Set<TModel>().Attach(entity);
        entities.Set<TModel>().Remove(entity);
        entities.SaveChanges();
    }

    public virtual TViewModel GetByID(int ID)
    {
        var item = entities.Set<TModel>().Find(ID);
        var result = new TViewModel
        {
            ID = item.id,
            Active = item.is_active,
            CreatedBy = item.created_by,
            CreatedOn = item.created_on,
            Description = item.description,
            ModifiedBy = item.modified_by,
            ModifiedOn = item.modified_on,
            Name = item.value
        };
        return result;
    }

    public void Dispose()
    {
        entities.Dispose();
    }

}

该库编译良好,我在 MVC 应用程序的数据层项目中使用它。首先创建一个新的视图模型:

public class RoleViewModel : GenericLookupViewModel
{


}

然后,让我们创建一个服务:

public class RoleService : GenericLookupModelDataService<tblkp_Role, RoleViewModel> 
{

}

使实体框架类继承抽象模型:

partial class tblkp_Role : GenericLookupModel
{

}

最后让我们创建我们的控制器:

public class EmployeeController : Controller
{
    private RoleService roleService;

    public EmployeeController()
    {
        dataService = new EmployeeService();
        PopulateLookups();
    }

    private void PopulateLookups()
    {
        roleService = new RoleService();
        ViewData["roles"] = roleService.ReadAllActive();
    }

    public ActionResult Index()
    {
        return View();
    }

}

对不起,代码墙,为简洁起见,一些代码已被删除。 编译时给了我3个错误:

更新:提供由 EF 自动生成的 tblk_Role 类(DB First 方法):

using System;
using System.Collections.Generic;

public partial class tblkp_Role
{
    [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2214:DoNotCallOverridableMethodsInConstructors")]
    public tblkp_Role()
    {
        this.tbl_Employee = new HashSet<tbl_Employee>();
    }

    public int id { get; set; }
    public string value { get; set; }
    public string desciption { get; set; }
    public bool is_active { get; set; }
    public System.DateTime created_on { get; set; }
    public string created_by { get; set; }
    public System.DateTime modified_on { get; set; }
    public string modified_by { get; set; }

    [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2227:CollectionPropertiesShouldBeReadOnly")]
    public virtual ICollection<tbl_Employee> tbl_Employee { get; set; }
}

更新 2:纯文本格式的错误:

错误 33 'DataLayer.Model.tblkp_Role' 必须是非抽象类型 使用公共无参数构造函数,以便将其用作 泛型类型或方法中的参数“TModel” 'MyLib.Model.GenericLookupModelDataService' c:\Projects\Sources\MyLib\bin\Release\MyLib.dll

错误 32 类型“DataLayer.Model.tblkp_Role”不能用作类型 泛型类型或方法中的参数“TModel” 'MyLib.Model.GenericLookupModelDataService'。那里 没有从 'DataLayer.Model.tblkp_Role' 到的装箱转换 'MyLib.Model.GenericLookupModel'。 c:\Projects\Sources\MyLib\bin\Release\MyLib.dll

【问题讨论】:

  • 您的一个错误表明您需要添加参考。你做到了吗?
  • 解释一下:在你的控制器中,你使用的是RoleService,它直接继承自GenericLookupModelDataService&lt;tblkp_Role, RoleViewModel&gt;。这种设计的结果是使用RoleService 的任何其他类(例如您的EmployeeController)也必须直接了解继承树中直接公开的所有内容。因此,您的控制器也需要知道tblkp_Role
  • 是的,我做到了。我的解决方案中的所有 3 个项目中都存在库引用。似乎控制器知道 tblkp_Role 没有任何问题
  • 您只显示了tblkp_Role 的部分定义。它是非抽象的吗?它有一个公共的无参数构造函数吗?
  • 用该外观修复更新了帖子。它在更新 2 中

标签: c# asp.net-mvc entity-framework generics abstract-class


【解决方案1】:

你有以下:

public abstract class GenericLookupModelDataService<TModel, TViewModel> : object 
    where TModel : GenericLookupModel, new()
    where TViewModel : GenericLookupViewModel, new()
{
  // ...

该类有两个通用参数,称为TModelTViewModel。其中每一个都有约束,在where 上下文关键字之后表示。

对于TModel,约束是:

  • 一个基类约束,要求类 GenericLookupModel 必须是任何类型的基类,以替换 TModel,并且
  • 构造函数约束new() 要求用于TModel 的类型必须公开一个采用零参数的public 实例构造函数。

您询问的错误之一是:

错误 33 'DataLayer.Model.tblkp_Role' 必须是非抽象类型 使用公共无参数构造函数,以便将其用作 泛型类型或方法中的参数“TModel” 'MyLib.Model.GenericLookupModelDataService'

这仅仅意味着您尝试用于TModel 的类型tblkp_Role 不符合构造函数约束。你有零参数的构造函数吗?

您询问的另一个错误是:

错误 32 类型“DataLayer.Model.tblkp_Role”不能用作 泛型类型或方法中的类型参数“TModel” 'MyLib.Model.GenericLookupModelDataService'。那里 没有从 'DataLayer.Model.tblkp_Role' 到的装箱转换 'MyLib.Model.GenericLookupModel'。

这表示不满足基类约束。由于错误文本谈到“装箱转换”,看来编译器正在使用的类型tblkp_Role 实际上是一个值类型(struct 类型或enum 类型)。像这样的类型永远不能像约束要求的那样从GenericLookupModel 派生。

必须是 C# 编译器使用的类型 tblkp_Role 与您使用 partial class tblkp_Role : GenericLookupModel 定义的类型不同。您可能有一些冲突的名称或引用项目中的一些重复代码/名称。

在您的编译时错误的映像版本中,我们看到编译器还抱怨您使用的类型 tblkp_Role 是在您没有引用的程序集中声明的。尝试先修复那个。一旦编译器可以看到tblkp_Role 的所有详细信息,也许其他的就会消失,因为它具有对定义该类型的项目的引用。

【讨论】:

    【解决方案2】:

    当您尝试在不同的类中使用相同的泛型类型参数而没有在其中至少一个类中定义所有约束时,通常会遇到您提到的错误。请参阅this Jon Skeet 的回答以了解清楚。

    但是您在这里只在一个类中使用 TModel,即 GenericLookupModelDataService,因此我尝试了以下方法:

    我将您的所有代码都写在同一个代码文件中,这意味着没有外部库。像这样:

    class Program
    {
        static void Main(string[] args)
        {
            RoleService roleService = new RoleService();
        }
    }
    
    class RoleService : GenericLookupModelDataService<tblkp_Role, RoleViewModel> 
    { }
    
    public abstract class GenericLookupModelDataService<TModel, TViewModel> : object
        where TModel : GenericLookupModel, new()
        where TViewModel : GenericLookupViewModel, new()
    { }
    
    public abstract class GenericLookupViewModel { }
    
    public abstract class GenericLookupModel { }
    
    public class RoleViewModel : GenericLookupViewModel { }
    
    public partial class tblkp_Role : GenericLookupModel 
    {
    }
    
    public partial class tblkp_Role
    {
        public tblkp_Role()
        {
    
        }
    }
    

    这编译成功。 因此我怀疑编译器不知道 tblkp_Role 的完整定义。

    我建议重新构建库并再次重新引用它(同时检查引用路径以确保您没有错误地引用旧版本)。

    当我尝试定义元数据类时,EF 在 DB first 方法中自动创建的部分类也遇到了类似的问题。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2014-05-18
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-03-04
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多