I am trying to get a test application to work using Oracle Change Notification with C#, but I am not receiving the callback notification in my application. Oracle DB version is 11.2.0.1.0. Oracle.DataAccess v.2.112.30. I can confirm the query gets registered in Oracle by viewing SYS.USER_CHANGE_NOTIFICATION_REGS and SYS.USER_CQ_NOTIFICATION_QUERIES. However, nothing ever appears in SYS.DBA_CHANGE_NOTIFICATION_REGS.
The registration persists until I commit a transaction on the table. The registration disappears after a few seconds after the commit and my application does not received the notification.
I have made sure my computer is listening on the correct port and have even tried turning off any firewall that could be blocking the port.
I do have GRANT CHANGE NOTIFICATION to MYSCHEMA, GRANT EXECUTE ON DBMS_CHANGE_NOTIFICATION TO MYSCHEMA, and the JOB_QUEUE_PROCESSES is set to 1.
Questions:
1) Should the registration be visible in SYS.DBA_CHANGE_NOTIFICATION_REGS and, if so, what could be causing it not to be when it is visible in SYS.USER_CHANGE_NOTIFICATION_REGS and SYS.USER_CQ_NOTIFICATION_QUERIES?
2) What could be causing the registration to disappear after a commit?
3) What could be causing the failure of the notification to my application?
Here is the C# code I am using and it is basically the same as from the Oracle website:
using System;
using System.Threading;
using System.Data;
using Oracle.DataAccess.Client;
namespace NotifyTest
{
public class Program
{
public static bool IsNotified = false;
public static void Main(string[] args)
{
string constr = "User Id=mySchema;Password=myPassword;Data Source=myOracleInstance";
OracleDependency dep = null;
try
{
using (var con = new OracleConnection(constr))
{
Console.WriteLine("Registering query...");
var cmd = new OracleCommand("select * from mySchema.NOTIFY_TEST", con);
con.Open();
OracleDependency.Port = 1005;
dep = new OracleDependency(cmd);
dep.OnChange += OnMyNotificaton;
int queryRegistered = cmd.ExecuteNonQuery();
// If oracle returns -1, then the query is successfully registered
if (queryRegistered == -1)
{
Console.WriteLine("Query Registered...");
Console.WriteLine("Listening for Callback...");
}
else
{
Console.WriteLine("There was an error...");
}
}
}
catch (Exception e)
{
Console.WriteLine(e.Message);
}
// Loop while waiting for notification
while (IsNotified == false)
{
Thread.Sleep(100);
}
}
public static void OnMyNotificaton(object sender, OracleNotificationEventArgs arg)
{
Console.WriteLine("Table change notification event is raised");
Console.WriteLine(arg.Source.ToString());
Console.WriteLine(arg.Info.ToString());
Console.WriteLine(arg.Source.ToString());
Console.WriteLine(arg.Type.ToString());
IsNotified = true;
}
}
}
Just wanted to provide an update as to how I resolved this issue. I changed my Oracle.DataAccess.dll from v.2.112.3.0 to v.2.112.1.2 and it works fine.
In SYS.CHNF$_REG_INFO attribute QOSFLAGS there is QOS_DEREG_NFY, which specifies that the database should unregister the registration on the first notification.
Not sure but the value on job_queue_processes (1) is a bit low. Oracle performs all kinds of maintenance and event handling tasks internally. For this they also use Job slaves. Raise job_queue_processes (default 1000) and check Troubleshooting CQN Registrations
Related
This is my first project using the NetMQ (ZMQ) framework, so, maybe I didn't understand how to use it exactly.
I create a Windows Forms project with two applications, one send a "ping" to the other and receives a "pong" as an answer. The protocol is not so complex and uses the Request-Reply pattern, all the commands have a first part that identifies the objective, like "query" or "inform", and a second part that contains the command or the message itself. In this case one app send a "query-ping" and the other answer with "inform-pong".
I create a class to encapsulate all the dirty job, so the main form can use the protocol in a very simple way. Everything is working fine, but when I try to close the app, it gets stuck in the poller and the app never closes. In Visual Studio I can see the pause and stop button but I don't get any exception or errors:
and when I click in pause button I get this message (The application is in break mode):
If I click in 'Continue execution' the app back to same state and never closes.
If I remove the poller the app closes normally, but of course, the poller doesn't work and the app doesn't answer anymore.
This is the code from the Form1:
using CommomLib;
using System;
using System.Windows.Forms;
namespace Test1
{
public partial class Form1 : Form
{
ZmqCommunication zmqComm = new ZmqCommunication();
int portNumber;
string status;
public Form1()
{
InitializeComponent();
InitializeZmqComm();
}
public void InitializeZmqComm()
{
// Calls the ZMQ initialization.
portNumber = zmqComm.InitializeComm();
if (portNumber == 0)
status = "Ini error";
else
status = "Ini ok";
}
// Executes a ping command.
private void button1_Click(object sender, EventArgs e)
{
richTextBox1.Clear();
richTextBox1.AppendText(zmqComm.RequestPing(55001) + "\n");
}
}
}
And this is the code from my NetMQ class. It is in a separated library project. In my Dispose method I tried all combinations of Remove, Dispose and StopAsync and nothing works:
using NetMQ;
using NetMQ.Sockets;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Threading;
namespace CommomLib
{
public class ZmqCommunication
{
ResponseSocket serverComm = new ResponseSocket();
NetMQPoller serverPoller;
int _portNumber;
public ZmqCommunication()
{
_portNumber = 55000;
}
// Problem here! The serverPoller gets stuck.
public void Dispose()
{
//serverPoller.RemoveAndDispose(serverComm);
//serverComm.Dispose();
if (serverPoller.IsDisposed)
Debug.WriteLine("A");
//serverPoller.RemoveAndDispose(serverComm);
serverPoller.Remove(serverComm);
//serverPoller.StopAsync();
serverPoller.Dispose();
serverComm.Dispose();
if (serverPoller.IsDisposed)
Debug.WriteLine("B");
Thread.Sleep(500);
if (serverPoller.IsDisposed)
Debug.WriteLine("C");
Thread.Sleep(500);
if (serverPoller.IsDisposed)
Debug.WriteLine("D");
}
// ZMQ initialization.
public int InitializeComm()
{
bool ok = true;
bool tryAgain = true;
// Looks for a port.
while (tryAgain && ok)
{
try
{
serverComm.Bind("tcp://127.0.0.1:" + _portNumber);
tryAgain = false;
}
catch (NetMQ.AddressAlreadyInUseException)
{
_portNumber++;
tryAgain = true;
}
catch
{
ok = false;
}
}
if (!ok)
return 0; // Error.
// Set up the pooler.
serverPoller = new NetMQPoller { serverComm };
serverComm.ReceiveReady += (s, a) =>
{
RequestInterpreter();
};
// start polling (on this thread)
serverPoller.RunAsync();
return _portNumber;
}
// Message interpreter.
private void RequestInterpreter()
{
List<string> message = new List<string>();
if (serverComm.TryReceiveMultipartStrings(ref message, 2))
{
if (message[0].Contains("query"))
{
// Received the command "ping" and answers with a "pong".
if (message[1].Contains("ping"))
{
serverComm.SendMoreFrame("inform").SendFrame("pong");
}
}
}
}
// Send the command "ping".
public string RequestPing(int port)
{
using (var requester = new RequestSocket())
{
Debug.WriteLine("Running request port {0}", port);
requester.Connect("tcp://127.0.0.1:" + port);
List<string> msgResp = new List<string>();
requester.SendMoreFrame("query").SendFrame("ping");
if (requester.TryReceiveMultipartStrings(new TimeSpan(0, 0, 10), ref msgResp, 2))
{
if (msgResp[0].Contains("inform"))
{
return msgResp[1];
}
}
}
return "error";
}
}
}
Can you try to call NetMQConfig.Cleanup(); in window close event?
Place a breakpoint and see if you even get to ZmqCommunication.Dispose - that might be the issue - the form class not disposing the ZmqCommunication class.
Thanks guys for the answers, they were not the solution but pointed me in the right direction.
After a lot of debugging, I figured out it was a silly problem. I have two programs almost identical (I had both opened at the same time), one of them had the method Dispose() and the other no. During the debugging, in the breaking point, I thought I was in one program but I was in the other one. Really silly.
I have a SQL Server 2008 database with two tables, a .NET 4.5 application using a TransactionScope with two queries to the database, each using their own separate connection which shares a single connection string.
This code that works fine:
internal static void Main(string[] args)
{
string connStr = string.Format(
"Data Source={0};Initial Catalog={1};" +
"Integrated Security=True", "My DB Server", "My Database");
using (var scope = new TransactionScope())
{
string query1 = "(actual query not relevant)";
var ds = GetSqlServerDataSet(query1, connStr);
string query2 = "(actual query not relevant)";
var ds1 = GetSqlServerDataSet(query2, connStr);
scope.Complete();
}
}
private static System.Data.DataSet GetSqlServerDataSet(string usingQuery,
string usingConnectionString,
params System.Data.SqlClient.SqlParameter[] withParameters)
{
using (var ds = new System.Data.DataSet())
using (var conn = new System.Data.SqlClient.SqlConnection(usingConnectionString))
using (var command = new System.Data.SqlClient.SqlCommand(usingQuery, conn))
{
command.Parameters.AddRange(withParameters);
using (var adapter = new System.Data.SqlClient.SqlDataAdapter(command))
{
adapter.Fill(ds);
}
return ds;
}
}
Now, if I throw an exception inside the scope to test roll-back, that's when stuff gets strange. I'm using a referenced class library I've written to persist info about exceptions to a database. It's the same type of setup -- same SQL Server, .NET version, identical ADO.NET code, etc. It's just writing to a different database.
Here's how it works: I've added this method to my app:
private static void HandleUnhandledException(Object sender, System.UnhandledExceptionEventArgs e)
{
ExceptionHandling.Core.Main.ProcessException((Exception) e.ExceptionObject);
Environment.Exit(0);
}
and I've added this line to the top of my Main method:
AppDomain.CurrentDomain.UnhandledException += HandleUnhandledException;
Now when I throw an exception, e.g. throw new Exception("blah"); at the bottom of Main right before scope.Complete(), it automatically jumps to HandleUnhandledException, and the rest happens as I've described above. This results in System.Transactions.TransactionManagerCommunicationException with message:
Network access for Distributed Transaction Manager (MSDTC) has been
disabled. Please enable DTC for network access in the security
configuration for MSDTC using the Component Services Administrative
tool.
This happens on the call to Connection.Open() in my class library.
So what's happening here? I know what the error is telling me, and I know how to fix it. My question is why is this happening in the first place? I thought the using statement would take care of rolling back the transaction before it hits my HandleUnhandledException method, so there shouldn't be two transactions involved here. Or am I wrong?
It happens because when your HandleUnhandledException method runs - you are still inside using block and it's finally part has not been run yet. It's easy to verify with this code:
class Program {
static void Main(string[] args) {
AppDomain.CurrentDomain.UnhandledException += OnUnhandledException;
try {
throw new Exception("test");
}
finally {
Console.WriteLine("finally");
}
}
private static void OnUnhandledException(object sender, UnhandledExceptionEventArgs e) {
Console.WriteLine("handle exception");
}
}
Which outputs:
handle exception
Unhandled exception: System.Exception: test ...
finally
You may want to read this question (and comments, from Eric Lipper for example) for discussion of related behavior. But I think reasons are not very relevant to your question - we can say it just works like this.
Given that information it's clear why you observe your exception - you are still inside uncommited transaction scope and trying to open connection to different database.
One solution that comes to mind in your case is check if we are inside transaction and suppress it if yes (because you anyway don't want your logging code to affect ambient transaction in any way):
private static void HandleException(Exception ex) {
TransactionScope scope = null;
if (Transaction.Current != null)
scope = new TransactionScope(TransactionScopeOption.Suppress);
try {
// do stuff
}
finally {
scope?.Dispose();
}
}
Or maybe just always run inside supressed TransactionScope.
I'm trying to create a web app which does many things but the one that I'm currently focused in is the inbox count. I want to use EWS StreamSubscription so that I can get notification for each event and returns the total count of items in the inbox. How can I use this in terms of MVC? I did find some code from Microsoft tutorial that I was gonna test, but I just couldn't figure how I could use it in MVC world i.e. What's the model going to be, if model is the count then how does it get notified every time an event occurs in Exchange Server, etc.
Here's the code I downloaded from Microsoft, but just couldn't understand how I can convert the count to json and push it to client as soon as a new change event occurs. NOTE: This code is unchanged, so it doesn't return count, yet.
using System;
using System.Linq;
using System.Net;
using System.Threading;
using Microsoft.Exchange.WebServices.Data;
namespace StreamingNotificationsSample
{
internal class Program
{
private static AutoResetEvent _Signal;
private static ExchangeService _ExchangeService;
private static string _SynchronizationState;
private static Thread _BackroundSyncThread;
private static StreamingSubscriptionConnection CreateStreamingSubscription(ExchangeService service,
StreamingSubscription subscription)
{
var connection = new StreamingSubscriptionConnection(service, 30);
connection.AddSubscription(subscription);
connection.OnNotificationEvent += OnNotificationEvent;
connection.OnSubscriptionError += OnSubscriptionError;
connection.OnDisconnect += OnDisconnect;
connection.Open();
return connection;
}
private static void SynchronizeChangesPeriodically()
{
while (true)
{
try
{
// Get all changes from the server and process them according to the business
// rules.
SynchronizeChanges(new FolderId(WellKnownFolderName.Inbox));
}
catch (Exception ex)
{
Console.WriteLine("Failed to synchronize items. Error: {0}", ex);
}
// Since the SyncFolderItems operation is a
// rather expensive operation, only do this every 10 minutes
Thread.Sleep(TimeSpan.FromMinutes(10));
}
}
public static void SynchronizeChanges(FolderId folderId)
{
bool moreChangesAvailable;
do
{
Console.WriteLine("Synchronizing changes...");
// Get all changes since the last call. The synchronization cookie is stored in the _SynchronizationState field.
// Only the the ids are requested. Additional properties should be fetched via GetItem calls.
var changes = _ExchangeService.SyncFolderItems(folderId, PropertySet.IdOnly, null, 512,
SyncFolderItemsScope.NormalItems, _SynchronizationState);
// Update the synchronization cookie
_SynchronizationState = changes.SyncState;
// Process all changes
foreach (var itemChange in changes)
{
// This example just prints the ChangeType and ItemId to the console
// LOB application would apply business rules to each item.
Console.Out.WriteLine("ChangeType = {0}", itemChange.ChangeType);
Console.Out.WriteLine("ChangeType = {0}", itemChange.ItemId);
}
// If more changes are available, issue additional SyncFolderItems requests.
moreChangesAvailable = changes.MoreChangesAvailable;
} while (moreChangesAvailable);
}
public static void Main(string[] args)
{
// Create new exchange service binding
// Important point: Specify Exchange 2010 with SP1 as the requested version.
_ExchangeService = new ExchangeService(ExchangeVersion.Exchange2010_SP1)
{
Credentials = new NetworkCredential("user", "password"),
Url = new Uri("URL to the Exchange Web Services")
};
// Process all items in the folder on a background-thread.
// A real-world LOB application would retrieve the last synchronization state first
// and write it to the _SynchronizationState field.
_BackroundSyncThread = new Thread(SynchronizeChangesPeriodically);
_BackroundSyncThread.Start();
// Create a new subscription
var subscription = _ExchangeService.SubscribeToStreamingNotifications(new FolderId[] {WellKnownFolderName.Inbox},
EventType.NewMail);
// Create new streaming notification conection
var connection = CreateStreamingSubscription(_ExchangeService, subscription);
Console.Out.WriteLine("Subscription created.");
_Signal = new AutoResetEvent(false);
// Wait for the application to exit
_Signal.WaitOne();
// Finally, unsubscribe from the Exchange server
subscription.Unsubscribe();
// Close the connection
connection.Close();
}
private static void OnDisconnect(object sender, SubscriptionErrorEventArgs args)
{
// Cast the sender as a StreamingSubscriptionConnection object.
var connection = (StreamingSubscriptionConnection) sender;
// Ask the user if they want to reconnect or close the subscription.
Console.WriteLine("The connection has been aborted; probably because it timed out.");
Console.WriteLine("Do you want to reconnect to the subscription? Y/N");
while (true)
{
var keyInfo = Console.ReadKey(true);
{
switch (keyInfo.Key)
{
case ConsoleKey.Y:
// Reconnect the connection
connection.Open();
Console.WriteLine("Connection has been reopened.");
break;
case ConsoleKey.N:
// Signal the main thread to exit.
Console.WriteLine("Terminating.");
_Signal.Set();
break;
}
}
}
}
private static void OnNotificationEvent(object sender, NotificationEventArgs args)
{
// Extract the item ids for all NewMail Events in the list.
var newMails = from e in args.Events.OfType<ItemEvent>()
where e.EventType == EventType.NewMail
select e.ItemId;
// Note: For the sake of simplicity, error handling is ommited here.
// Just assume everything went fine
var response = _ExchangeService.BindToItems(newMails,
new PropertySet(BasePropertySet.IdOnly, ItemSchema.DateTimeReceived,
ItemSchema.Subject));
var items = response.Select(itemResponse => itemResponse.Item);
foreach (var item in items)
{
Console.Out.WriteLine("A new mail has been created. Received on {0}", item.DateTimeReceived);
Console.Out.WriteLine("Subject: {0}", item.Subject);
}
}
private static void OnSubscriptionError(object sender, SubscriptionErrorEventArgs args)
{
// Handle error conditions.
var e = args.Exception;
Console.Out.WriteLine("The following error occured:");
Console.Out.WriteLine(e.ToString());
Console.Out.WriteLine();
}
}
}
I just want to understand the basic concept as in what can be model, and where can I use other functions.
Your problem is that you are confusing a service (EWS) with your applications model. They are two different things. Your model is entirely in your control, and you can do whatever you want with it. EWS is outside of your control, and is merely a service you call to get data.
In your controller, you call the EWS service and get the count. Then you populate your model with that count, then in your view, you render that model property. It's really that simple.
A web page has no state. It doesn't get notified when things change. You just reload the page and get whatever the current state is (ie, whatever the current count is).
In more advanced applications, like Single Page Apps, with Ajax, you might periodically query the service in the background. Or, you might have a special notification service that uses something like SignalR to notify your SPA of a change, but these concepts are far more advanced than you currently are. You should probably develop your app as a simple stateless app first, then improve it to add ajax functionality or what not once you have a better grasp of things.
That's a very broad question without a clear-cut answer. Your model could certainly have a "Count" property that you could update. The sample code you found would likely be used by your controller.
I have created one project in windows application and I provided multiple users accessibility for the project but I need restrict it to only 3 users accessing it at a time. For that purpose I have added column status (bit), for every login I made status=true and log out status=false but if I ended my project from end task and system shutdown then I'm getting problem in counting logged users.
Is it possible to find active users and there status? If it is possible then I'll run trigger every 5 minutes and update the status fields accordingly.
OK, so since this is a Windows application, and you haven't provided any code, I'd do something like this when they login:
// connect to the database
using (SqlConnection c = new SqlConnection("connection string"))
{
// count the logged in users
var loggedInUsers = (int)new SqlCommand("SELECT COUNT(UserId) FROM Users WHERE status = 1", c).ExecuteScalar();
// if the threshold has been met then shut down the application
if (loggedInUsers == 3)
{
MessageBox.Show("There are already 3 concurrent users logged into the system -please try again at a later time.", "User Limit Met", MessageBoxButtons.OK, MessageBoxIcon.Information);
Application.Exit();
}
}
but you also have to do this when the application shuts down, like in the OnClosing method of the main form:
protected override void OnClosing(CancelEventArgs e)
{
// connect to the database
using (SqlConnection c = new SqlConnection("connection string"))
{
// log the user out
var cmd = new SqlCommand("UPDATE Users SET status = 0 WHERE UserId = #UserId", c);
cmd.Parameters.AddWithValue("#UserId", // get your user id from somewhere
var rowsAffected = cmd.ExecuteNonQuery();
// make sure the update worked
if (rowsAffected == 0)
{
// do something here to make sure they get logged out
}
}
}
EDIT
To handle the edge case of an application failure during a task manager quit for example you should be able to leverage Application.Exit, so in your startup method (i.e. where Application.Run is called) put this line before that:
Application.ApplicationExit += new EventHandler(Application_ApplicationExit);
and then consume it:
private void Application_ApplicationExit(object sender, EventArgs e)
{
// and now you can probably just move all of the code here
// instead of the OnClosing
}
I have a C# program that queries the SQL Server database for some values.
Currently the application queries the database every minutes to make sure that the table is up to date.
What I would like to be able to do is that the query is only done when the database has been changed / updated. How do I notify my program when something has been updated in the database?
Thanks
Polling database is not very elegant solution.
SqlDependency from ADO.NET will be useful in your case. It does not use polling but notification mechanism. The notifications are provided by Service Broker in your database, so will need to enable this service in your databse. The OnChange event will raise when specified table changes(update, delete, insert..)
Here is an example how to use SqlDependency:
void Initialization()
{
// Create a dependency connection.
SqlDependency.Start(connectionString, queueName);
}
void SomeMethod()
{
// Assume connection is an open SqlConnection.
// Create a new SqlCommand object.
using (SqlCommand command=new SqlCommand(
"SELECT ShipperID, CompanyName, Phone FROM dbo.Shippers",
connection))
{
// Create a dependency and associate it with the SqlCommand.
SqlDependency dependency=new SqlDependency(command);
// Maintain the refence in a class member.
// Subscribe to the SqlDependency event.
dependency.OnChange+=new
OnChangeEventHandler(OnDependencyChange);
// Execute the command.
using (SqlDataReader reader = command.ExecuteReader())
{
// Process the DataReader.
}
}
}
// Handler method
void OnDependencyChange(object sender,
SqlNotificationEventArgs e )
{
// Handle the event (for example, invalidate this cache entry).
}
void Termination()
{
// Release the dependency.
SqlDependency.Stop(connectionString, queueName);
}
from http://msdn.microsoft.com/en-us/library/62xk7953.aspx
Here is how to enable Service Broker(note that you will have exclusiveness on the database to do that - best do it after restart of the sql server):
http://blogs.sftsrc.com/stuart/archive/2007/06/13/42.aspx(Broken link)
Possible alternative link: http://technet.microsoft.com/en-us/library/ms166086(v=sql.105).aspx
If you are on SQL Server 2005 and above, you can consider using the SqlDependency object.
It represents a query notification dependency between an application and an instance of SQL Server 2005.
An application can create a SqlDependency object and register to receive notifications via the OnChangeEventHandler event handler.
Refer this link on MSDN for more information
However, do note the caveat that MS puts against its use. It is advised to have a caching layer and then use SQLDependency in coordination with that layer .
SqlDependency was designed to be used in ASP.NET or middle-tier services where there is a relatively small number of servers having dependencies active against the database. It was not designed for use in client applications, where hundreds or thousands of client computers would have SqlDependency objects set up for a single database server.
To get a notify when some record is updated, avoid the application to query the table you cab use TableDependency component (in your specific case SqlTableDependency). Here is an example:
public partial class Window1 : Window
{
private IList<Stock> _stocks;
private readonly string _connectionString =
"data source=.;initial catalog=myDB;integrated security=True";
private readonly SqlTableDependency<Stock> _dependency;
public Window1()
{
this.InitializeComponent();
this.McDataGrid.ItemsSource = LoadCollectionData();
this.Closing += Window1_Closing;
var mapper = new ModelToTableMapper<Stock>();
mapper.AddMapping(model => model.Symbol, "Code");
_dependency = new SqlTableDependency<Stock>(_connectionString, "Stocks", mapper);
_dependency.OnChanged += _dependency_OnChanged;
_dependency.OnError += _dependency_OnError;
_dependency.Start();
}
private void Window1_Closing(object sender, System.ComponentModel.CancelEventArgs e)
{
_dependency.Stop();
}
private void _dependency_OnError(object sender, TableDependency.EventArgs.ErrorEventArgs e)
{
throw e.Error;
}
private void _dependency_OnChanged(
object sender,
TableDependency.EventArgs.RecordChangedEventArgs<Stock> e)
{
if (_stocks != null)
{
if (e.ChangeType != ChangeType.None)
{
switch (e.ChangeType)
{
case ChangeType.Delete:
_stocks.Remove(_stocks.FirstOrDefault(c => c.Symbol == e.Entity.Symbol));
break;
case ChangeType.Insert:
_stocks.Add(e.Entity);
break;
case ChangeType.Update:
var customerIndex = _stocks.IndexOf(
_stocks.FirstOrDefault(c => c.Symbol == e.Entity.Symbol));
if (customerIndex >= 0) _stocks[customerIndex] = e.Entity;
break;
}
this.McDataGrid.Dispatcher.Invoke(DispatcherPriority.Background, new Action(() =>
{
this.McDataGrid.Items.Refresh();
}));
}
}
}
private IEnumerable<Stock> LoadCollectionData()
{
_stocks = new List<Stock>();
using (var sqlConnection = new SqlConnection(_connectionString))
{
sqlConnection.Open();
using (var sqlCommand = sqlConnection.CreateCommand())
{
sqlCommand.CommandText = "SELECT * FROM [Stocks]";
using (var sqlDataReader = sqlCommand.ExecuteReader())
{
while (sqlDataReader.Read())
{
var code = sqlDataReader
.GetString(sqlDataReader.GetOrdinal("Code"));
var name = sqlDataReader
.GetString(sqlDataReader.GetOrdinal("Name"));
var price = sqlDataReader
.GetDecimal(sqlDataReader.GetOrdinal("Price"));
_stocks.Add(new Stock { Symbol = code, Name = name, Price = price });
}
}
}
}
return _stocks;
}
The event handler is triggered for every INSERT UPDATE or DELETE operation done on the table, reporting you the modified value. So, in case you are interested to keep your C# Datatable up to date, you simple can get the fresh data from the event handler.
What I would like to be able to do is that the query is only done when the database has been changed/updated.How do i notify my program when some thing updated in database.
There isn't any means of the database pushing notifications to the application. The application needs to poll the database to check for updates, and then deal with the updates appropriately.
If by "updates to the database" you mean any update by any application, you're out of luck: it's not doable.
If, however, you mean changes made by your app, it's easy: every time you update the DB raise and event and have handlers respond to the event.