是否可以使用对类一无所知的类属性甚至是类型和名称?

问题描述

我想尝试不使用sql语句用类似的代码编写所有方法和类,而是遵循DRY原理。现在,我需要访问一个未知类的属性

public T Get(string pathToClass,string sqlStatement)
{
    sqlConnection connection = new sqlConnection();
    connection.ConnectionString = connectionString; //connectionString is field of class.
    connection.open();

    sqlCommand command = new sqlCommand(sqlStatement);

    sqlDataReader reader = command.ExecuteReader();

    List<Type> types = new Reflect(pathToClass).GetTypes();   //Reflection is my class library
    List<string> names = new Reflect(pathToClass).GetNames(); //it only reads the classes and returns properties's names and types.

    object entity = new object(); //idk is it right or not,but seems it is.
    for(int i = 0; reader.Read(); i++)
    {
        entity.someProperty = types[i].Parse(reader[names[i]].ToString()); //There is a wrong point,where I'm confused
    }

    connection.Close();

    return (T)entity;
}

解决方法

如果不使用ORM,则通常的方法是使用静态工厂方法返回原始的DataRowIDataRecord对象,这些方法具有每种类型的静态工厂方法,并且知道如何构造实例的实例。输入IDataRecordDataRow输入该类型。

例如:

//Initial class might look like this:
public class Employee
{
    public string FirstName {get;set;}
    public string LastName {get;set;}
    public int ID {get;set;}
    public string Email {get;set;}

    public static Employee FromDataRow(DataRow row)
    {
        return new Employee
        {
            ID = row["ID"],FirstName = row["FirstName"],LastName = row["LastName"],Email = row["Email"]
        };
    }
}

//Data access might look like this:
public class DB
{
    private static string ConnectionString { get;} = "connection string here";
    public static IEnumerable<DataRow> FindEmployeeRecords(string LastName)
    {
        string sql = "SELECT ID,FirstName,LastName,Email FROM Employee WHERE LastName LIKE @LastName + '%'";
        using (var cn = new SqlConnection(ConnectionString))
        using (var cmd = new SqlCommand(sql,cn))
        using(var da = new SqlDataAdapter(cmd))
        {
            cmd.Parameters.Add("@LastName",SqlDbType.NVarChar,80).Value = LastName;
            var result = new DataSet();
            da.Fill(result);
            return results.Tables[0].Rows;
        }
    }
}

// and then you could use it like this:
var employees = DB.FindEmpoyeeRecords("Smith").Select(Employee.FromDataRow);
foreach (var employee in employees) 
{
   //...
}
// or you could bind employees to a datasource

当然,您可以使用reflection将DataRow字段映射到对象。您甚至可以将其中的一部分抽象为实用程序方法。但是,您仍然需要每种类型的factory方法(仅需少量代码),反射速度 slow (确实很慢),并且您失去了映射方式的灵活性,如果您希望类字段与表列有所不同。

我们也可以reduce boilerplate code in the database layer。但是,这需要对函数式编程有更好的了解。