简体   繁体   中英

C# .Net Core Utilize generic property in base controller class utilizing DbSet<TEntity>.FromSql

We have a group of controllers that return JSON data for to sync entities via a Web API. Each data entity has its own controller and data model ( "Order, Item, Customer, ..." ) So, this essentially has every controller using the exact same code. The only difference is name of the controller/web api path, and the data model that will be returned via JSON. What we would like to do is have one base controller (BaseController.cs) that all other controllers implement/extend. To do this, we want to have a virtual "EntityType" or "EntityName" property in the base class that each child controller class defines. Then, our code to return the data does not have to be repeated within every controller. Our problem is we can't figure out how to store/pass/define a "type" or "TEntity" variable. And we need this to execute the main part of our "Get" api method:

Example:

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

We implement GetEntityModelName() in the BaseController.cs class like this:

public virtual string GetEntityName() { return null; }

and an example of a child controller class would have the following:

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

How can we do this for a comparable GetEntityModelClass() that could pass the value that dbContext.Set() call needs?

In case it helps, here is a larger sample of our code:

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

    }
}

I am not a fan of the generic base controller. You can however make the controller generic, taking the desired entity type as the type argument. that should solve your problem.

Using your sample controller

[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);
    }
}

Derived controllers will then simply provide the type argument.

[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,
        }
    }
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM