Using SqlDependency results in constant updates - c#

I pulled an example from this MSDN page and have used it pretty much verbatim. When run the code compiles properly but changeCount increments endlessly whether or not there has actually been a change to the data returned. When a change actually has occurred dataGridView1 reflects the change correctly. Why does my SqlDependency seem like it's firing in a loop even though there apparently have been no changes?
Here's the source:
#region Using directives
using System;
using System.Collections;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Data.SqlClient;
using System.Drawing;
using System.Linq;
using System.Runtime.InteropServices;
using System.Security.Permissions;
using System.Text;
using System.Windows.Forms;
#endregion
namespace PreAllocation_Check
{
public partial class Form1 : Form
{
int changeCount = 0;
const string tableName = "MoxyPosition";
const string statusMessage = "Last: {0} - {1} changes.";
DataSet dataToWatch = null;
SqlConnection MoxyConn = null;
SqlCommand SQLComm = null;
public Form1()
{
InitializeComponent();
}
private void Form1_Load(object sender, EventArgs e)
{
if (CanRequestNotifications())
{
SqlDependency.Start(GetConnectionString());
if (MoxyConn == null)
MoxyConn = new SqlConnection(GetConnectionString());
if (SQLComm == null)
{
SQLComm = new SqlCommand(GetSQL(), MoxyConn);
SqlParameter prm = new SqlParameter("#Quantity", SqlDbType.Int);
prm.Direction = ParameterDirection.Input;
prm.DbType = DbType.Int32;
prm.Value = 100;
SQLComm.Parameters.Add(prm);
}
if (dataToWatch == null)
dataToWatch = new DataSet();
GetData();
}
}
private void Form1_FormClosed(object sender, FormClosedEventArgs e)
{
SqlDependency.Stop(GetConnectionString());
if (MoxyConn != null)
MoxyConn.Close();
}
private bool CanRequestNotifications()
{
try
{
SqlClientPermission SQLPerm = new SqlClientPermission(PermissionState.Unrestricted);
SQLPerm.Demand();
return true;
}
catch
{
return false;
}
}
private string GetConnectionString()
{
return "server=***;database=***;user id=***;password=***";
}
private void GetData()
{
dataToWatch.Clear();
SQLComm.Notification = null;
SqlDependency SQLDep = new SqlDependency(SQLComm);
SQLDep.OnChange += new OnChangeEventHandler(SQLDep_OnChange);
using (SqlDataAdapter adapter = new SqlDataAdapter(SQLComm))
{
adapter.Fill(dataToWatch, tableName);
dataGridView1.DataSource = dataToWatch;
dataGridView1.DataMember = tableName;
}
}
private string GetSQL()
{
return "SELECT PortID, CONVERT(money, SUM(PreAllocPos), 1) AS PreAllocation, CONVERT(money, SUM(AllocPos), 1) AS Allocation, CONVERT(money, SUM(PreAllocPos) - SUM(AllocPos), 1) AS PreLessAlloc " +
"FROM MoxyPosition " +
"WHERE CONVERT(money, PreAllocPos, 1) <> CONVERT(money, AllocPos, 1) " +
"GROUP BY PortID " +
"ORDER BY PortID ASC;";
}
void SQLDep_OnChange(object sender, SqlNotificationEventArgs e)
{
ISynchronizeInvoke i = (ISynchronizeInvoke)this;
if (i.InvokeRequired)
{
OnChangeEventHandler tempDelegate = new OnChangeEventHandler(SQLDep_OnChange);
object[] args = { sender, e };
i.BeginInvoke(tempDelegate, args);
return;
}
SqlDependency SQLDep = (SqlDependency)sender;
SQLDep.OnChange -= SQLDep_OnChange;
changeCount++;
DateTime LastRefresh = System.DateTime.Now;
label1.Text = String.Format(statusMessage, LastRefresh.TimeOfDay, changeCount);
GetData();
}
}
}
Edit: It's worth noting that the database I want to run this against does not currently have the Broker Service enabled, and so to test my code I backed up my target database and restored it with a new name, then ran ALTER DATABASE my_db_name SET ENABLE_BROKER against it. All of my testing has been on this alternate database, which means I'm the only user on it.

This is an old question, but the problem is that your query doesn't meet the requirements.
Short answer:
add schema name to the table "FROM DBO.MoxyPosition " +
Longer answer:
You can see a list of requirements here, which are very similar to those of creating an indexed view. When a SQL Dependency is registered, if it is invalid the notification immediately fires letting you know it's invalid. When you think about it, this makes sense, because how can Visual studio know what the internal requirements are for the SQL Engine?
So in your SQLDep_OnChange function you'll want to look at the reason the dependency fired. The reason is in the e variable (info, source, and type). Details on the event object can be found here:
Info options
Source
Type
For your specific case notice how MS describes the Type property :
Gets a value that indicates whether this notification is generated
because of an actual change, OR BY THE SUBSCRIPTION.

I had a similar problem. Turns out doing SELECT * FROM dbo.MyTable was causing the update to fire constantly. Changing to SELECT Id, Column1, Column2 FROM dbo.MyTable fixed the problem.
It doesn't look like you're using * in your query, but you might try simplifying your query to see if you still have the issue.

I don't have an answer for this, but you did break at least one of the rules here: http://msdn.microsoft.com/en-us/library/aewzkxxh.aspx
when you failed to use two-part table names. Change MoxyPosition to dbo.MoxyPosition and review the rules linked above. I hope it helps, but something tells me something else is at fault here.

see what the Type of the SqlNotificationEventArgs is in your handler (defined as below). If you see it hit hundreds of times and the Type is Subscribe each time then your SQL statement is wrong - see guidelines in other postings
private void HandleOnChange(object sender, SqlNotificationEventArgs e)
{
...
var someType = e.Type; /*If it is Subscribe, not Change, then you may have your SQL statement wrong*/
...
}

Related

Oracle Managed Provider mixing up parameters

I have some code that looks like this (I have simplified it to focus on the issue, but the code is exactly as below with sensitive data names replaced):
private const string TargetIdParamName = "TargetId";
private const string LinkedGroupIdParamName = "LinkedGroupId";
private static readonly string UpdateLinkFromSql =
$#"UPDATE MyTableName
Set LinkFromId = :{LinkedGroupIdParamName}
WHERE Id = :{TargetIdParamName}";
using (var dbConn = GetConnection())
{
dbConn.Open();
using (var trans = dbConn.BeginTransaction())
{
try
{
var cmd = dbConn.CreateTransactedCommand(trans);
AddParameters(cmd);
cmd.CommandText = UpdateLinkFromSql;
cmd.Parameters[TargetIdParamName].Value = request.TargetGroupId;
cmd.Parameters[LinkedGroupIdParamName].Value = request.BreakPreviousLink ? DBNull.Value : (object) request.PreviousGroupId.Value;
cmd.ExecuteNonQuery();
trans.Commit();
}
catch (Exception e)
{
trans.Rollback();
throw;
}
}
}
private void AddParameters(OracleCommand cmd)
{
cmd.Parameters.Add(TargetIdParamName, OracleDbType.Long, ParameterDirection.Input);
cmd.Parameters.Add(LinkedGroupIdParamName, OracleDbType.Long, ParameterDirection.Input);
}
public static class DataAccessExtensions
{
public static OracleCommand CreateTransactedCommand(this OracleConnection source, OracleTransaction trans)
{
var cmd = source.CreateCommand();
cmd.Transaction = trans;
return cmd;
}
}
To illustrate the problem, lets say the 'TargetGroupId' was 12345 and the 'PreviousGroupId' was 67890.
From this code, I would expect the record with Id 12345 to have it's LinkFromId updated to 67890.
But what happens is the opposite, record with Id 67890 has it's LinkFromId set to 12345.
Now to get the expected behavior is easy, swap the values assigned to each parameter.
But the question remains, why are the parameters being swapped from what is expected? And yes I have triple checked that the query is what I think it is, the parameters are passed in correctly (like I did not accidentally name the parameters in the query in the opposite order or anything). Am I missing something?
Make sure you tell the command object to bind the parameters by name before executing it.
cmd.BindByName = true;
If you don't do this, it binds them ordinally. If they were added in the reverse order in which you were referring to them, that would explain the swapping.
I always set that property to true because the alternative is not as usable. It should be the default but that would be a breaking change. Fortunately, it's specific to the Oracle providers.

SignalR SqlDependency Update work but when changing page it stops working

Hello and thanks for reading this.
I used this guide as my base, and I have only added Owin Login/Registration(cookie)
When I login it shows the Notification from my NotificationHub and Im able to update the database and it runs the Update right away. So everything works until you try to switch page, then the notification stop. Even if i just update the current page im on.
What can be the problem?
using System;
using System.Collections.Generic;
using System.Configuration;
using System.Data;
using System.Data.SqlClient;
using System.Linq;
using System.Web;
using System.Threading.Tasks;
using Microsoft.AspNet.SignalR;
using Microsoft.AspNet.SignalR.Hubs;
using System.Security.Claims;
using System.Collections.Concurrent;
namespace LifeChange
{
[HubName("notificationHub")]
public class NotificationHub : Hub
{
private static readonly ConcurrentDictionary<string, User> Users = new ConcurrentDictionary<string, User>(StringComparer.InvariantCultureIgnoreCase);
private static List<User> UserList = new List<User>();
Int16 totalNewMessages = 0;
string UserID;
[HubMethodName("check")]
public Task Check(string id)
{
if (!Users.ToList().Exists(i => i.Value.ProfileId == id))
{
string profileId = id; //Context.QueryString["id"];
string connectionId = Context.ConnectionId;
var user = Users.GetOrAdd(profileId, _ => new User
{
ProfileId = profileId,
ConnectionIds = connectionId
});
lock (user.ConnectionIds)
{
Groups.Add(connectionId, user.ProfileId);
}
return base.OnConnected();
}
return null;
}
[HubMethodName("sendNotifications")]
public Task SendNotifications(string id)
{
UserID = id;
using (var connection = new SqlConnection(ConfigurationManager.ConnectionStrings["DefaultConnection"].ConnectionString))
{
string query = "SELECT NotificationNumber FROM [dbo].[NotificationStatus] WHERE UserID=" + UserID;
connection.Open();
using (SqlCommand command = new SqlCommand(query, connection))
{
command.Notification = null;
DataTable dt = new DataTable();
SqlDependency dependency = new SqlDependency(command);
dependency.OnChange += new OnChangeEventHandler(dependency_OnChange);
if (connection.State == ConnectionState.Closed)
connection.Open();
var reader = command.ExecuteReader();
dt.Load(reader);
if (dt.Rows.Count > 0)
{
totalNewMessages = Int16.Parse(dt.Rows[0]["NotificationNumber"].ToString());
}
}
}
IHubContext context = GlobalHost.ConnectionManager.GetHubContext<NotificationHub>();
//return context.Clients.All.RecieveNotification(totalNewMessages);
return context.Clients.Client(Users.Values.FirstOrDefault(i => i.ProfileId == UserID).ConnectionIds).RecieveNotification(totalNewMessages);
}
private void dependency_OnChange(object sender, SqlNotificationEventArgs e)
{
if (e.Type == SqlNotificationType.Change)
{
NotificationHub nHub = new NotificationHub();
nHub.SendNotifications(UserID);
}
}
}
}
There are a lot of things wrong here, you should read some good basic docs about SignalR concepts first. The guide you are referring to is very poor. The main things you should check:
hubs are created by SignalR from scratch each time a hub method is called from a client, and then destroyed, therefore for something as basic as this you should not interfere with that, which instead you do with your event handler
because of the previous point, your UserID member is simply not in the right place, and it sort of works for you just because of wrong side effects you are generating with more mistakes (described below): do not store any state supposed to live across calls inside hubs instance members, it is not meant to survive
you explicitly do a new NotificationHub() for something that has nothing to do with SignalR broadcasting, you should move the sql dependency stuff outside of the hub and do it there, while leaving hubs do their own stuff only
when you change page your connection is lost, buy your event handler is (probably) artificially keeping a "dead" hub instance alive, and it uses it for notification, but that's simply wrong
You should review your design, starting by managing the sql dependency outside of hubs. You already know about IHubContext, so use that from outside hubs, and you should use hubs just for what they are for, without keeping them alive artificially when attaching them to dependency event or, even worse, creating them explicitly. You're on the road to entangled spaghetti code which is hard to reason about and understand, as you are clearly experiencing.

C# MySQL Select into Var -> Memory increasing

I am new to C# and trying to read a value out of a MySQL Database.
What i get is a single number.
I store that into an int variable and check for specific numbers to activate functions of a kuando busylight.
The whole thing is looped (tried while (true) {} and goto methods).
It works so far, but my memory usage is increasing. starting at ~6 MiB...30MiB and so on.
Somehow it saves data into the process but I did not find out how to clear the used memory.
As is said im very very new to this, maybe my code is toooooo crappy to work fine and its now problem of clearing unused data. just tell me :D
Thank you very much!
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using MySql.Data.MySqlClient;
using Plenom.Components.Busylight.Sdk;
using System.Threading;
namespace TANSS4BLL
{
class Program
{
static void Main()
{
var controller = new BusylightUcController();
int resultStatus;
while (true)
{
string sqlDataBaseSelect = "SELECT typID FROM az_manager where maID = 4328 ORDER BY datum DESC LIMIT 1";
string ConnectionString = "Server=localhost; Database=tanss; Uid=root; Pwd=password";
using (MySqlConnection connDataBase = new MySqlConnection(ConnectionString))
{
connDataBase.Open();
MySqlCommand cmd = new MySqlCommand(sqlDataBaseSelect, connDataBase);
resultStatus = (int)cmd.ExecuteScalar();
if (resultStatus == 1)
{
//Console.WriteLine("Debugtext1");
controller.Light(BusylightColor.Green);
}
else if (resultStatus == 2)
{
//Console.WriteLine("Debugtext2");
controller.Light(BusylightColor.Red);
}
else if (resultStatus == 3)
{
//Console.WriteLine("Debugtext3");
controller.Light(BusylightColor.Red);
}
else if (resultStatus == 9)
{
//Console.WriteLine("Debugtext4");
controller.Light(BusylightColor.Red);
}
connDataBase.Close();
}
//Console.WriteLine(resultStatus);
Thread.Sleep(1000);
//Console.Clear();
}
}
}
}
You are creating multiple connections to the database with your while(true) loop.
string ConnectionString = "Server=localhost; Database=tanss; Uid=root; Pwd=password";
This line can easily be move out of the while loop and you that should help with your problem.
Also, if this turns into production code, you need to set up parameters when you are working with the database. Parameters are how we avoid sql injection.
Avoiding SQL Injection
Hopefully this helps you out!
You don't have to handle it, Garbage Collector will handle it automatically. But try to avoid reconnecting to database in loop. If you need to do it this way - put MySqlCommand in loop, not MySqlConnection.
Garbage Collector automaticaly clear unused resources. You can run Garbage collector manualy, but it is quite expensive operation, so don't put it inside while(true) loop. Mor info about GC is here: http://msdn.microsoft.com/en-us/library/s5zscb2d(v=vs.85).aspx
One of the things I see that you are doing is you are not disposing of the MySqlCommand Object, that's going to continually build and take up resources. You need to properly dispose of that object whether it be through a using statement or call the dispose method on it. Also I would start you mysql Connection outside the for loop if your going to continually poll the database. I would also put some try catch logic into there as well to catch DB connection errors. Here is how I would attack this,
var controller = new BusylightUcController();
int resultStatus;
string sqlDataBaseSelect = "SELECT typID FROM az_manager where maID = 4328 ORDER BY datum DESC LIMIT 1";
string ConnectionString = "Server=localhost; Database=tanss; Uid=root; Pwd=password";
using (MySqlConnection connDataBase = new MySqlConnection(ConnectionString))
{
connDataBase.Open();
try
{
while (true)
{
using(MySqlCommand cmd = new MySqlCommand(sqlDataBaseSelect, connDataBase))
{
resultStatus = (int)cmd.ExecuteScalar();
switch(resultStatus)
{
case 1:
//Console.WriteLine("Debugtext1");
controller.Light(BusylightColor.Green);
break;
case 2:
//Console.WriteLine("Debugtext2");
controller.Light(BusylightColor.Red);
break;
case 3:
//Console.WriteLine("Debugtext3");
controller.Light(BusylightColor.Red);
break;
case 9:
//Console.WriteLine("Debugtext4");
controller.Light(BusylightColor.Red);
break;
default:
// handle when its not any of your results.
break;
}
}
//Console.WriteLine(resultStatus);
Thread.Sleep(1000);
//Console.Clear();
}
}catch(Exception ex)
{
//HandleException
}finally
{
connDataBase.Close();
}
}

Why doesn't SQL Server Service Broker notify me of an insert to a table?

I have set up a list that should be aware to changes to it's data source as follows, more or less:
public class SharedList<T>: ObservableCollection<T> where T: XTimeEntity
{
private const string DependencyQuery = "select TITLE_ACTIVE, TITLE_NAME from TITLE";
private readonly SqlDependency _dependency = new SqlDependency();
public SharedList()
{
var connectionString = ConfigurationManager.ConnectionStrings["XTime900Context"].ConnectionString;
SqlDependency.Stop(connectionString);
using (var sqn = new SqlConnection(connectionString))
{
using (var cmd = new SqlCommand(DependencyQuery, sqn))
{
_dependency.AddCommandDependency(cmd);
}
}
_dependency.OnChange += DependencyOnOnChange;
PopulateList();
SqlDependency.Start(connectionString);
}
}
Yet when I executed insert TITLE values (1, 1, 'Mr.') in SSMS, no event fired. Does the change that triggered the event have to be made on a SqlConnection object or something?
I think you need to execute the command. Read the official docs, they have a sample. SqlDependency is very fragile. There are many usage rules and race conditions involved.

How can I change the table adapter's command timeout

I'm using Visual Studio 2008 with C#.
I have a .xsd file and it has a table adapter. I want to change the table adapter's command timeout.
Thanks for your help.
With some small modifications csl's idea works great.
partial class FooTableAdapter
{
/**
* <summary>
* Set timeout in seconds for Select statements.
* </summary>
*/
public int SelectCommandTimeout
{
set
{
for (int i = 0; i < this.CommandCollection.Length; i++)
if (this.CommandCollection[i] != null)
this.CommandCollection[i].CommandTimeout = value;
}
}
}
To use it, just set
this.FooTableAdapter.CommandTimeout = 60; somewhere before the this.FooTableAdapter.Fill();
If you need to change the timeout on a lot of table adapters, you could create a generic extension method and have it use reflection to change the timeout.
/// <summary>
/// Set the Select command timeout for a Table Adapter
/// </summary>
public static void TableAdapterCommandTimeout<T>(this T TableAdapter, int CommandTimeout) where T : global::System.ComponentModel.Component
{
foreach (var c in typeof(T).GetProperty("CommandCollection", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.GetProperty | System.Reflection.BindingFlags.Instance).GetValue(TableAdapter, null) as System.Data.SqlClient.SqlCommand[])
c.CommandTimeout = CommandTimeout;
}
Usage:
this.FooTableAdapter.TableAdapterCommandTimeout(60);
this.FooTableAdapter.Fill(...);
This is a little slower. And there is the possibility of an error if you use it on the wrong type of object. (As far as I know, there is no "TableAdapter" class that you could limit it to.)
I have investigated this issue a bit today and come up with the following solution based on a few sources.
The idea is to create a base class for the table adapter too inherit which increases the timeout for all commands in the table adapter without having to rewrite too much existing code. It has to use reflection since the generated table adapters don't inherit anything useful. It exposes a public function to alter the timeout if you want to delete what i used in the constructor and use that.
using System;
using System.Data.SqlClient;
using System.Reflection;
namespace CSP
{
public class TableAdapterBase : System.ComponentModel.Component
{
public TableAdapterBase()
{
SetCommandTimeout(GetConnection().ConnectionTimeout);
}
public void SetCommandTimeout(int Timeout)
{
foreach (var c in SelectCommand())
c.CommandTimeout = Timeout;
}
private System.Data.SqlClient.SqlConnection GetConnection()
{
return GetProperty("Connection") as System.Data.SqlClient.SqlConnection;
}
private SqlCommand[] SelectCommand()
{
return GetProperty("CommandCollection") as SqlCommand[];
}
private Object GetProperty(String s)
{
return this.GetType().GetProperty(s, BindingFlags.NonPublic | BindingFlags.GetProperty | BindingFlags.Instance).GetValue(this, null);
}
}
}
I had a couple of issues with using Mitchell Gilman's solution that I was eventually able to workaround.
First of all, I needed to make sure to use the right namespace. It took me a while to figure out that Designer file for the xsd data set actually contains two namespaces, one for the data set in general and one for the table adapters. So the first thing is to note is that the namespace for the table adapter should be used, not for the data set in general.
Secondly, the commandcollection may not always be initialized when the timeout command is used for the first time. To work around this, I called the InitCommandCollection command if this was the case.
So the adapted solution I used was
namespace xxx.xxxTableAdapters
partial class FooTableAdapter
{
/**
* <summary>
* Set timeout in seconds for Select statements.
* </summary>
*/
public int SelectCommandTimeout
{
set
{
if (this.CommandCollection == null)
this.InitCommandCollection();
for (int i = 0; i < this.CommandCollection.Length; i++)
if (this.CommandCollection[i] != null)
this.CommandCollection[i].CommandTimeout = value;
}
}
}
Hope that's helpful to people!
In some cases you cannot access members like Adapter in your class, since they are defined as private to the class.
Fortunately, the wizard will generate partial classes, which means you can extend them. As described in [this thread by Piebald][1], you can write your own property to set the timeout on select-commands.
Generally, you would do this:
partial class FooTableAdapter
{
/**
* <summary>
* Set timeout in seconds for Select statements.
* </summary>
*/
public int SelectCommandTimeout
{
set
{
for ( int n=0; n < _commandCollection.Length; ++n )
if ( _commandCollection[n] != null )
((System.Data.SqlClient.SqlCommand)_commandCollection[n])
.commandTimeout = value;
}
}
}
Note that I have not actually tried this myself, but it seems like a viable solution.
Say your dataset is called MySET.
There is one table called MyTable
MySETTableAdapters.MyTableTableAdapter fAdapter =
new MySETTableAdapters.MyTableTableAdapter();
fAdapter.Adapter.SelectCommand.CommandTimeout = <fill inyour value here>;
Call ChangeTimeout Function by providing the TableAdapter and Time in seconds.
this.ChangeTimeout(this.taTest, 500);
Function :
private void ChangeTimeout(Component component, int timeout)
{
if (!component.GetType().FullName.Contains("TableAdapter")) {
return;
}
PropertyInfo adapterProp = component.GetType().GetProperty("CommandCollection", BindingFlags.NonPublic | BindingFlags.GetProperty | BindingFlags.Instance);
if (adapterProp == null) {
return;
}
SqlCommand[] command = adapterProp.GetValue(component, null) as SqlCommand[];
if (command == null) {
return;
}
Interaction.command(0).CommandTimeout = timeout;
}
Here's some example code from MSDN, using VB.NET:
Imports System.Data.SqlClient
Namespace MyDataSetTableAdapters
Partial Class CustomersTableAdapter
Public Sub SetCommandTimeOut(ByVal timeOut As Integer)
For Each command As SqlCommand In Me.CommandCollection
command.CommandTimeout = timeOut
Next
End Sub
End Class
End Namespace
When it comes time to call a long query, just call the SetCommandTimeOut method before the query:
Dim ds As New MyDataSet
Dim customersTA As New MyDataSetTableAdapters.CustomersTableAdapter
' Increase time-out to 60 seconds
customersTA.SetCommandTimeOut(60000)
' Do the slow query
customersTA.FillSlowQuery(ds.Customers)
There seems to be a more convenient way to do this. Here's a quick recap of what I found.
Let's say I add a (class library) project called MyDB to my solution. Into that project I add a DataSet called "Data". And into that dataset, I drag a table called "X".
What I get on the design surface is an object that shows that I have an object called "XTableAdapter".
I now open the generated code, Data.Designer.cs, and look for XTableAdapter.
When I find it, I note that it's contained in namespace MyDB.DataTableAdapters - which is just a concatenation of the name of the project, "MyDB", the name of the DataSet, "Data", and "TableAdapters".
With that in hand, I now go back to the class library, still called Class1.cs (which I'll ignore for now).
I change its namespace from MyDB to MyDB.DataTableAdapters.
I change the class declaration to public partial class XTableAdapter,
and make it look like this:
using System.Data.SqlClient;
namespace MyDB.DataTableAdapters
{
public partial class XTableAdapter
{
public void SetTimeout(int seconds)
{
foreach (SqlCommand cmd in CommandCollection)
{
cmd.CommandTimeout = seconds;
}
}
}
}
The calling sequence could hardly be clearer:
int TwoMinutes = 120;
XTableAdapter.SetTimeout(TwoMinutes);
Less muss, less fuss, less reflection (well, none), less filling.
If you use a partial class, make you have the right namespace. Probably [your data set's name] + "TableAdapters'. Example:
namespace MyProject.DataSet1TableAdapters
You can open up the Properties folder, open Settings.settings and alter the Timeout property of your connection string.
This one is a bit old now and suspect this solution is not relevant to everyone, but I've ended up using AniPol's solution to override the ObjectDataSource control as follows:
public class MyObjectDataSource : ObjectDataSource
{
public MyObjectDataSource()
{
this.ObjectCreated += this.MyObjectDataSource_ObjectCreated;
}
private void MyObjectDataSource_ObjectCreated(object sender, ObjectDataSourceEventArgs e)
{
var objectDataSourceView = sender as ObjectDataSourceView;
if (objectDataSourceView != null && objectDataSourceView.TypeName.EndsWith("TableAdapter"))
{
var adapter = e.ObjectInstance;
PropertyInfo adapterProp = adapter.GetType()
.GetProperty(
"CommandCollection",
BindingFlags.NonPublic | BindingFlags.GetProperty | BindingFlags.Instance);
if (adapterProp == null)
{
return;
}
SqlCommand[] commandCollection = adapterProp.GetValue(adapter, null) as SqlCommand[];
if (commandCollection == null)
{
return;
}
foreach (System.Data.SqlClient.SqlCommand cmd in commandCollection)
{
cmd.CommandTimeout = 120;
}
}
}
}
I do like this ; Right click Fill() or GetX() function and click Goto Defination from menu.
You will see Source code of DATATABLE. And find ;
private global::System.Data.SqlClient.SqlCommand[] _commandCollection;
command line from your dataadapter class.
And Change the private to public .
Now you can access the _commandCollection and you can change all attributes.
But be careful when you add or change any Filed form DESIGNER , the public will be private again by autogenerate system.
And also , when you finish to call Fill or Get Function you must reset _commandColleciton calling this function ( InitCommandCollection() )
public void InitCommandCollection() {}
This function is also private by autogen, you must change to public also!
Example:
dsIslemlerTableAdapters.tblIslemlerTableAdapter _t = new dsIslemlerTableAdapters.tblIslemlerTableAdapter();
dsIslemler.tblIslemlerDataTable _m = new dsIslemler.tblIslemlerDataTable();
_t._commandCollection[0].CommandText = "Select * From tblIslemler Where IslemTarihi>='' And IslemTarihi<=''";
_m = _t.GetData();
_t.InitCommandCollection();
Expanding on the already very useful answers for tableadapters that helped me a lot, I also had the need to read out the actual timeout value. Thus:
namespace XTrans.XferTableAdapters
{
public partial class FooTableAdapter
{
int? _timeout = null;
///<summary>
///Get or set the current timeout in seconds for Select statements.
///</summary>
public int CurrentCommandTimeout
{
get
{
int timeout = 0;
if (_timeout != null)
{
timeout = (int)_timeout;
}
else
{
for (int i = 0; i < this.CommandCollection.Length; i++)
if (this.CommandCollection[i] != null)
timeout = this.CommandCollection[i].CommandTimeout;
}
return timeout;
}
set
{
if (this.CommandCollection == null)
this.InitCommandCollection();
for (int i = 0; i < this.CommandCollection.Length; i++)
if (this.CommandCollection[i] != null)
{
this.CommandCollection[i].CommandTimeout = value;
_timeout = value;
}
}
}
}
}
If you go to [name of DataSet].Designer.cs which is a file added under the data set file in the solution and then search for :
private void InitCommandCollection();
This is a function that you should be able to set properties for functions that have been defined in a table adapter.
The first line in that function is
this._commandCollection = new global::System.Data.IDbCommand[<number of function defined in a table adapater>];
and then in the next line for each of those function, you can set
((global::System.Data.SqlClient.SqlCommand)(this._commandCollection[<index>])).CommandTimeout = 0;
which 0 indicates no limitation and the function will not stop due to the time out and also it can be set to 10, 20, 30 or 1000 and so on
After scratching my head all day long I finally got the resolution for this. After designing your .xsd file all you need to do is go to your dataset.designer.cs page which is an auto generated page and change the code to provided below. It really works. Give it a try.
protected global::System.Data.SqlClient.SqlCommand[] CommandCollection
{
get
{
if ((this._commandCollection == null))
{
this.InitCommandCollection();
_commandCollection[0].CommandTimeout = 0;
}
_commandCollection[0].CommandTimeout = 0;
return this._commandCollection;
}
}

Categories