目前,我使用的是这样的东西:
try
{
dr = SQL.Execute(sql);
if(dr != null) {
while(dr.Read()) {
CustomObject c = new CustomObject();
c.Key = dr[0].ToString();
c.Value = dr[1].ToString();
c.Meta = dr[2].ToString();
customerInfo.CustomerList.Add(c);
}
}
else
{
customerInfo.ErrorDetails="No records found";
}
有没有办法直接进行这种映射(假设列名与字段名称匹配),而不是我手动进行分配。
然而,一个要求是我希望通过我当前使用sql查询的方法而不是使用基于纯LINQ的方法来实现这一点。首先,SQL查询足够大,涉及复杂的JOIN并且已经过彻底测试,因此我不想在此时引入更多错误。有什么建议吗?
答案 0 :(得分:6)
一个简单的解决方案是为CustomObject
制作一个DataRow
的构造函数(来自示例,如果它是另一个类,请纠正我)。
在你的新构造函数中,按照你自己的例子做。
public CustomObject(DataRow row)
{
Key = row[0].ToString();
// And so on...
}
另一种方法是引入泛型,并在SQL类中创建一个新函数
示例(从Passing arguments to C# generic new() of templated type获取代码):
// This function should reside in your SQL-class.
public IEnumerable<T> ExecuteObject<T>(string sql)
{
List<T> items = new List<T>();
var data = ExecuteDataTable(sql); // You probably need to build a ExecuteDataTable for your SQL-class.
foreach(var row in data.Rows)
{
T item = (T)Activator.CreateInstance(typeof(T), row);
items.Add(item);
}
return items;
}
使用示例:
public IEnumerable<CustomObject> GetCustomObjects()
{
return SQL.ExecuteObject<CustomObject>("SELECT * FROM CustomObject");
}
我已经在LinqPad中测试了这段代码,它应该可以工作。
答案 1 :(得分:5)
您可以通过为您的需求创建通用方法来实现。您也可以将新方法作为数据表的扩展名。
public static List<T> ToList<T>(this DataTable table) where T : class, new()
{
try
{
List<T> list = new List<T>();
foreach (var row in table.AsEnumerable())
{
T obj = new T();
foreach (var prop in obj.GetType().GetProperties())
{
try
{
PropertyInfo propertyInfo = obj.GetType().GetProperty(prop.Name);
propertyInfo.SetValue(obj, Convert.ChangeType(row[prop.Name], propertyInfo.PropertyType), null);
}
catch
{
continue;
}
}
list.Add(obj);
}
return list;
}
catch
{
return null;
}
}
}
用法:
DataTable dtCustomer = GetCustomers();
List<CustomObject> CustomObjectList = dtCustomer.ToList<CustomObject>();
答案 2 :(得分:4)
你应该研究一下MicroORMs。与提供必须使用的SDL的常规ORM不同,MicroORM允许您使用自己的SQL查询,并且仅提供从SQL结果集到C#对象以及从C#对象到SQL参数的映射。
我最喜欢的是PetaPoco,它还提供了一个查询构建器,它使用您自己的SQL,但对参数编号进行了一些巧妙的操作。
答案 3 :(得分:3)
假设:如果您只需要对象进行序列化或简单的临时输出。
您可以像这样使用ExpandoObject
和SqlDataReader.GetSchemaTable()
:
private IEnumerable<dynamic> ReaderToAnonymmous(SqlCommand comm) {
using (var reader = comm.ExecuteReader()) {
var schemaTable = reader.GetSchemaTable();
List<string> colnames = new List<string>();
foreach (DataRow row in schemaTable.Rows) {
colnames.Add(row["ColumnName"].ToString());
}
while (reader.Read()) {
var data = new ExpandoObject() as IDictionary<string, Object>;
foreach (string colname in colnames) {
var val = reader[colname];
data.Add(colname, Convert.IsDBNull(val) ? null : val);
}
yield return (ExpandoObject)data;
}
}
}
虽然发布了更快的解决方案(我将其作为ad-hoc SQL / Reader结果/输出的替代惰性方法发布)。
答案 4 :(得分:1)
以下函数接受SQL字符串和对象,它要求对象具有select语句中每列的属性。必须实例化对象。
//Have your setup(Eclipse,Android SDK, ioswebkit proxy, Appium) installed in MAC for instance 10.10
//create an instance with your thread to generalize for both android and ios
public LaunchAppiumServer(){
public synchronized void startiOSWebKitProxy(String UDID,String debugProxy,String platform){
if(Settings.getProperty("testbed").equals("localbrowser") && !Settings.getProperty("LOCAL_SETTINGS").contains("Emulator")){
if(platform.equals("ios")){
//Webkit Proxy command
CommandLine iOSProxyCommand = new CommandLine("ios_webkit_debug_proxy");
iOSProxyCommand.addArgument("-c");
iOSProxyCommand.addArgument(UDID+":"+debugProxy);
iOSProxyCommand.addArgument("-F");
DefaultExecuteResultHandler iOSProxyresultHandler = new DefaultExecuteResultHandler();
DefaultExecutor iOSProxyexecutor = new DefaultExecutor();
iOSProxyexecutor.setExitValue(1);
try {
iOSProxyexecutor.execute(iOSProxyCommand, iOSProxyresultHandler);
Thread.sleep(5000);
System.out.println("iOS Proxy started in :"+ deviceName);
} catch (IOException e) {
e.printStackTrace();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
public synchronized void startAppiumServer(String UDID,String port,String platform,String debugProxy) throws Exception{
CommandLine command = new CommandLine("appium");
command.addArgument("--udid");
command.addArgument(UDID);
command.addArgument("--address", false);
command.addArgument("127.0.0.1");
command.addArgument("--port", false);
command.addArgument(port);
command.addArgument("--webkit-debug-proxy-port");
command.addArgument(debugProxy);
command.addArgument("--tmp");
command.addArgument("/tmp/appium-"+port);
command.addArgument("--full-reset", true);
command.addArgument("--log-level", false);
command.addArgument("error");
command.addArgument("--log", false);
Timestamp currentTimestamp = new java.sql.Timestamp(Calendar.getInstance().getTime().getTime());
command.addArgument("/Users/sethu/Documents/appiumLogs/appium"+currentTimestamp.toString().replace(" ", "")+".log");
DefaultExecuteResultHandler resultHandler = new DefaultExecuteResultHandler();
executor.setExitValue(1);
try {
executor.execute(command, resultHandler);
} catch (IOException e) {
System.out.println("Unable to Launch Appium Server: ");
e.printStackTrace();
} catch (InterruptedException e) {
System.out.println("Unable to Launch Appium Server: ");
e.printStackTrace();
}
}
}
答案 5 :(得分:1)
@user1553525的答案很好,但是,如果您的列名与属性名不完全匹配,则无法使用。
因此,首先您要创建一个自定义属性。然后,在您要反序列化的类中使用该属性,最后,您要反序列化DataTable。
我们创建一个自定义属性,该属性将应用于类内部的属性。我们创建具有该属性Name
的类,稍后将使用该属性从DataTable中获取正确的列。
[AttributeUsage(AttributeTargets.Property, Inherited = false)]
public class MySqlColName : Attribute
{
private string _name = "";
public string Name { get => _name; set => _name = value; }
public MySqlColName(string name)
{
_name = name;
}
}
接下来,在我们要填充的类中,我们将使用刚刚创建的属性[MySqlColName]
声明将链接到类中属性的列名。
但是,如果属性名称与数据库列相同,则我们不需要在属性中指定列名称,因为.ToList<>()
函数将使用属性名称中的列名称。
public class EventInfo
{
[MySqlColName("ID")]
public int EventID { get; set; }
//Notice there is no attribute on this property?
public string Name { get; set; }
[MySqlColName("State")]
public string State { get; set; }
[MySqlColName("Start_Date")]
public DateTime StartDate { get; set; }
[MySqlColName("End_Date")]
public DateTime EndDate { get; set; }
}
最后,我们通过添加检查以查看是否提供了我们的自定义属性来修改@user1553525的答案。如果是,则将列的名称设置为提供的名称,否则,我们使用属性名称(请参见try块内部的代码)。
public static List<T> ToList<T>(this DataTable table) where T : class, new()
{
try
{
List<T> list = new List<T>();
foreach (var row in table.AsEnumerable())
{
T obj = new T();
foreach (var prop in obj.GetType().GetProperties())
{
try
{
//Set the column name to be the name of the property
string ColumnName = prop.Name;
//Get a list of all of the attributes on the property
object[] attrs = prop.GetCustomAttributes(true);
foreach (object attr in attrs)
{
//Check if there is a custom property name
if (attr is MySqlColName colName)
{
//If the custom column name is specified overwrite property name
if (!colName.Name.IsNullOrWhiteSpace())
ColumnName = colName.Name;
}
}
PropertyInfo propertyInfo = obj.GetType().GetProperty(prop.Name);
//GET THE COLUMN NAME OFF THE ATTRIBUTE OR THE NAME OF THE PROPERTY
propertyInfo.SetValue(obj, Convert.ChangeType(row[ColumnName], propertyInfo.PropertyType), null);
}
catch
{
continue;
}
}
list.Add(obj);
}
return list;
}
catch
{
return null;
}
}//END METHOD
最后,我们可以调用.ToList<>()
方法并获取序列化对象的列表
List<EventInfo> CustomObjectList;
using (DataTable dtCustomer = GetDataTable("SELECT * FROM EventIndex"))
{
CustomObjectList = dtCustomer.ToList<EventInfo>();
}
侧面说明: :我有一些自定义方法
public static bool IsNullOrWhiteSpace(this string x)
{
return string.IsNullOrWhiteSpace(x);
}
public static DataTable GetDataTable(string Query)
{
MySqlConnection connection = new MySqlConnection("<Connection_String>");
try
{
DataTable data = new DataTable();
connection.Open();
using (MySqlCommand command = new MySqlCommand(Query, connection))
{
data.Load(command.ExecuteReader());
}
return data;
}
catch (Exception ex)
{
// handle exception here
Console.WriteLine(ex);
throw ex;
}
finally
{
connection.Close();
}
}
答案 6 :(得分:0)
搜索此答案时,我发现您可以使用Dapper库:https://dapper-tutorial.net/knowledge-base/44980945/querying-into-a-complex-object-with-dapper
您可以使用以下内容:
using (var connection = new SqlConnection(ConnectionString))
{
connection.Open();
IList<CustomObject> result = connection.Query<CustomObject>(sql, commandType: CommandType.Text).ToList();
}
答案 7 :(得分:0)
虽然这个问题一直存在,但我找不到一个干净的解决方案。为了我的目的,我想出了以下在我的情况下效果很好的方法。
using System.Dynamic;
private IEnumerable<ExpandoObject> GetQueryToList()
{
try
{
using (var conn = new SqlConnection(ConnectionString))
using (var cmd = new SqlCommand(MyQuery, conn))
{
var list = new List<ExpandoObject>();
conn.Open();
var reader = cmd.ExecuteReader();
while (reader.Read())
{
var expandoObject = new ExpandoObject();
for (var i = 0; i < reader.FieldCount; i++)
{
((IDictionary<string, object>) expandoObject).Add(
reader.GetName(i), reader[i]);
}
list.Add(expandoObject);
}
reader.Close();
return list;
}
}
catch (Exception ex)
{
var m = MethodBase.GetCurrentMethod();
Console.WriteLine(ex.Message + " " + m.Name);
}
return null;
}