通用MVC控制器无法为EF FindBy函数提供delagate

时间:2017-02-15 21:17:54

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

我正在为一个配置应用程序包装一个通用的SQL表编辑器,使用Entity Framework,一个Repository模式,依此类推。层上可能有点矫枉过正。在任何情况下,我都有一个通用的MVC控制器(我们称之为MyController),希望将其调用发送到通用服务层(MyServices),其中“T”的所有内容都是直接表示SQL表的一些数据模型类。每个数据模型类都有一个名为“Id”的ident字段,它是从一个基本模型(它本身正在实现一个接口)实现的。

一切都非常简单和流畅,除非我需要在数据集上调用FindBy选项,我必须提供一个委托来搜索泛型类型的东西。我知道类型约束需要附加一个接口,以便我可以访问任何类型T中的“Id”字段,但这样做会导致与显式实现通用控制器的任何控制器发生冲突。所以:

通用服务层:

public abstract class MyServices<T> : IMyService<T> where T : class,  new()
{
    IMyRepository<T> _MyRepository;

    public MyServices(IMyRepository<T> MyRepository)
    {
        _MyRepository = MyRepository;
    }
    public IQueryable<T> FindBy(System.Linq.Expressions.Expression<Func<T, bool>> predicate)
    {
       return _MyRepository.FindBy(predicate);
    }
}

通用存储库:

public partial class MyRepository<T> : IMyRepository<T> where T : class, new() 
{
     MyTablesEntities _entities = new MyTablesEntities();

     public IQueryable<T> FindBy(System.Linq.Expressions.Expression<Func<T, bool>> predicate)
    {

        IQueryable<T> query = _entities.Set<T>().Where(predicate);
        return query;
    }
}

通用控制器:

public class MyController<T> : Controller where T : class, new()
{
    private string ViewTitle = typeof(T).ToString();
    private readonly IMyServices<T> _MyServices = default(IMyServices<T>);

    public MyController() { }

    public MyController(IMyServices<T> mtt)
    {
        _MyServices = mtt;
    }

 public ActionResult Edit(int? id) 
    {
        if (id == null)
        {
            return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
        }

        T editItem = _MyServices.FindBy(c => **c.Id == id**).SingleOrDefault(); 

        if (editItem == null)
        {
            return HttpNotFound();
        }

        return PartialView(editItem);
    }
}

和使用MyController的Controller:

 public class AccountsController : MTTablesController<Accounts>
{  }

显然,使用此设置,FindBy委托子句无法区分T的类型直到运行时,因此无法以这种方式编译,给出:

  

'T'不包含'Id'的定义,也没有扩展方法'Id'可以找到'T'类型的第一个参数

但是,如果我将包含“Id”属性的接口(IMyTableEntity)添加到通用控制器的约束中:

public class MyController<T> : Controller where T : class, MyTables.DataAccess.Metadata.Base.IMyTableEntity, new()

public interface IMyTableEntity
{
    int Id { get; set; }
}

我现在在执行控制器上得到了这个错误:

  

类型'MyTables.DataAccess.Account'不能在泛型类型或方法'MyController'中用作类型参数'T'。没有从“MyTables.DataAccess.Account”到“MyTables.DataAccess.Metadata.Base.IMyTableEntity”的隐式引用转换。

我陷入了僵局,因为这两个错误都让我重新尝试了另一种方式,这两种方式都没有效果。我需要通用控制器上的通用“T”足够智能,随身携带Id字段,而不会混淆上面的实现控制器。

1 个答案:

答案 0 :(得分:0)

trailmax可能已找到它......

  

您的班级'MyTables.DataAccess.Account'必须实现IMyTableEntity

我的数据建模以这种方式设置:

  1. EF基础部分类:

    public partial class Accounts  {
       public int Id { get; set; }
       public string Code { get; set; }
       public string Description { get; set; }
    }
    
  2. 包含数据注释的元数据模型:

    public class AccountsMetadata : MyTables.DataAccess.Metadata.Base.MyTableEntity {
    [Key]
    [Required]
    [UniqueValidator]        
    public string Code { get; set; }
    
    [Display(Name="Description")]
    [StringLength(100)]
    public string Description { get; set; }
    }
    
  3. 将他们联系在一起的引导程序

    [MetadataType(typeof(AccountsMetadata))]
      public partial class Accounts { }
    
  4. 我需要做的是将接口的实现添加到每个部分的bootstrap定义中,即:

    [MetadataType(typeof(AccountsMetadata))]
    public partial class Accounts : MyTables.DataAccess.Metadata.Base.IMyTableEntity { }
    

    然后通过将IMyTableEntity接口设置为通用控制器中T的约束来跟进,如上所述......