首页 > 解决方案 > EF Core Generic Context.Find 用于 CRUD(创建/检索/更新/删除)视图

问题描述

考虑以下情况,其中控制器是 generic<T, U> where T : DbContext where U : class, new()

注意:这适用于当您有许多表时的场景,比如说在您正在搭建的某些现有数据库中,25+ 甚至高达 100+,并且您希望在后端有一个管理 CRUD 视图以用于列出/添加/更新/deleting 为每个表记录相同的记录,不需要更新,因为在运行像 Scaffold-DbContext 这样的工具时,DbContext 模型中的数据库架构会发生变化。

    [Route("Get/{id}")]
    public async Task<IActionResult> Get(string id)
    {
        var entityType = Context.Model.GetEntityTypes().Where(x => x.ClrType.Name == typeof(U).Name).FirstOrDefault();
        var keyProperty = entityType.FindPrimaryKey().Properties.ToArray()[0];
        var key = Convert.ChangeType(id, keyProperty.PropertyInfo.PropertyType);
        var result = Context.Find(typeof(U), new object[] { key });
        return new JsonResult(result);
    }

如果需要,您如何使其一般支持主键中的多个字段?有没有更清洁的方法来完成上述操作?

更新,将 ChangeType 更改为 TypeDescriptor,为 Find 添加了多个键:

    [Route("Get/{id}")]
    public async Task<IActionResult> Get(string id)
    {
        string[] keysStr = id.Split(',');
        var entityType = Context.Model.GetEntityTypes().Where(x => x.ClrType.Name == typeof(U).Name).FirstOrDefault();
        var keyProperties = entityType.FindPrimaryKey().Properties.ToArray();
        List<dynamic> keys = new List<object>();
        if (keysStr.Length != keyProperties.Length)
        {
            throw new ArgumentException("Keys must be comma-separated and must be of the same number as the number of keys in the table.");
        }
        for(int i=0;i<keyProperties.Length;i++)
        {
            //var key = Convert.ChangeType(id, keyProperty.PropertyInfo.PropertyType);
            TypeConverter converter = TypeDescriptor.GetConverter(keyProperties[i].PropertyInfo.PropertyType);
            var key = converter.ConvertFromString(id);
            keys.Add(key);
        }
        var result = Context.Find(typeof(U), keys.ToArray());
        return new JsonResult(result);
    }

使用 string.Split(',') 分隔 Route 中的多个关键字段。更好的方法?

标签: c#asp.net-coreentity-framework-corecrud

解决方案


虽然看起来可行,但我建议不要使用这种方法。尤其成问题的是 CRUD 的“C”和“U”:你将如何检查类失效?例如,用户可能会提交 a Birthdayin the future 或 a StartDateafter EndDate,而您将无法阻止它,或者至少这会很棘手,尤其是在复杂的前提条件下,例如“状态 S 的父实体不得超过 N孩子们”。

如果您仍然想这样做,请考虑让客户端指定实体类型。

像这样的东西:

[ApiController]
[Route("any")]
public class JackOfAllTradesCOntroller : ControllerBase
{
    private readonly MyContext _ctx;

    public JackOfAllTradesCOntroller(MyContext ctx)
    {
        _ctx = ctx;
        // Add a random entity.
        // Customer configured with:
        // modelBuilder.Entity<Customer>().HasKey(new[] { "Id", "FirstName" });
        _ctx.Customers.Add(
            new Customer() {
                Id = 1,
                FirstName = Guid.NewGuid().ToString().Substring(1,6),
                LastName = Guid.NewGuid().ToString(),
                Address = "Addr"
            });
        _ctx.SaveChanges();
    }

    private IEntityType GetType(string typeName)
    {
        return _ctx.Model.GetEntityTypes()
            .Where(x => x.ClrType.Name == typeName).First();
    }

    [Route("GetAll/{entityType}")]
    public IActionResult GetAll(string entityType)
    {
        IEntityType eType = GetType(entityType);
        MethodInfo set = _ctx.GetType().GetMethod("Set")
            .MakeGenericMethod(eType.ClrType);
        object result = set.Invoke(_ctx, new object[0]);
        return new JsonResult(result);
    }

    [Route("Get/{entityType}")]
    public IActionResult Get(
        string entityType,
        [FromQuery(Name = "id")] string[] id)
    {
        IEntityType eType = GetType(entityType);
        IProperty[] keyProperties = eType.FindPrimaryKey().Properties.ToArray();
        // ... validate id here ...
        object[] keys = id.Zip(
            keyProperties,
            (i, p) => Convert.ChangeType(i, p.PropertyInfo.PropertyType)
            ).ToArray();
        object result = _ctx.Find(eType.ClrType, keys);
        return new JsonResult(result);
    }
}

演示:

演示


推荐阅读