【问题标题】:Change method data type dynamically / at runtime动态/在运行时更改方法数据类型
【发布时间】:2020-03-05 12:34:40
【问题描述】:

我有一个包含 40 个表的 Sql DB,我构建了代表每个表的模型,我还使用 Dapper 作为我的 ORM...

我正在 DAL(数据访问层)中构建我的 CRUD 语句,它将采用 myModelObj(通过 HTTPPost 进入)和 modelName(通过 URL 参数进入)

我还有一个方法 (MyMapper) 可以将我的 myModelObj 映射到我的模型 - 但是我创建的方法是 object 类型,它返回 System.Object 类型 - 我希望发生的事情是方法返回模型并动态/在运行时转换调用方法的数据类型,以匹配返回的模型类型...

我的代码:

控制器

private readonly IDbOperation _dbo;
...
...

public DataController(IDbOperation dboperation)
{
      _dbo = dboperation;
}

...

[HttpPost("create/{modelName}", Name = "Generic CRUD: Create Method")]
[Produces("application/json")]
public ActionResult Create([FromBody] JObject createObject, string modelName)
{
  try
  {
      ... ... ...

      object response = _dbo.CreateRecord(createObject, modelName);

// HTTP Post Body example
{
    "Fullname": "Juan Carlos",
    "Firstname": "Juan",
    "Lastname": "Carlos",
    "Salutation": "Dr.",
    "Age": 30,
    "CreatedOn": "2019-11-07T12:25:10"
}

数据库操作

private readonly IDbConnector _dbConnector
...

public DbOperation(IDbConnector dbConnector)
{
      _dbConnector = dbConnector;
}

public JsonResult CreateRecord(JObject model, string modelName)
{
      ... ... ...
      var create = MyMapper(modelName, model); // <<<
      ... ...
      try
      {
            using (var connection = _dbConnector.GetMsSqlConnection())
            {
                 // https://dapper-tutorial.net/insert
                 var createdId = connection.Insert(create); 
                 ...
            }
      }
}

private object MyMapper(string modelName, JObject mod)
{
      switch (modelName.ToLower())
      {
           case "transaction":
                return JsonConvert.DeserializeObject<ModelRepoDTO.Transaction>(model.ToString());

           case "items":
                return JsonConvert.DeserializeObject<ModelRepoDTO.Items>(model.ToString());

           case "users":
                return JsonConvert.DeserializeObject<ModelRepoDTO.Users>(mod.ToString());
      ...
      ...
           default:
                _logger.Error("DataAccessLayer", "DbOperation", ">>> Mapping decision could not be made");
                break;
    }
}

数据库连接器

public class DbConnector : IDbConnector
{
    private readonly string _connectionstring;

    public DbConnector(IConfiguration config)
    {
        _connectionstring = config.GetValue<string>("Connectionstring");
    }

    public SqlConnection GetMsSqlConnection()
    {
        SqlConnection conn = null;
        try
        {
            conn = new SqlConnection(_connectionstring);
            conn.Open();
            if (conn.State != ConnectionState.Open)
            {
                // throw error
            }
        }
        catch (Exception ex)
        {
            if (conn != null && conn.State == ConnectionState.Open)
            {
                conn.Close();
            }
            conn = null;
        }

        // return the connection
        return conn;
    }
}

(当前)从MyMapper("User", {&lt;Object from HTTP Post&gt;}) 返回的数据类型是System.Object,因为MyMapper 的类型是object

我想要的是动态更改数据类型以匹配模型...

MyMapper("User", {&lt;Object from HTTP Post&gt;}) => 数据类型 = User

请记住,我可以针对 40 个不同的表/模型进行 CRUD 处理,显然在运行时之前我不会知道调用了哪个表/模型...

(完成示例) ...我把从MyMapper 返回的对象传递给connection.insert(...); (我修改了我的帖子以显示这一点 - 看看 DbOperation)

有什么建议吗?想法?

【问题讨论】:

  • MyMapper 的通用方法可能是最优雅的解决方案...在某些时候,您需要明确指定您期望的类型,所以您不会例如,能够输入一个 JSON 字符串并在没有某种映射的情况下神奇地获得预期的类型(可能由一个巨大的 switch 语句实现)
  • 有趣的是你提到...MyMapper 是一个巨大的switch 声明...我将更新我的帖子,其中一些只是为了说明
  • 明智的设计,感觉就像你试图让一个方法处理许多不同的类型,但就像object。总之——从长远来看,这将是痛苦的。要么更改映射器以返回泛型,要么(我建议)制作 40 种不同的操作方法(端点)。当然,会有一些代码重复,但很容易阅读和推理。
  • @user1538301 如果需要,OP 甚至可以保留其现有的 URL 结构。将create/bob 路由到一个操作,create/cathy 到另一个操作等等。
  • 更改 MyMapper 以使用泛型 - 因此它将采用 T 类型而不是 string 模型名称。当您尝试这样做时,您将意识到“40 个不同端点”方法的好处(因为您需要能够在编译时传入泛型类型)。是的,这将是一些工作。 :)

标签: c# asp.net-core


【解决方案1】:

如果我理解您的问题,您正在尝试调用第三方 (Dapper) 泛型方法,而无需预知泛型类型参数,通常必须在编译时提供。

这可以是accomplished via Reflection。在您的包装类(我相信您称之为“DbConnector”)中添加一个名为“InsertDynamicObject”的新方法,并将其编码为使用反射来查找您希望调用的方法定义。然后,您可以调用 MakeGenericMethod,传递泛型类型参数。一个示例可能如下所示:

namespace Dapper  //This is a stub for the Dapper framework that you cannot change
{
    class DbConnection
    {
        public void Insert<T>(T obj)
        {
            Console.WriteLine("Inserting an object with type {0}", typeof(T).FullName);
        }
    }
}

namespace MyProgram
{
    class DbConnector  //Here is your DbConnector class, which wraps Dapper
    {
        protected readonly Dapper.DbConnection _connection = new Dapper.DbConnection();

        public void InsertDynamicObject(object obj)
        {
            typeof(Dapper.DbConnection)
                .GetMethod("Insert")
                .MakeGenericMethod(new [] { obj.GetType() })
                .Invoke(_connection, new[] { obj });
        }
    }

    public class Program
    {
        public static void Main()
        {
            object someObject = "Test";  //This is the object that you deserialized where you don't know the type at compile time.

            var connector = new DbConnector();
            connector.InsertDynamicObject(someObject);
        }
    }
}

输出:

Inserting an object with type System.String

这是 DotNetFiddle 上一个工作示例的链接:Link

【讨论】:

  • 非常感谢!!!这是我一直在寻找的,但是我认为在 .net 中使用 Reflection 非常昂贵
猜你喜欢
  • 2022-01-02
  • 2012-01-20
  • 1970-01-01
  • 1970-01-01
  • 2014-06-02
  • 2018-02-14
  • 2019-10-21
  • 2014-03-05
  • 1970-01-01
相关资源
最近更新 更多