【问题标题】:How to convert datarow to generic model class?如何将数据行转换为通用模型类?
【发布时间】:2020-12-14 09:14:26
【问题描述】:

我有很多模型类,我用 API 中的数据填充它们。
API 数据的结构是 DataRow。
对于每个类,都有从 DataRow 到模型类的隐式转换。 我必须为每个模型类编写一组函数。其中一个函数,从数据库中获取,对于所有模型类都是通用的,所以我决定为此编写具有泛型类型的函数。

这是一个模型类的例子:

public class Person
{
    public long Id { get; set; }
    public string FirstName { get; set; }
    public string LastName { get; set; }

    public static implicit operator Person(DataRow dataRow)
    {
        if (dataRow != null)
        {
            return new Person
            {
                Id = long.Parse((string)dataRow["Id"]),
                FirstName = (string)dataRow["FirstName"],
                LastName = (string)dataRow["LastName"]
            };
        };
        return null;
    }
}

具有用于强制转换的泛型类型的类示例,如下所示:

public class DefaultRepository<T>
{        
    public T CreateInstanceOfModelFromDataRow(DataRow dataRow)
    {
        T modelInstance = (T)Activator.CreateInstance(typeof(T));

        modelInstance = (T)dataRow;
        
        return modelInstance;
    }     
}

测试代码如下所示:

// for testing I will create DataTable and DataRow and populate it fake data            
DataTable fakeTable = new DataTable();

fakeTable.Columns.Add("Id");
fakeTable.Columns.Add("FirstName");
fakeTable.Columns.Add("LastName");

DataRow fakeRow = fakeTable.NewRow();
        
fakeRow["Id"] = "1";
fakeRow["FirstName"] = "John";
fakeRow["LastName"] = "Doe";

// Cast DataRow to Pesron without error
Person fakePerson01 = (Person)fakeRow;

// Cant cast on this way create error while compiling
DefaultRepository<Person> defRepo = new DefaultRepository<Person>();
Person fakePerson02 = defRepo.CreateInstanceOfModelFromDataRow(fakeRow);

modelInstance = (T)dataRow; 行编译“无法将类型'System.Data.DataRow' 转换为'T'”时出现错误

我也尝试使用(T)Convert.ChangeType(dataRow, typeof(T));,但没有运气。

是否可以进行这种转换?

【问题讨论】:

  • 您正在重建 EF Core 或 Dapper。为什么?除了必须处理 DataTable 的缺点之外,您一无所获。使用 Dapper,所有这些代码都将替换为 conn.Query&lt;Person&gt;("select * from Persons where LastName=@name.",new {name="Doe"});
  • 另一方面,如果你使用 EF Core,你甚至不需要存储库,你会得到一个开箱即用的工作单元参数化SQL 生成。
  • 感谢@PanagiotisKanavos 的建议,但我不能使用任何 ORM,因为没有直接访问数据库的权限。
  • 感谢@WimOmbelets,但我无法使用该解决方案,因为来自 DataRow(s) 和模型类的字段名称冲突太多。如果我要使用这个解决方案,我需要为每个模型类创建映射,并且已经编写了转换。

标签: c# generics datarow


【解决方案1】:

由于您使用的是泛型,编译器不知道T 是什么类型,因此您会收到编译时错误。

您需要为通用实现添加约束 -

public class DefaultRepository<T> where T : Person
{
        public T CreateInstanceOfModelFromDataRow(DataRow dataRow) 
        {
            T modelInstance = (T)Activator.CreateInstance(typeof(T));

            modelInstance = (T)dataRow;

            return modelInstance;
        }
}

您也不需要通过反射创建实例。您可以将 dataRow 投射到您的班级 -

    public class DefaultRepository<T> where T : Person
    {
        public T CreateInstanceOfModelFromDataRow(DataRow dataRow) 
        {
            var modelInstance = (T)dataRow;

            return modelInstance;
        }
    }

【讨论】:

  • 这意味着编译器将我限制为只有一个类,并且我不能使用多个模型类,如 Person、Student... 因为使用约束一次只能定义一个类。我对吗?感谢@NikhilPatil 的解释。
  • 是的,但是您可以对接口或抽象类进行约束,并使您的类从它们派生或实现。问题是在这个“抽象”定义中定义一个隐式运算符。我试着去做,但我没有工作。如果有帮助,请参考 - stackoverflow.com/questions/32519131/…stackoverflow.com/questions/6946412/…
  • 非常感谢 Nikhil 的帮助和努力! :)
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2019-07-20
  • 2022-08-08
  • 2020-10-22
  • 2014-10-09
  • 2016-06-25
  • 1970-01-01
  • 2010-11-26
相关资源
最近更新 更多