【问题标题】:C# .Net Core Utilize generic property in base controller class utilizing DbSet<TEntity>.FromSqlC# .Net Core 利用 DbSet<TEntity>.FromSql 的基本控制器类中的通用属性
【发布时间】:2018-06-29 07:36:39
【问题描述】:

我们有一组控制器返回 JSON 数据以通过 Web API 同步实体。每个数据实体都有自己的控制器和数据模型(“Order, Item, Customer, ...”) 因此,这基本上让每个控制器都使用完全相同的代码。唯一的区别是控制器/web api 路径的名称,以及将通过 JSON 返回的数据模型。 我们想要做的是拥有一个所有其他控制器实现/扩展的基本控制器(BaseController.cs)。为此,我们希望在每个子控制器类定义的基类中拥有一个虚拟的“EntityType”或“EntityName”属性。然后,我们返回数据的代码不必在每个控制器中重复。 我们的问题是我们无法弄清楚如何存储/传递/定义“类型”或“TEntity”变量。我们需要它来执行“Get” api 方法的主要部分:

例子:

var returnValue = dbContext.Set<GetEntityModelClass()>().FromSql("EXEC dbo.[AJ_P_{0}_API]", GetEntityModelName()).ToList();

我们在 BaseController.cs 类中实现 GetEntityModelName(),如下所示:

public virtual string GetEntityName() { return null; }

子控制器类的示例将具有以下内容:

public override string GetEntityName() { return "Product"; }

对于可以传递 dbContext.Set() 调用所需的值的类似 GetEntityModelClass(),我们如何做到这一点?

如果有帮助,这里有一个更大的代码示例:

BaseController.cs
-----------------------------------------
namespace API.Controllers
{
    [Produces("application/json")]
    public class BaseController : Controller
    {

        // Returns the name of an entity as it is used in stored procedures, class names, etc...
        public virtual string GetEntityName()
        {
            return null;
        }
        // Return a type that can be utilized with the dbContext.Set<???>() call
        public virtual ??? GetEntityName()
        {
            return ???;
        }
        // SiteContext is the API site's data context.
        protected readonly SiteContext dbContext;
        public BaseController(SiteContext context)
        {
            dbContext = context;
        }

        [HttpGet("{token}")]
        public IActionResult Get([FromRoute] string token)
        {
            return Get(token);
        }
        public IActionResult Results(string token)
        { 
            if (!ModelState.IsValid) { return BadRequest(ModelState); }

            var returnValue = dbContext.Set<???GetEntityModel()???>().FromSql("EXEC dbo.[AJ_P_{0}_API] @Token={1}", GetEntityName(), token).ToList();

            return Ok(returnValue);
        }
    }
}

ProductController.cs
-----------------------------------------

namespace API.Controllers
{
    [Produces("application/json")]
    [Route("/Product")]
    public class ProductController : BaseController
    {
        public override string GetEntityName() {
            return "Product";
        }
        public override ???? GetEntityModel() {
            return ( ??? ProductModel type ??? )
        }

        public ProductController(SiteContext context) : base(context) { }

    }
}

【问题讨论】:

  • 我不是通用基本控制器的粉丝。但是,您可以使控制器通用,将所需的实体类型作为类型参数。那应该可以解决您的问题。 public abstract class BaseController&lt;TEntity&gt; : Controller

标签: c# generics asp.net-core asp.net-core-2.0


【解决方案1】:

我不是通用基本控制器的粉丝。但是,您可以使控制器通用,将所需的实体类型作为类型参数。这应该可以解决您的问题。

使用您的示例控制器

[Produces("application/json")]
public abstract class BaseController<TEntity, TModel> : Controller
    where TEntity : class 
    where TModel : class {

    // Returns the name of an entity as it is used in stored procedures, class names, etc...
    protected virtual string GetEntityName() {
        return typeof(TEntity).Name;
    }

    // SiteContext is the API site's data context.
    protected readonly SiteContext dbContext;
    protected BaseController(SiteContext context) {
        dbContext = context;
    }

    [HttpGet("{token}")]
    public IActionResult Get([FromRoute] string token) {
        return GetInternal(token);
    }

    protected abstract TModel Map(TEntity entity);

    protected virtual IActionResult GetInternal(string token)  
        if (!ModelState.IsValid) { return BadRequest(ModelState); }
        var sql = string.Format("EXEC dbo.[AJ_P_{0}_API] @Token", GetEntityName());
        var entities = dbContext.Set<TEntity>().FromSql(sql, token).ToList();
        var returnValue = entities.Select(Map);
        return Ok(returnValue);
    }
}

然后派生控制器将简单地提供类型参数。

[Produces("application/json")]
[Route("[controller]")]
public class ProductController : BaseController<Product, ProductModel> {

    public ProductController(SiteContext context) : base(context) { }

    protected override ProductModel Map(Product entity) {
        return new ProductModel {
            Property1 = entity.Property1,
            //...
            PropertyN = entity.PropertyN,
        }
    }
}

【讨论】:

  • 我没有正确强调的问题是数据模型类中存在大量特定属性和 JSON 格式。因此,显示 dbContext.Set 的行确实必须有效地最终表现得像“dbContext.Set”,“dbContext.Set',......我无法弄清楚的棘手部分是如何用“ProductModel”、“CustomerModel”等动态替换“TEntity”... BaseController 类的通用定义会自动传递这个吗?
  • @GETools 你不能那样做。查看自动映射器。您从 EF 中获取实体,然后将它们映射到模型。
  • 将您提议的基础设施准备就绪,效果很好。
猜你喜欢
  • 2018-07-15
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-07-10
  • 1970-01-01
  • 1970-01-01
  • 2015-08-27
  • 2018-09-15
相关资源
最近更新 更多