T должен быть не абстрактным типом с открытым конструктором без параметров, чтобы использовать его в качестве параметра "TModel" в родовом типе или методе

Я попытался найти 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;
    }
}

И класс для ViewModel с предварительно установленными атрибутами MVC

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

}

Сделать класс 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 ошибки: введите описание изображения здесь

ОБНОВЛЕНИЕ. Предоставлен класс tblk_Role, автоматически созданный EF (первый подход DB):

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: Erros в текстовом формате:

Ошибка 33 'DataLayer.Model.tblkp_Role' должен быть не абстрактным типом с открытым конструктором без параметров, чтобы использовать его как Параметр "TModel" в общем типе или методе 'MyLib.Model.GenericLookupModelDataService < TModel, TViewModel > ' C:\Проекты\Sources\MyLib\Bin\Release\MyLib.dll

Ошибка 32 Тип "DataLayer.Model.tblkp_Role" не может использоваться как тип Параметр "TModel" в общем типе или методе 'MyLib.Model.GenericLookupModelDataService < TModel, TViewModel > '. Там не является конверсией бокса из "DataLayer.Model.tblkp_Role" в 'MyLib.Model.GenericLookupModel. C:\Проекты\Sources\MyLib\Bin\Release\MyLib.dll

Ответ 1

У вас есть следующее:

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

Этот класс имеет два общих параметра, называемых TModel и TViewModel. Каждый из них имеет ограничения, указанные после контекстного ключевого слова where.

Для TModel существуют следующие ограничения:

  • ограничение базового класса, требующее, чтобы класс GenericLookupModel должен был быть базовым классом того, что когда-либо был заменен на TModel, и
  • ограничение конструктора new(), требующее, чтобы тип, используемый для TModel, должен выставлять конструктор экземпляра public, который принимает нулевые аргументы.

Одна из ошибок, о которых вы спрашиваете:

Ошибка 33 'DataLayer.Model.tblkp_Role' должен быть не абстрактным типом с открытым конструктором без параметров, чтобы использовать его как Параметр "TModel" в общем типе или методе 'MyLib.Model.GenericLookupModelDataService < TModel, TViewModel > '

Это просто означает, что тип tblkp_Role, который вы пытаетесь использовать для TModel, не соответствует ограничению конструктора. У вас есть конструктор с 0 параметрами?

Еще одна ошибка, о которой вы спрашиваете:

Ошибка 32 Тип "DataLayer.Model.tblkp_Role" не может использоваться как введите параметр "TModel" в общий тип или метод 'MyLib.Model.GenericLookupModelDataService < TModel, TViewModel > '. Там не является конверсией бокса из "DataLayer.Model.tblkp_Role" в 'MyLib.Model.GenericLookupModel'.

Это означает, что ограничение базового класса не выполняется. Поскольку текст ошибки говорит о "преобразовании бокса", похоже, что тип tblkp_Role, который использует компилятор, на самом деле является типом значения (тип struct или enum). Подобные типы никогда не могут быть получены из GenericLookupModel по мере необходимости.

Должно быть, тип 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, особенно когда я пытался определить классы метаданных.