C# 在数据访问层中,将数据解析为对象的最佳实践是什么?

C# 在数据访问层中,将数据解析为对象的最佳实践是什么?,c#,n-tier-architecture,C#,N Tier Architecture,我正在向使用n层体系结构的旧asp.net应用程序添加新功能。一个基本的例子如下 对象 public class Test { public int ID{get;set;} public int name{get;set;} } 数据访问层 public static List<Test> GetTests() { List<Test> list = new List<Test>(); try { /

我正在向使用n层体系结构的旧asp.net应用程序添加新功能。一个基本的例子如下

对象

public class Test
{
    public int ID{get;set;}
    public int name{get;set;}
}
数据访问层

public static List<Test> GetTests()
{
    List<Test> list = new List<Test>();
    try
    {
        //codes
        SqlDataReader dr  = com.ExecuteReader();
        while(dr.Read())
            list.Add(FillTestRecord(dr))
        //codes
    }
    catch{}
    return list;
}

private static Test FillTestRecord(IDataRecord dr)
{
    Test test = new Test();
    try{test.ID = Convert.ToInt32(dr["ID"]);}
    catch{}
    try{test.Name = Convert.ToInt32(dr["Name"]);}
    catch{}
    return test;
}
因此,
FillTestRecord
方法将

private static Test FillTestRecord(IDataRecord dr)
{
    Test test = new Test();

    int ordinal = default(int);

    if(dr.TryGetOrdinal("ID",out ordinal))
        test.ID = Convert.ToInt32(dr.GetValue(ordinal));
    if(dr.TryGetOrdinal("Name",out ordinal))
        test.Name = Convert.ToString(dr.GetValue(ordinal));

    return test;
}
对此,我们非常感谢您的任何建议

更新03-02-2016 在调试过程中,我发现
try-catch
DataRecord
中找不到提供的列名时,如果
GetOrdinal
抛出错误,则会严重影响性能。因此,我编写了一个新方法,在
DataReader
中获取列名,并用
Array.IndexOf
替换
GetOrdinal

public static bool TryGetOrdinal(this IDataRecord dr, string[] columnNames, string column, out int ordinal)
{
    ordinal = Array.IndexOf(columnNames, column);
    return ordinal >= 0;
}
我的
FillTestRecord
变成-

private static Test FillTestRecord(IDataRecord dr, string[] columnNames)
{
    Test test = new Test();

    int ordinal = default(int);

    if(dr.TryGetOrdinal(columnNames, "id",out ordinal))
        test.ID = Convert.ToInt32(dr.GetValue(ordinal));
    if(dr.TryGetOrdinal(columnNames, "name",out ordinal))
        test.Name = Convert.ToString(dr.GetValue(ordinal));

    return test;
}
列名被传递给Fill方法,如下所示-

using (var dr = com.ExecuteReader())
{
    string[] colNames = dr.GetColumnNames();
    while (dr.Read())
        list.Add(FillTestRecord(dr, colNames));
}
“GetColumnNames”是新的扩展方法-

public static string[] GetColumnNames(this IDataReader dr)
{
    string[] columnNames = new string[dr.FieldCount];
    for (int i = 0; i < dr.FieldCount; i++)
    {
        columnNames[i] = dr.GetName(i).ToLower();
    }
    return columnNames;
}
公共静态字符串[]GetColumnNames(此IDataReader dr)
{
string[]columnNames=新字符串[dr.FieldCount];
对于(int i=0;i
在我看来,您的方向是正确的。
只要解析是在一个被所有上层类重用的集中位置进行的,这看起来是一个很好的解决方案

我唯一要更改的是用检查列中是否存在数据替换
try-catch
语句。当然有办法判断(列不存在?DB Null值?) 您可以使用类似的


我使用以下代码映射不同对象的属性。它使用反射来获取源对象和目标对象的属性,但您可以轻松地将其更改为使用IDataRecord:

public static T MapDTO<T>(object dto) where T : new()
{
    T Result = new T();

    if (dto == null)
        return Result;

    dto.GetType().GetProperties().ToList().ForEach(p =>
        {
            PropertyInfo prop = Result.GetType().GetProperty(p.Name);

            if (prop != null && prop.CanWrite)
            {
                try
                {
                    var convertedVal = Convert.ChangeType(p.GetValue(dto, null), prop.PropertyType);
                    prop.SetValue(Result, convertedVal, null);
                }
                catch (Exception ex)
                {
                    try
                    {
                        prop.SetValue(Result, p.GetValue(dto, null), null);
                    }
                    catch (Exception ex1) { }
                }
            }
        });
    return Result;
}
publicstatictmapdto(objectdto),其中T:new()
{
T结果=新的T();
if(dto==null)
返回结果;
dto.GetType().GetProperties().ToList().ForEach(p=>
{
PropertyInfo prop=Result.GetType().GetProperty(p.Name);
if(prop!=null&&prop.CanWrite)
{
尝试
{
var convertedVal=Convert.ChangeType(p.GetValue(dto,null),prop.PropertyType);
prop.SetValue(结果,convertedVal,null);
}
捕获(例外情况除外)
{
尝试
{
prop.SetValue(结果,p.GetValue(dto,null),null);
}
catch(异常ex1){}
}
}
});
返回结果;
}

这里的关键是源属性和目标属性应该具有相同的名称。

下面是我为将IDataRecord映射到属性而编写的一些代码

    public static T ParseRecord<T>(this IDataRecord reader) where T : new()
    {
        var model = new T();
        var type = typeof(T);

        for (int i = 0; i < reader.FieldCount; i++) {
            var fieldType = reader.GetFieldType(i);
            var fieldName = reader.GetName(i);
            var val = reader.GetValue(i);
            var prop = type.GetProperty(fieldName);

            // handle or throw instead here if needed
            if (prop == null)
                continue;

            var propType = prop.PropertyType;

            // HACK: remove this if you don't want to coerce to strings
            if (propType == typeof(string))
                prop.SetValue(model, val.ToString());
            else if (fieldType != propType)
                throw new Exception($"Type mismatch field {fieldType} != prop {propType}");
            else
                prop.SetValue(model, val);
        }

        return model;
    }
public static T ParseRecord(此IDataRecord读取器),其中T:new()
{
var模型=新T();
var类型=类型(T);
对于(int i=0;i
您基本上是在尝试重新发明轮子。请改用实体框架。如果您正在捕获,请以有用的方式使用异常。空捕获是非常不受欢迎的。@HighCore,如果我使用实体框架,那么我必须进行大量重新编码,目前这不是一个可行的选择。我同意@DavidL的观点。空异常表明这不是一个真正的错误,应该以不同的方式处理。我建议检查-在行动之前,而不是行动,而不是请求原谅…@th1rdey3我发布的
HasColumn
方法如何?如果返回的数据记录中没有
“ID”
命名列,则dr[“ID”]将给出
索引超出范围错误
。因此,如果我遵循您的解决方案,它仍然会在
if(TryParseDataRow(dr[“ID”],out tempId))
处给出一个错误,我认为
(obj.GetType()!=typeof(T))
将始终返回true,因为
obj/record
不会是
typeof(T)
。另外,
record[coln]
如果record中不存在列,则会引发异常。是的,这就是当您有无法运行和测试的代码时得到的结果。。。任何方式的答案都被修改了。我尝试了其他方法,更新了我的问题,让我知道你们的想法。@th1rdey3看起来不错。在您的方案中,仍然会抛出异常,但您可以在本地处理它们。这比你最初提出的空捕获条款要好得多。在我的建议中,我完全避免了例外情况。另一方面,如果数据库中的数据类型将发生变化,比如从int变为decimal,那么您将得到一个异常,而不是一个空字段。这其实很好。。。
    /// <summary>
    /// Determines whether the specified record has column.
    /// </summary>
    /// <param name="record">The record.</param>
    /// <param name="columnName">Name of the column.</param>
    /// <returns>true if column exist, false otherwise</returns>
    public static bool HasColumn(this IDataRecord record, string columnName)
    {
        for (int i = 0; i < record.FieldCount; i++)
        {
            if (record.GetName(i).Equals(columnName, StringComparison.InvariantCultureIgnoreCase))
                return true;
        }
        return false;
    }
public static T MapDTO<T>(object dto) where T : new()
{
    T Result = new T();

    if (dto == null)
        return Result;

    dto.GetType().GetProperties().ToList().ForEach(p =>
        {
            PropertyInfo prop = Result.GetType().GetProperty(p.Name);

            if (prop != null && prop.CanWrite)
            {
                try
                {
                    var convertedVal = Convert.ChangeType(p.GetValue(dto, null), prop.PropertyType);
                    prop.SetValue(Result, convertedVal, null);
                }
                catch (Exception ex)
                {
                    try
                    {
                        prop.SetValue(Result, p.GetValue(dto, null), null);
                    }
                    catch (Exception ex1) { }
                }
            }
        });
    return Result;
}
    public static T ParseRecord<T>(this IDataRecord reader) where T : new()
    {
        var model = new T();
        var type = typeof(T);

        for (int i = 0; i < reader.FieldCount; i++) {
            var fieldType = reader.GetFieldType(i);
            var fieldName = reader.GetName(i);
            var val = reader.GetValue(i);
            var prop = type.GetProperty(fieldName);

            // handle or throw instead here if needed
            if (prop == null)
                continue;

            var propType = prop.PropertyType;

            // HACK: remove this if you don't want to coerce to strings
            if (propType == typeof(string))
                prop.SetValue(model, val.ToString());
            else if (fieldType != propType)
                throw new Exception($"Type mismatch field {fieldType} != prop {propType}");
            else
                prop.SetValue(model, val);
        }

        return model;
    }