С# Неверная попытка вызова Чтение, когда читатель закрыт

У меня есть неверная попытка вызвать Read, когда ошибка чтения закрыта, когда я делаю проект на 3 уровня на языке С#. То, что я пытаюсь сделать, - это получить столбцы данных адреса, объединив две таблицы вместе и отобразив их в выпадающем списке. Вот мой уровень доступа к данным:

    public List<Distribution> getDistributionAll()
    {
        List<Distribution> distributionAll = new List<Distribution>();
        string address;
        SqlDataReader dr = FoodBankDB.executeReader("SELECT b.addressLineOne FROM dbo.Beneficiaries b INNER JOIN dbo.Distributions d ON d.beneficiary = b.id");

        while (dr.Read())
        {
            address = dr["addressLineOne"].ToString();
            distributionAll.Add(new Distribution(address));
        }

        return distributionAll;
    }

И это мой класс FoodBankDB:

   public class FoodBankDB
{
    public static string connectionString = Properties.Settings.Default.connectionString;
    public static SqlDataReader executeReader(string query)
    {
        SqlDataReader result = null;
        System.Diagnostics.Debug.WriteLine("FoodBankDB executeReader: " + query);
        SqlConnection connection = new SqlConnection(connectionString);
        SqlCommand command = new SqlCommand(query, connection);
        connection.Open();
        result = command.ExecuteReader();
        connection.Close();
        return result;
    }

Я разделил их на два класса, чтобы всякий раз, когда моя строка соединения была изменена, я могу легко изменить весь проект, изменив класс FoodBankDB.

И это мой уровень бизнес-логики:

public List<Distribution> getAllScheduledDistribution()
    {
        List<Distribution> allDistribution = new List<Distribution>();
        Distribution distributionDAL = new Distribution();
        allDistribution = distributionDAL.getDistributionAll();
        return allDistribution;
    }

И последнее, но не менее важное: мой уровень представления:

List<Distribution> scheduledList = new List<Distribution>();
scheduledList = packBLL.getAllScheduledDistribution();
ddlScheduleList.DataSource = scheduledList;
ddlScheduleList.DataTextField = "address";
ddlScheduleList.DataValueField = "address";
ddlScheduleList.DataBind();

Он работал хорошо, если я не разделил уровень доступа к данным и класс строки подключения. Кто-нибудь знает, как решить эту ошибку?

Спасибо заранее.

Обновленная часть

        public static string GetConnectionString()
    {
        return connectionString;
    }

Ответ 1

Это не работает, потому что вы закрываете соединение перед возвратом читателя. Reader работает только при открытии соединения:

result = command.ExecuteReader();
connection.Close();

return result; // here the reader is not valid

Вообще говоря, вы не должны возвращать читателя на бизнес-уровень. Считыватель должен использоваться только на уровне доступа к данным. Он должен использоваться, а затем он и соединение должны быть закрыты.

Вы должны скорее вернуть объект, который может работать после закрытия соединения, например. a DataSet или DataTable или, альтернативно, набор DTO. Например:

public List<Distribution> getDistributionAll()
{
    List<Distribution> distributionAll = new List<Distribution>();

    using (var connection = new SqlConnection(FoodBankDB.GetConnectionString())) // get your connection string from the other class here
    {
        SqlCommand command = new SqlCommand("SELECT b.addressLineOne FROM dbo.Beneficiaries b INNER JOIN dbo.Distributions d ON d.beneficiary = b.id", connection);
        connection.Open();
        using (var dr = command.ExecuteReader())
        {
            while (dr.Read())
            {
                string address = dr["addressLineOne"].ToString();

                distributionAll.Add(new Distribution(address));
            }
        }
    }

    return distributionAll;
}

Ответ 2

Предыдущий пример является хорошим примером... Но вы также можете выполнить его ниже кода, который automatically закрывает экземпляр connection, когда метод datareader.close() вызывается...

reader = Sqlcmd.ExecuteReader(CommandBehavior.CloseConnection);