2

从数据库中检索数据后,我发现自己这样做是为了从 a DataRow(在本例中为 DVD)中的数据创建域对象:

DataRow drDvd = myDataTable.Rows[0];
Dvd myDvd = new Dvd();
myDvd.id = drDvd.Field<long>("id");
myDvd.title = drDvd.Field<string>("title");
myDvd.description = drDvd.Field<string>("description");
myDvd.releaseDate = drDvd.Field<DateTime>("releaseDate");

当然,我很快就感觉到,我在伪代码中一遍又一遍地这样做:

myDvd.field = drDvd.Field<field.type>(field.name);

而且我想知道是否可以将其放入循环中,但是我以前从未使用过反射。我试过的代码是这样的:

Dvd aDvd = new Dvd();
Type t = aDvd.GetType();
FieldInfo[] fields = t.GetFields();
foreach (FieldInfo fi in fields)
{
    fi.SetValue(aDvd, drDvd.Field<fi.FieldType>(fi.Name));
}

如您所知,问题是Field类方法的扩展DataRow不接受变量,需要显式填写。

我对 C# 没有那么丰富的经验,所以我想提出以下两个问题:

  1. 这是我想要做的好习惯吗?
  2. 如何填写正确的扩展Field<extension>(name)
4

4 回答 4

1

您需要获取泛型方法的方法信息,然后调用它。通过这种方式,您可以以编程方式将泛型类型传递给它。我在我的手机上,但它应该看起来像这样:

MethodInfo mField = typeof(Dvd).GetMethod("Field");
MethodInfo genericMethod = mField.MakeGenericMethod(new Type[] { fi.FieldType });

GenericMethod.Invoke(aDvd,new Object[]{fi.Name});
于 2012-04-28T13:42:42.190 回答
1

在没有必要时使用反射通常是一种不好的做法。因为反射方法是在运行时而不是编译时检查的,所以错误的代码更难跟踪,因为编译器无法检查错误。

如果我是您,请查看实体框架,因为您基本上是将数据库数据映射到域对象。http://msdn.microsoft.com/en-us/library/aa697427%28v=vs.80%29.aspx

于 2012-04-28T13:59:06.783 回答
0

这是构建和填充域对象的一种方式

    DataRow drDvd = new DataRow();
    Dvd aDvd = new Dvd();
    Type type = typeof(Dvd);
    foreach (FieldInfo fi in type.GetFields())
    {
        fi.SetValue(aDvd, drDvd[fi.Name]);
    } 

您使用 DataRow.Field 的方法可能是四舍五入。在您的情况下,它不适用。

或者,您可以考虑在您的应用程序中使用其中一种实体框架(NHibernate、Microsoft EF 等)。

于 2012-04-28T14:00:15.530 回答
0

我会做一个自定义属性。在执行属性时,您会遇到与数据库相同的字段名称。我目前在我当前的应用程序中使用它并且效果很好。它与 Entity SQL 非常相似。

public class SqlMetaAttribute : Attribute
{
    public string ColumnName { get; set; }
}

然后你有你的课是这样的

public class Person
{
    [SqlMeta(ColumnName = "First_Name")]
    publice string FirstName { get; set; }

    [SqlMeta(ColumnName = "Last_Name")]
    publice string LastName { get; set; }
}

然后,您将拥有一个具有相同功能的辅助类。在这种情况下,我假设外部调用者正在遍历数据表。使用模板 T 使其具有通用性使其真正可重用。而不是仅仅拥有一个“DVD”类型的实现并为另一个进行应对和粘贴。

public static T CreateObjectFromRow<T>(DataRow row)
{
    var newObject = new T();

    if (row != null) SetAllProperties(row, newObject);

    return newObject;
}

public static void SetAllProperties<T>(DataRow row, T newObject)
{
    var properties = typeof(T).GetProperties();

    foreach(var propertyInfo in properties)
    {
        SetPropertyValue(row, newObject, propertyInfo);
    }
}
public static void SetPropertyValue(DataRow row, T newObject, PropertyInfo propertyInfo)
{
    var columnAttribute = propertyInfo.FindAttribute<SqlMetaAttribute>();

    if (columnAttribute == null) return;

    // If the row type is different than the object type and exception will be thrown, but that is
    // okay because if that happens you have to fix your object you are using, or might need some
    // more custom code to help you with that.
    propertyInfo.SetValue(newObject, row.GetValue<object>(columnAttribute.ColumnName), null);
}

// Extension method for row.GetValue<object> used above
public static T GetValue<T>(this DataRow row, string columnName)
{
    if (row.ColumnNameNotFound(columnName) || row.Table.Columns[columnName] == null || row[columnName] is DBNull)
    {
        return default(T);
    }

    return (T)row[columnName];
}
于 2012-04-28T14:09:01.783 回答