当返回一定数量的记录时,SqlHelper.ExecuteReader 结果会发生变化且意外

发布于 2024-09-08 06:55:01 字数 4121 浏览 1 评论 0原文

注意:您可能不需要阅读整篇文章就知道出了什么问题...如果您愿意,可以跳到有关 3 个场景的部分。仅当您想了解有关我如何尝试实现此操作以及错误发生位置的背景信息时,才阅读开头部分。

首先,我尝试检索存储在 CRM_Clients 表中的给定 callerIdClients 列表。

我使用 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 种情况

  1. 如果没有从存储过程返回的记录(通过 sql server mgmt studio 执行),当调用方法序列 reader.Read() 返回 false 并完全跳过它。

  2. 如果从存储过程返回 1 条记录(通过 sql server mgmt studio 执行),则在调用方法序列时 reader.Read() 返回 true 但枚举结果会给出一条消息,表示 IEnumerable 返回没有结果

  3. 如果从存储过程返回 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

  1. 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.

  2. 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 results

  3. If 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 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

热血少△年 2024-09-15 06:55:02

每次调用 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.

夕色琉璃 2024-09-15 06:55:02

Reader.Read() 仅检查返回的第一行,并自动将结果集移动到下一条记录。

请尝试以下操作:

while (reader.Read())
{
results = CBO.FillCollection<Client>(reader); 
}

您可能需要调整 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:

while (reader.Read())
{
results = CBO.FillCollection<Client>(reader); 
}

You may need to adjust your FillCollection method to accomodate this change.

最初的梦 2024-09-15 06:55:02

reader.Read 告诉您记录是否可用

//expecting one record
if(reader.Read())
{
     //get reader["values"];
}

或..

//expecting multiple records
while(reader.Read())
{
     //get reader["values"];
}

reader.Read tells you if a record is available

//expecting one record
if(reader.Read())
{
     //get reader["values"];
}

or..

//expecting multiple records
while(reader.Read())
{
     //get reader["values"];
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文