T必须是具有公共无参数构造函数的非抽象类型,以便将其用作参数' TModel'在泛型类型或方法中

时间:2016-01-20 14:23:40

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

我已经尝试过搜索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 App里面的数据层项目中使用它。首先创建一个新的视图模型:

public class RoleViewModel : GenericLookupViewModel
{


}

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

public class RoleService : GenericLookupModelDataService<tblkp_Role, RoleViewModel> 
{

}

使Entity Framework类继承自抽象模型:

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个错误: enter image description here

UPDATE :提供由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&#39; DataLayer.Model.tblkp_Role&#39;必须是非抽象类型   使用公共无参数构造函数以将其用作   参数&#39; TModel&#39;在泛型类型或方法中   &#39;&MyLib.Model.GenericLookupModelDataService LT; TModel的,TViewModel&GT;&#39; C:\项目\源头\ MyLib中\ BIN \发布\中是指mylib.dll

     

错误32类型&#39; DataLayer.Model.tblkp_Role&#39;不能用作类型   参数&#39; TModel&#39;在泛型类型或方法中   &#39;&MyLib.Model.GenericLookupModelDataService LT; TModel的,TViewModel&GT;&#39 ;.那里   是没有拳击转换来自&#39; DataLayer.Model.tblkp_Role&#39;至   &#39; MyLib.Model.GenericLookupModel&#39 ;. C:\项目\源头\ MyLib中\ BIN \发布\中是指mylib.dll

2 个答案:

答案 0 :(得分:21)

您有以下内容:

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

该类有两个名为TModelTViewModel的通用参数。其中每个都有 约束 ,在where上下文关键字后面显示。

对于TModel,约束是:

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

您提出的错误之一是:

  

错误33&#39; DataLayer.Model.tblkp_Role&#39;必须是非抽象类型   使用公共无参数构造函数以将其用作   参数&#39; TModel&#39;在泛型类型或方法中   &#39;&MyLib.Model.GenericLookupModelDataService LT; TModel的,TViewModel&GT;&#39;

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

您提出的另一个错误是:

  

错误32类型&#39; DataLayer.Model.tblkp_Role&#39;不能用作   类型参数&#39; TModel&#39;在泛型类型或方法中   &#39;&MyLib.Model.GenericLookupModelDataService LT; TModel的,TViewModel&GT;&#39 ;.那里   是没有拳击转换来自&#39; DataLayer.Model.tblkp_Role&#39;至   &#39; MyLib.Model.GenericLookupModel&#39;

这表示不满足基类约束。由于错误文本讨论&#34;装箱转换&#34;,看起来编译器正在使用的类型tblkp_Role实际上是值类型(struct类型,或{{1 })。类似的类型永远不能从约束所需的enum派生出来。

必须是C#编译器使用的类型GenericLookupModel是另一种类型,而不是您使用tblkp_Role定义的类型。您可能在引用的项目中有一些冲突的名称或一些重复的代码/名称。

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

答案 1 :(得分:3)

当您尝试在不同的类中使用相同的泛型类型参数而不在其中至少一个中定义所有约束时,通常会遇到您提到的错误。有关明确性,请参阅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的完整定义。

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

我遇到了部分类的类似问题,这些问题是由我在数据库第一种方法中自动创建的,当我尝试定义元数据类时。