【问题标题】:How to return SqlDataReader in C# WCF?如何在 C# WCF 中返回 SqlDataReader?
【发布时间】:2013-05-15 17:14:29
【问题描述】:

在我的 C# WCF 服务中,我有一个 SqlDataReader,其中包含我想返回给客户端的数据行。

如何返回SqlDataReader中的所有内容?

我现在有

if (sqlReader != null)
{
    if (sqlReader.HasRows)
    {
        while (sqlReader.Read())
        {
            return sqlReader[0].ToString();
        }
        sqlConn.Close();
    }
    else
    {
        return null;
    }
}

只返回第一个结果。类的返回类型目前是字符串。 我正在考虑类似数组中的数组,但我不确定如何?

编辑:

感谢您的许多回复。我有兴趣返回服务“创建”的整个 SQL 数据。第一列 ([0]) 未在线 - 这仅用于测试。

但我不确定如何将服务中的所有内容返回给客户端。

  • 如何退货?

例如。在 Powershell 中,如果我必须在客户端之间传递它,我会创建一个集合并将对象添加到该集合中。

我在 C# 和 WCF 中寻找类似的东西。

到目前为止非常感谢:)

编辑#2:

知道了! :)

创建了一个新类(例如):

public class ObjectNotification
{
    public string AlertDescription;
    public string Servername; 
}

在我顶部的 svc.cs 文件中:

List<ObjectNotification> objlist = new List<ObjectNotification>();

    if (sqlReader.HasRows)
    {
       while (sqlReader.Read())
       {
             ObjectNotification obj = new ObjectNotification();
             obj.AlertDescription = sqlReader["AlertDescription"].ToString();
             obj.Servername = sqlReader["ComputerName"].ToString();
             objlist.Add(obj);
       }
    }
    return objlist;

这正是我想要的:)

最好的问候

【问题讨论】:

  • 类没有返回类型。你说的是方法吗?你能把你使用SqlDataReader的整个方法贴出来吗?
  • 我认为这将是很多额外的工作,并且需要对您的代码设计进行大量更改(因此我不会将此作为答案发布)但真正的答案是您必须使用WCF Data Services,但是您使用它的方式与普通 WCF 非常不同。
  • 代码还有一个缺陷,第一次点击return语句时控件永远存在循环,实际上只返回第一行....
  • @user1281991 从我的角度来看一些提示:如果您只需要 2 个属性来捆绑,则可以使用结构而不是类。如果你想添加方法,一个类是有意义的。 “ObjectNotification”这个名字对于 imo 类来说听起来不太好。如果其他人想阅读您的代码,则类名应该一目了然。您创建了解决方案真是太好了。 :-)
  • 我还是更喜欢:DataTable dt=new DataTable(); dt.Load(数据读取器); List objList=(List) dt.ToList();一旦你得到了 dt.tolist ,你就有了一个可以一次又一次地重用的代码......如果你有一个具有 50 个属性的对象,那就太好了;)

标签: c# visual-studio-2010 wcf


【解决方案1】:

您需要定义 DataContract,如果您传递字符串列表或字符串数​​组,您的服务使用者需要知道哪个索引是哪个列等。当您将来向服务添加或删除列时,这种方法会很困难.您可以做的是创建 DataContract ,它具有您需要发送的所有属性并相应地创建操作合同。现在服务消费者可以在将来更新服务引用以防更改字段,他们会得到编译器错误。这很容易识别。

public List<MyDataContract>  GetData()
{
    List<MyDataContract> list = new List<MyDataContract>();
    //your code

    if (sqlReader != null)
    {
        if (sqlReader.HasRows)
        {
            while (sqlReader.Read())
            {
                list.Add(new MyDataContract() { 
                    Id = (int)sqlReader["Id"].ToString(), 
                    Name= sqlReader = sqlReader["Name"].ToString() });

            }
            sqlConn.Close();
        }
    }

    //finally return list of data

    return list;
}

示例数据合同

[DataContract]
public class MyDataContract
{
    [DataMember]
    public int Id{ get; set; }

    [DataMember]
    public string Name{ get; set; }
}

和运营合同

     [OperationContract]
     List<MyDataContract> GetData();

在我看来,我们需要更通用的可重用代码...

如果您只有 .net 服务使用者,您可以从服务方法返回 DaTaSet 或 DataTable。您需要有 SqlDataAdapter 而不是 sqlReader 并填充 DataTable 或 Dataset 并返回它。您可以在服务方法定义中不改变任何数量的列。您甚至可以使用DataSet.GetXml() 将返回类型作为字符串发送

将 DataTable 转换为 Json

string json = JsonConvert.SerializeObject(table, Formatting.Indented);

【讨论】:

  • 您假设对象具有 Id 和 Name 属性,我认为我们需要更通用的可重用代码...
  • @eloycm DataSet 或 DaTaTable 作为返回类型怎么样,你甚至不需要逐行阅读。
  • 你可以做到,但我更喜欢发送对象......对我来说,理想的情况是忘记肥皂、数据合同和所有这些东西,将整个东西转换为 json 并通过一个不错的宁静的服务;)
  • 我理解对逐行阅读的担忧,但我已经尝试过,效果很好。而且我的印象是 GetXml 实际上也会通​​过行读取......
  • @eloycm try Json.net 你可以将DataTable转换为Json
【解决方案2】:
// for instance
List<string> list = new List<string>();

if (sqlReader != null)
    {
        if (sqlReader.HasRows)
        {
            while (sqlReader.Read())
            {
                //return sqlReader[0].ToString();
                list.Add(sqlReader[0].ToString());
            }
            sqlConn.Close();
        }
        else
        {
            return null;
        }
    }
return list; // ta-da

【讨论】:

  • @ebyrob 是的 :-) 他会知道他的专栏,并且可以根据自己的要求对其进行修改 :-)
  • 这段代码不够通用,代码应该适用于与任意合约匹配的任何类型的列......
  • @eloycm huh .. 一种通用方法可能是遍历所有列(通过它们的名称)-> 旧参数“第一列”。 (在 2008 年已经这样做了 - 性能正在下降)随后,可以使用类型转换 -> 参数“任意”。 ...... #表现。 “任意合同”-> 为什么在一切都应该任意工作时使用合同?(修辞问题)如果您知道有关模板功能的链接适合您的评论,请随时发布。 :-)
【解决方案3】:

SqlReader 旨在最小化工作内存,因此只会一一查询结果。如果您想获得所有结果,您可以在代码周围使用 while 循环来获取行,只要它们可用。通过调用 return 您将中断 while 循环并仅返回第一行。如果您调用 yield return,您的方法将返回 IEnumerable&lt;string&gt; 而不是 string

【讨论】:

  • 谢谢,以前没有听说过“产量”。我会尝试谷歌它:)
【解决方案4】:

您可能可以创建一个数组或列表并使用您的 sqlReader 填充列表。 List 和 Array 可以使用 WCF 进行序列化和传输,也可以互操作。

在while循环内

listObject.Add(sqlReader[0].ToString());

【讨论】:

  • 非常感谢您的建议。我已经更新了我的答案。我需要将 SQL 查询返回的所有内容传输回客户端,而不仅仅是一列。
【解决方案5】:

您需要通过将所有数据读取器行加载到数据表中来实际读取它们,数据读取器是一个延迟加载对象,并且不能通过网络发送,因为它在开始时从未完全填充:

DataTable dt=new DataTable();     
dt.Load(dr);

然后,由于您使用的是 wcf,您可能需要将该数据表加载到与您的合同接口匹配的对象中。看看下面的代码片段,它使用反射将任意数据表转换为行为良好的对象匹配属性。

片段使用

List<myType> r = (List<myType>) dt.ToList<myType>();

片段

    using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Data;
using System.Reflection;

namespace xxxx.Sql.Extensions
{
    public static class DataTableExtensions
    {
        /// <summary>
        /// Gets a list of objects  based on a generic datatable
        /// </summary>
        /// <typeparam name="T">List of objects</typeparam>
        /// <param name="table">Existing datatable</param>
        /// <returns></returns>
        public static IList<T> ToList<T>(this DataTable table) where T : new()
        {
            IList<PropertyInfo> properties = typeof(T).GetProperties().ToList();
            IList<T> result = new List<T>();

            foreach (var row in table.Rows)
            {
                var item = CreateItemFromRow<T>((DataRow)row, properties);
                result.Add(item);
            }

            return result;
        }

        private static T CreateItemFromRow<T>(DataRow row, IList<PropertyInfo> properties) where T : new()
        {
            T item = new T();

            foreach (var property in properties)
            {
                if (row.Table.Columns.Contains(property.Name))
                {
                    var prop = row[property.Name] == System.DBNull.Value ? null : row[property.Name];
                    property.SetValue(item, prop, null);
                }
            }
            return item;
        }
        /// <summary>
        /// Creat a generic string list on the first field of a dataTable
        /// </summary>
        /// <param name="table"></param>
        /// <returns></returns>
        public static List<string> ToStringList(this DataTable table)
        {
            List<string> result = new List<string>();

            foreach (DataRow dr in table.Rows)
                result.Add(dr[0].ToString());

            return result;
        }
    }
}

【讨论】:

    【解决方案6】:

    古老的线程,但我会添加我的两个:

            string sqltext = @"select a.columnown, a.columntwo, a.columnthreee from blahblah as a";
            List<List<string>> toReturn = new List<List<string>>();
            using (SqlConnection con = new SqlConnection("YourConnectionString"))
            {
                con.Open();
                SqlCommand cmd = con.CreateCommand();
                cmd.CommandText = sqlTest;
    
                using (SqlDataReader sqlReader = cmd.ExecuteReader())
                {
                    if (sqlReader != null)
                    {
                        if (sqlReader.HasRows)
                        {
                            while (sqlReader.Read())
                            {
                                List<string> innerList = new List<string>();
                                for (int i = 0; i < sqlReader.FieldCount; i++)
                                {
                                    innerList.Add(sqlReader[i].ToString());
                                }
                                toReturn.Add(innerList);
                            }
                            con.Close();
                        }
                    }
                }
            }
    

    那么您只需返回您的List&lt;List&lt;string&gt;&gt;

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-10-24
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-01-11
      相关资源
      最近更新 更多