хороший способ запроса многих баз данных в ASP.NET
-
08-07-2019 - |
Вопрос
Я пытаюсь запустить один и тот же SQL-выбор во многих базах данных Oracle (по крайней мере, дюжине) и отобразить выходные данные в Gridview.
Я взломал кое-что, что работает, но, к сожалению, это очень медленно.Я думаю, что ситуация усугубляется тем фактом, что по крайней мере одна из дюжины баз данных всегда будет недоступна или иным образом находится в состоянии ошибки.
Помимо того, что я медленный, я не могу избавиться от мысли, что это не лучший способ сделать это и не очень похоже на «.NET».
Раньше я писал что-то подобное в виде простого цикла в PHP, который просто подключается к каждой базе данных по очереди, запускает sql и записывает другую <tr>
, и он работает как минимум в два раза быстрее для данного запроса.Но меня это не очень устраивает, хотелось бы подтянуть свои знания!
Я изучаю C# и ASP.NET, поэтому извините за ужасный код :)
public void BindData(string mySQL)
{
OracleConnection myConnection;
OracleDataAdapter TempDataAdapter;
DataSet MainDataSet = new DataSet();
DataTable MainDataTable = new DataTable();
DataSet TempDataSet;
DataTable TempDataTable;
string connectionString = "";
Label1.Visible = false;
Label1.Text = "";
foreach (ListItem li in CheckBoxList1.Items)
{
if (li.Selected)
{
connectionString = "Data Source=" + li.Text + "";
connectionString += ";Persist Security Info=True;User ID=user;Password=pass;Unicode=True";
myConnection = new OracleConnection(connectionString);
try
{
TempDataAdapter = new OracleDataAdapter(mySQL, myConnection);
TempDataSet = new DataSet();
TempDataTable = new DataTable();
TempDataAdapter.Fill(TempDataSet);
TempDataTable = TempDataSet.Tables[0].Copy();
/* If the main dataset is empty, create a table by cloning from temp dataset, otherwise
copy all rows to existing table.*/
if (MainDataSet.Tables.Count == 0)
{
MainDataSet.Tables.Add(TempDataTable);
MainDataTable = MainDataSet.Tables[0];
}
else
{
foreach (DataRow dr in TempDataTable.Rows)
{
MainDataTable.ImportRow(dr);
}
}
}
catch (OracleException e)
{
Label1.Visible = true;
Label1.Text = Label1.Text + e.Message + " on " + li.Text + "<br>";
}
finally
{
if (myConnection != null)
{
myConnection.Close();
myConnection = null;
}
TempDataSet = null;
TempDataAdapter = null;
TempDataTable = null;
}
}
}
GridView1.DataSourceID = String.Empty;
if (MainDataSet.Tables.Count != 0)
{
GridView1.DataSource = MainDataSet;
if (GridView1.DataSource != null)
{
GridView1.DataBind();
}
}
}
protected void Button1_Click(object sender, EventArgs e)
{
BindData(TextBox1.Text);
}
Спасибо!
ОБНОВЛЯТЬ:Код SQL варьируется, для тестирования я использовал очень простые запросы, такие как select sysdate from dual
или select name from v$database
.В конечном итоге это будет намного сложнее, идея в том, что я смогу запускать практически все, отсюда и BindData(TextBox1.Text)
ОБНОВЛЯТЬ:Причина подключения ко многим базам данных из кода ASP.NET, а не хранимой процедуры в одной или всех базах данных или репликации в одну базу данных, двояка.Во-первых, рассматриваемые базы данных являются часто обновляемыми копиями нескольких аналогичных производственных сред (обычно это разработка, тестирование и поддержка для каждого клиента), поэтому все, что делается с реальными базами данных, должно будет регулярно обновляться или переделываться, поскольку они в любом случае перезагружаются.Во-вторых, я заранее не знаю, какой запрос может быть выполнен, эта форма позволяет мне просто ввести, например. select count (name) from dbusers
против дюжины баз данных без необходимости предварительно думать о репликации таблицы dbusers в главную базу данных.
Решение
Если вы запустите метод DataAdapter.Fill для объекта DataTable, таблица будет обновлена с учетом результатов запроса.Таким образом, вместо того, чтобы создавать новые объекты DataTable и DataSet, а затем копировать DataRows вручную, вы можете просто добавить строки в одну и ту же таблицу.
Попробуйте что-то вроде этого (в непроверенном коде C#):
public void BindData(string mySQL)
{
OracleConnection myConnection;
// Empty connection string for now
OracleDataAdapter MainDataAdapter = new OracleDataAdapter(mySQL, "");
DataTable MainDataTable = new DataTable();
string connectionString = "";
Label1.Visible = false;
Label1.Text = "";
foreach (ListItem li in CheckBoxList1.Items)
{
if (li.Selected)
{
connectionString = "Data Source=" + li.Text + "";
connectionString += ";Persist Security Info=True;User ID=user;Password=pass;Unicode=True";
MainDataAdapter.SelectCommand.Connection.ConnectionString = connectionString
try
{
MainDataAdapter.Fill(MainDataTable);
}
catch (OracleException e)
{
Label1.Visible = true;
Label1.Text = Label1.Text + e.Message + " on " + li.Text + "<br>";
}
}
}
GridView1.DataSourceID = String.Empty;
GridView1.DataSource = MainDataTable;
GridView1.DataBind();
}
Я сделал следующие изменения:
- Создал один адаптер данных и назначил ему команду выбора, используя ваш запрос MySQL.
- Дал соединению пустую строку подключения
- Создал объект таблицы данных и удалил наборы данных (они нужны вам только в том случае, если ваш запрос возвращает несколько строк).
- Изменен цикл, чтобы просто установить строку подключения SelectCommand (возможно, вам придется изменить это, заменив SelectCommand новым)
- Удалены вызовы Connection.Close().DataAdapter делает это автоматически.
Вот и все.Если ваши базы данных находятся в автономном режиме, замедление работы все равно будет наблюдаться, но, по крайней мере, код станет проще и быстрее, поскольку вам не придется копировать все строки между таблицами.
Еще кое-что.Вероятно, вы можете установить тайм-аут соединения в строке подключения.Попробуйте снизить этот показатель.
Другие советы
может быть много факторов, вызывающих его медленную работу.Какой оператор sql выполняется медленно?
Если кто-то, читающий это, использует sql-сервер, Скотт Митчелл только что написал хорошую статью, которая поможет решить эту проблему на sql-сервере: Выполнение одного и того же запроса к нескольким базам данных
Почему бы не использовать для этого репликацию... вы знаете, одну центральную базу данных, которая собирает новые данные из других баз данных и просто выполняет ваши запросы к этому набору данных, который никогда будет вниз.
Почему бы не запустить одну хранимую процедуру в одной базе данных Oracle и не позволить процедуре вызывать другие базы данных?Это правильный способ работы со связанными базами данных.
Похоже, вас больше интересует ответ на более общий вопрос: Как я могу выполнить длительную задачу без зависания пользовательского интерфейса (ASP или WinForms)?
Ответ на этот вопрос — использовать несколько Потоки.Я бы выполнил подобную длительную задачу в отдельном потоке и показал пользователю страницу с текущими результатами (либо автоматическое обновление, либо с помощью ajax и т. д.).Вы даже можете проявить фантазию и создать задачи для каждого доступного процессора, чтобы максимально эффективно использовать возможности вашей машины (используя что-то вроде Параллельные расширения);однако это значительно увеличивает сложность, и может быть трудно сделать все правильно.
Если вы еще не работали с потоками в .Net, можно найти отличный учебник. здесь (одним и единственным Джон Скит)