当返回一定数量的记录时,SqlHelper.ExecuteReader 结果会发生变化且意外
注意:您可能不需要阅读整篇文章就知道出了什么问题...如果您愿意,可以跳到有关 3 个场景的部分。仅当您想了解有关我如何尝试实现此操作以及错误发生位置的背景信息时,才阅读开头部分。
首先,我尝试检索存储在 CRM_Clients
表中的给定 callerId
的 Clients
列表。
我使用 SelectLiveClientsForCaller
方法从控制器检索客户端。然后,消息通过 DataProvider 类中的static Instance
方法传递到 DAL:
public List<Client> SelectLiveClientsForCaller(int callerID)
{
List<Client> results = new List<Client>();
IDataReader reader;
reader = DataProvider.Instance().SelectLiveClientsForCaller(callerID);
if (reader.Read())
{
// If I break here and enumerate the reader, it says that the IEnumerable returned no results
results = CBO.FillCollection<Client>(reader); // Always comes out as a count of 0
}
return results;
}
我的 DataProvider
类是一个抽象类,它概述了 SqlDataProvider 的所有方法
可用:
public abstract class DataProvider
{
// singleton reference to the instantiated object
static DataProvider objProvider = null;
// constructor
static DataProvider()
{
CreateProvider();
}
// dynamically create provider
private static void CreateProvider()
{
objProvider = (DataProvider)Reflection.CreateObject("data", "Owu.Modules.CRM", "");
}
// return the provider
public static DataProvider Instance()
{
return objProvider;
}
public abstract IDataReader SelectLiveClientsForCaller(int callerID);
/* More abstract methods here... */
}
在子类 SqlDataProvider
中,实际处理 SelectLiveClientsForCaller
方法并为存储过程调用 SqlHelper.ExecuteReader
CRM_Clients_SelectLiveForCaller:
public class SqlDataProvider : DataProvider
{
private const string ProviderType = "data";
private ProviderConfiguration _providerConfiguration = ProviderConfiguration.GetProviderConfiguration(ProviderType);
private string _myConnectionString;
private string _providerPath;
private string _objectQualifier;
private string _databaseOwner;
private string _moduleQualifier;
public SqlDataProvider()
{
//Read the configuration specific information for this provider
Provider objProvider = (Provider)_providerConfiguration.Providers[_providerConfiguration.DefaultProvider];
//Read the attributes for this provider
//Get Connection string from web.config
_myConnectionString = Config.GetConnectionString();
}
public string MyConnectionString
{
get { return _myConnectionString; }
}
public override IDataReader SelectLiveClientsForCaller(int callerID)
{
return (IDataReader)SqlHelper.ExecuteReader(
myConnectionString,
"CRM_Clients_SelectLiveForCaller",
callerID);
}
/* More methods here... */
}
最后,存储过程CRM_Clients_SelectLiveForCaller
ALTER PROCEDURE [dbo].[CRM_Clients_SelectLiveForCaller]
@CallerID int
AS
BEGIN
SET NOCOUNT ON;
IF @CallerID = -1
BEGIN
SELECT * FROM CRM_Clients WHERE IsDeleted = 'false'
END
ELSE
BEGIN
SELECT * FROM CRM_Clients WHERE ClientID IN
(SELECT ClientID FROM CRM_CallersClients WHERE CallerID = @CallerID)
AND IsDeleted = 'false'
END
END
返回给定callerid
的所有未删除客户端。
然后,这应该返回线路并从控制器返回结果...
到目前为止,我注意到了 3 种情况
如果没有从存储过程返回的记录(通过 sql server mgmt studio 执行),当调用方法序列
reader.Read()
返回 false 并完全跳过它。如果从存储过程返回 1 条记录(通过 sql server mgmt studio 执行),则在调用方法序列时
reader.Read()
返回 true 但枚举结果会给出一条消息,表示 IEnumerable 返回没有结果如果从存储过程返回 2 条记录(通过 sql server mgmt studio 执行),调用方法序列时
reader.Read()
返回 true 但枚举结果仅返回 1 条记录而不是 2 条
谁能解释为什么我会为每种情况得到这些结果?
如果您需要更多信息,请询问,我会尽快更新。
谢谢,
马特
Note: You may not need to read the whole thing to know what's wrong... skip down to the part about the 3 scenarios if you'd like. Only read the begin if you'd like some background info on how I've tried implementing this and where the errors occurred.
To begin with, I'm trying to retrieve a list of Clients
stored in the CRM_Clients
table for a given callerId
.
I retrieve the clients from my Controller with the SelectLiveClientsForCaller
method. The message is then passed on to the DAL through the static Instance
method in the DataProvider class:
public List<Client> SelectLiveClientsForCaller(int callerID)
{
List<Client> results = new List<Client>();
IDataReader reader;
reader = DataProvider.Instance().SelectLiveClientsForCaller(callerID);
if (reader.Read())
{
// If I break here and enumerate the reader, it says that the IEnumerable returned no results
results = CBO.FillCollection<Client>(reader); // Always comes out as a count of 0
}
return results;
}
My DataProvider
class is an abstract class which outlines all the methods the SqlDataProvider
has available:
public abstract class DataProvider
{
// singleton reference to the instantiated object
static DataProvider objProvider = null;
// constructor
static DataProvider()
{
CreateProvider();
}
// dynamically create provider
private static void CreateProvider()
{
objProvider = (DataProvider)Reflection.CreateObject("data", "Owu.Modules.CRM", "");
}
// return the provider
public static DataProvider Instance()
{
return objProvider;
}
public abstract IDataReader SelectLiveClientsForCaller(int callerID);
/* More abstract methods here... */
}
In the subclass SqlDataProvider
the SelectLiveClientsForCaller
method is actually handled and calls SqlHelper.ExecuteReader
for the stored procedure CRM_Clients_SelectLiveForCaller
:
public class SqlDataProvider : DataProvider
{
private const string ProviderType = "data";
private ProviderConfiguration _providerConfiguration = ProviderConfiguration.GetProviderConfiguration(ProviderType);
private string _myConnectionString;
private string _providerPath;
private string _objectQualifier;
private string _databaseOwner;
private string _moduleQualifier;
public SqlDataProvider()
{
//Read the configuration specific information for this provider
Provider objProvider = (Provider)_providerConfiguration.Providers[_providerConfiguration.DefaultProvider];
//Read the attributes for this provider
//Get Connection string from web.config
_myConnectionString = Config.GetConnectionString();
}
public string MyConnectionString
{
get { return _myConnectionString; }
}
public override IDataReader SelectLiveClientsForCaller(int callerID)
{
return (IDataReader)SqlHelper.ExecuteReader(
myConnectionString,
"CRM_Clients_SelectLiveForCaller",
callerID);
}
/* More methods here... */
}
Finally, the stored procedure CRM_Clients_SelectLiveForCaller
ALTER PROCEDURE [dbo].[CRM_Clients_SelectLiveForCaller]
@CallerID int
AS
BEGIN
SET NOCOUNT ON;
IF @CallerID = -1
BEGIN
SELECT * FROM CRM_Clients WHERE IsDeleted = 'false'
END
ELSE
BEGIN
SELECT * FROM CRM_Clients WHERE ClientID IN
(SELECT ClientID FROM CRM_CallersClients WHERE CallerID = @CallerID)
AND IsDeleted = 'false'
END
END
Returns all non-deleted clients for a given callerid
.
This should then go back up the line and return results from the controller...
There are 3 scenarios I've noticed so far
If there are no records returned from the stored proc (executed through sql server mgmt studio), when calling the sequence of methods
reader.Read()
returns false and it skips over it completely.If there is 1 record returned from the stored proc (executed through sql server mgmt studio), when calling the sequence of methods
reader.Read()
returns true but enumerating the results gives a message saying IEnumerable returned no resultsIf there are 2 records returned from the stored proc (executed through sql server mgmt studio), when calling the sequence of methods
reader.Read()
returns true but enumerating the results only returns 1 record instead of 2
Can anyone explain why I'm getting these results for each scenario?
If you need any more info please ask and I'll update this as soon as I can.
Thanks,
Matt
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(3)
每次调用 reader.Read() 都会读取一行,因此您正在使用行,就像任何 IEnumerable 调用者使用行一样。
您需要重新考虑在填充例程之外调用 .Read() 来检查行是否存在,因为如果您在使用当前位于读取器中的行之前在填充例程内再次调用 .Read() ,您将丢失第一行。
Each call to reader.Read() reads a row, so you are consuming rows just like any IEnumerable caller would consume rows.
You need to reconsider calling .Read() outside your fill routine to check for existence of rows, because if you call .Read() again inside the fill routine before using the row currently positioned in the reader, you will have lost that first row.
Reader.Read() 仅检查返回的第一行,并自动将结果集移动到下一条记录。
请尝试以下操作:
您可能需要调整 FillCollection 方法以适应此更改。
Reader.Read() only examines the first row that is returned, and automatically moves the result set onto the next record.
Try the following instead:
You may need to adjust your FillCollection method to accomodate this change.
reader.Read 告诉您记录是否可用
或..
reader.Read tells you if a record is available
or..