I was trying to make an application which can popup a notify message whenever a new row is added to the database.i was using mysql and changed the default engine to csv engine so that i can use the FileSystemWatcher to detect any changes.The filesystemwatcher is triggering whenever a row is deleted but the problem is its not triggering changed event when a new row is added to the database.I also observed that when a row is deleted the "Date Modified" is changing but when I add a new row its not updating. Please help me.
private void button1_Click(object sender, EventArgs e)
{
FileSystemWatcher fsw = new FileSystemWatcher();
fsw.Path = "C:\\xampp\\mysql\\data\\doubts\\";
fsw.EnableRaisingEvents = true;
fsw.Changed += new FileSystemEventHandler(func);
}
private void func(Object obj,FileSystemEventArgs e)
{
notifyIcon1.Icon = SystemIcons.Application;
notifyIcon1.BalloonTipText =
"Addition of new row to the database detected...";
notifyIcon1.ShowBalloonTip(4000);
}
I would recommend that you poll the database periodically to check for changed records.
If your query is expensive, you could create an AFTER INSERT or AFTER UPDATE trigger which inserts into another table, then poll that table.
If this still is not sufficient, you can do this: From your application, run a command like
SELECT "WaitForChanges", SLEEP(999);
Create a trigger which kills this select via the KILL QUERY command: How to do this from a stored procedure.
Related
I have used NUGET to install the Sqlite Core package into my c# project using:
>Install-Package System.Data.SQLite.Core
I create a database connection as follows:
var data = new SQLiteConnection(connectionString);
I then hook an event handler to the update event which fires every time that an update statement occurs (for the purposes of a last write date field for a particular piece of business logic)
data.Update += DataOnUpdate;
This is all awesome. However the SqliteConnection class also exposes an event called Trace The documentation says the following about this event:
"This event is raised whenever SQLite Statement First begins executing on this connection. It only applies for the given connection"
I read this to mean that it performs a similar function to the Update event whereby it should fire whenever an SQL statement is being executed.
HOWEVER
When I hook this event up as follows:
data.Trace += DataOnTrace;
It never fires. I have tried SELECT, UPDATE, DELETE, CREATE TABLE, TRANSACTIONS and basically every bit of Sql logic that I can think of and it refuses to fire.
What is this event there for if not to fire? or is there something I need to do to get the connection to fire this event?
I downloaded the System.Data.SQLite package and wrote the following code. The trace event seems to fire OK for me.
Given a SQLite database containing a table called "tbl1" (schema unimportant)
static void Main(string[] args)
{
using (SQLiteConnection conn = new SQLiteConnection(#"Data Source=C:\dev\Sandbox\Sandbox.Console\test.db;Version=3;"))
{
conn.Open();
conn.Trace += conn_Trace;
using(SQLiteCommand cmd = new SQLiteCommand("Select * from tbl1", conn))
{
using (SQLiteDataAdapter da = new SQLiteDataAdapter(cmd))
{
DataSet ds = new DataSet();
da.Fill(ds);
}
}
conn.Trace -= conn_Trace;
conn.Close();
}
}
static void conn_Trace(object sender, TraceEventArgs e)
{
System.Console.WriteLine(e.Statement);
}
i have data grid view that its data source is a data table in form in c#,
how can i make it read table from database continuously
i mean if my program running in many computers in same network and connected with same database , if computer 1 add row to the database its appears automatically in computer 2 without clicking any button to refresh.
void load()
{
c.connect("sel_dep");
c.com.CommandType = CommandType.StoredProcedure;
SqlDataAdapter da=new SqlDataAdapter (c.com);
DataTable dt = new DataTable();
c.open();
int last = 0;
while (true)
{
if (dt.Rows.Count > 0)
dt.Rows.Clear();
da.Fill(dt);
dd = dt;
if (dt.Rows.Count != last)
{
last = dt.Rows.Count;
this.Invoke((MethodInvoker)delegate { dataGridView1.DataSource = dt; dataGridView1.SelectedRows[0].Selected = true; label1.Text = dataGridView1.RowCount.ToString(); });
}
}
c.close();
}
private void Form3_Load(object sender, EventArgs e)
{
aa = new Thread(() => { load(); });
aa.Start();
}
this is my tray
If you are using Winforms to create the desktop application, you would not need timer.
Simply drag and drop Timer control from the Toolbox on your form, in design mode.
' The form load should be altered as below
private void Form3_Load(object sender, EventArgs e)
{
' Assume the Timer control is named as 'oTimer'
oTimer.Enabled = true;
oTimer.Interval = 60000; ' In Milliseconds, equates to 1 minute
oTimer.Start();
}
Create the Tick event for the Timer. This event would be fired each time the interval elapses.
private void oTimer_Tick(object sender, EventArgs e)
{
<Call your function to initiate/refresh the DataGrid.DataSource within this event>
}
In order to further understand how the Timer class works, refer Timer Class (System.Windows.Forms).
Also refer to Stackoverlow question Winforms Timer for Dummies with plenty of resources and tips to master the Timer control.
If you're using Sql Server you can have a look at Query Notifications, more specifically at SqlDependency class. It can be used to get notifications from sql server, when data changes, into your desktop application
Built upon the Service Broker infrastructure, query notifications
allow applications to be notified when data has changed. This feature
is particularly useful for applications that provide a cache of
information from a database, such as a Web application, and need to be
notified when the source data is changed.
I have a csv file and I would like to make some actions whenever a new row is inserted to this csv file.
is there such a listener in c#?
Thanks a lot
There is a class FileSystemWatcher to inspect the file changes.
FileSystemWatcher MSDN. in your case, you just need to filter with "*.csv".
No, there is not specifically a listener for a row being added, but there is such a thing, in .Net, as a FileSystemWatcher, which is a class that can monitor a file for changes. With it, you can react to a specific set of changes to the file that you choose, but what happens is completely up to you.
You can use the FileSystemWatcher to watch an arbitrary file for change events:
public MainWindow()
{
InitializeComponent();
FileSystemWatcher fsw = new FileSystemWatcher();
fsw.Filter = "test1.csv";
fsw.NotifyFilter = NotifyFilters.LastWrite;
fsw.Path = "z:\\temp\\";
fsw.Changed += Fsw_Changed;
fsw.EnableRaisingEvents = true;
}
private void Fsw_Changed(object sender, FileSystemEventArgs e)
{
MessageBox.Show(e.FullPath);
}
I've got a table and a SqlDependency that is waiting for new inserts.
OnChange fires as I need, but I don't understand if it's possible to get the row which cause the databse change.
SqlDependency sql command:
SqlCommand cmd = new SqlCommand("SELECT id FROM dbo.DataRequests", m_sqlConn);
OnChange code:
private void OnChange(object sender, SqlNotificationEventArgs e)
{
SqlDependency dependency = sender as SqlDependency;
dependency.OnChange -= OnChange;
Console.WriteLine("Info: " + e.Info.ToString());
Console.WriteLine("Source: " + e.Source.ToString());
Console.WriteLine("Type: " + e.Type.ToString());
Console.WriteLine(DateTime.Now);
GetMessages();
}
No information is available about the rows that caused the dependency to be fired.
I guess as a workaround you could always put a timestamp on your records and track when the event was last fired.
Take a look at this component:
SqlTableDependency
For every change done on a SQL Server database table, the C# code receive an event containing a list of RECORDs changed.
Find a very ingenious solution here
According to this post, you can't: http://social.msdn.microsoft.com/Forums/en-US/sqlservicebroker/thread/07234067-73e1-4db5-a4e6-0f9f0bae22ae/
You can only narrow down the reason for the notification by using the properties
Source
Type
Info
of the provided SqlNotificationEventArgs
Just use a cross-platform .NET 3.5 compatible and open-source SqlDependencyEx. It uses a database trigger and native Service Broker notification to receive events about the table changes. This is a usage example:
int changesReceived = 0;
using (SqlDependencyEx sqlDependency = new SqlDependencyEx(
TEST_CONNECTION_STRING, TEST_DATABASE_NAME, TEST_TABLE_NAME))
{
sqlDependency.TableChanged += (o, e) => changesReceived++;
sqlDependency.Start();
// Make table changes.
MakeTableInsertDeleteChanges(changesCount);
// Wait a little bit to receive all changes.
Thread.Sleep(1000);
}
Assert.AreEqual(changesCount, changesReceived);
You can get change notifications as well as information which was changed. Please follow the link for details.
I hope this helps you:
string commandString = string.Format("SELECT [Id] FROM [dbo].[Tech]");
command = new SqlCommand(commandString, connection);
private void dependency_OnChange(object sender, SqlNotificationEventArgs e)
{
SqlDependency dependency = (SqlDependency)sender;
dependency.OnChange -= dependency_OnChange;
this.Dispatcher.Invoke((System.Action)(() =>
{
if (e.Info.ToString().ToLower().Trim() == "insert")
{
GetData();
int NewTechID = TechIDs.Last();
}
}));
}
private void GetData()
{
command.Notification = null;
SqlDependency dependency = new SqlDependency(command);
dependency.OnChange += new OnChangeEventHandler(dependency_OnChange);
command.Connection.Open();
using (SqlDataReader reader = command.ExecuteReader())
{
while (reader.Read())
{
TechIDs.add(int.Parse(reader.GetValue(0).ToString()));
}
reader.Close();
}
command.Connection.Close();
}
You can use Temp tables.
first of all you need to create a temp table with all the fields you need to keep under investigation. something like:
CREATE TABLE ##TempTab(
[field1] [varchar](50) NULL,
[field2] [varchar](50) NULL
}
Please note that kind of tables created within external cose are automatically dropped since the creator program quits so you don't need to drop it on formClosing...
Now, after setting up sqlDepency stuffs you have to fill up you temp table, it's something like a snapshot of the starting scenario.
Then, every time the onChange event is fired you just need to compare your temp table with updated situation. it could be something like:
select * from ##temptable left outer join mytable
ON ##temptable.field1=myTable.field1 AND ##temptable.field2=myTable.field2
WHERE myTable.field2 is null
this will give you all rows has just been deleted (or chagend with old values).
On the other side:
select * from mytable left outer join ##temptable
ON ##temptable.field1=myTable.field1 AND ##temptable.field2=myTable.field2
WHERE ##temptable.field2 is null
will give you all rows has just been added (or changed with new values).
After this compare you just need to update your temp table with new values (faster way is to delete everything and insert all values)
Of course, if your programm will be run simultaneously by different users, you'll need to handle userid within temp table.
New to entity framework, but I would think this should be pretty simple.
My form load creates a context from my Entities. I create a list of clients and have a binding source that I assign clients to. The binding source is assigned to a Binding Navigator - clientBindingNavigator.
private void ClientExtForm_Load (object sender, EventArgs e)
{
_context = new IDVisitorEntities ();
List<IDVM.Client> clients = _context.Clients.ToList ();
clientBindingSource.DataSource = clients;
}
excerpt from ClientExtForm.Designer.cs
//
// clientBindingNavigator
//
this.clientBindingNavigator.AddNewItem = this.bindingNavigatorAddNewItem;
this.clientBindingNavigator.BindingSource = this.clientBindingSource;
this.clientBindingNavigator.CountItem = this.bindingNavigatorCountItem;
this.clientBindingNavigator.DeleteItem = this.bindingNavigatorDeleteItem;
this.clientBindingNavigator.Items.AddRange(new System.Windows.Forms.ToolStripItem[] {
this.bindingNavigatorMoveFirstItem,
this.bindingNavigatorMovePreviousItem,
this.bindingNavigatorSeparator,
this.bindingNavigatorPositionItem,
this.bindingNavigatorCountItem,
this.bindingNavigatorSeparator1,
this.bindingNavigatorMoveNextItem,
this.bindingNavigatorMoveLastItem,
this.bindingNavigatorSeparator2,
this.bindingNavigatorAddNewItem,
this.bindingNavigatorDeleteItem,
this.clientBindingNavigatorSaveItem});
When I click the Delete Button on the navigator tool bar the the ClientBindingSource.Count has been reduced by 1 .
private void clientBindingNavigatorSaveItem_Click (object sender, EventArgs e)
{
this.OnSave ();
}
public override void OnSave ()
{
foreach (ObjectStateEntry entry in _context.ObjectStateManager.GetObjectStateEntries (EntityState.Deleted))
{
// nothing shows up in this
}
foreach (ObjectStateEntry entry in _context.ObjectStateManager.GetObjectStateEntries (EntityState.Modified))
{
// when modified
}
foreach (ObjectStateEntry entry in _context.ObjectStateManager.GetObjectStateEntries (EntityState.Added))
{
// when adding this finds it
}
clientBindingSource.EndEdit ();
visitorHostsBindingSource.EndEdit ();
_context.SaveChanges ();
base.OnSave ();
}
It appears as though the navigator is removing the item from the collection.
Added info: It appears that in the navigator the DeleteItem button corresponds to the RemoveCurrent method (on click event calls it). Not sure how to tie in before the RemoveCurrent does it's thing.
What are my options for preforming the delete?
Removing an item from the clientBindingSource will have no effect on the item at the database level. You have to explicitly call _context.Clients.DeleteObject(deletedClient); You perform must all CRUD operations through the ObjectContext.
After looking around found some blogs that suggest to not use the default DeleteItem.
this.clientBindingNavigator.DeleteItem = null;//= this.bindingNavigatorDeleteItem;
In my case to make it clear for the BindingNavigator I replaced this.bindingNavigatorDeleteItem with a new button this.toolStripButton1 in the Items list.
this.clientBindingNavigator.Items.AddRange(new System.Windows.Forms.ToolStripItem[] {
this.bindingNavigatorMoveFirstItem,
this.bindingNavigatorMovePreviousItem,
this.bindingNavigatorSeparator,
this.bindingNavigatorPositionItem,
this.bindingNavigatorCountItem,
this.bindingNavigatorSeparator1,
this.bindingNavigatorMoveNextItem,
this.bindingNavigatorMoveLastItem,
this.bindingNavigatorSeparator2,
this.bindingNavigatorAddNewItem,
this.toolStripButton1,
this.clientBindingNavigatorSaveItem});
Creation of the new button looks like this:
//
// toolStripButton1
//
this.toolStripButton1.Image = ((System.Drawing.Image) (resources.GetObject ("bindingNavigatorDeleteItem.Image")));
this.toolStripButton1.RightToLeftAutoMirrorImage = true;
this.toolStripButton1.Name = "toolStripDeleteItem";
this.toolStripButton1.Size = new System.Drawing.Size(23, 22);
this.toolStripButton1.Text = "Delete";
this.toolStripButton1.Click += new System.EventHandler(this.toolStripButton1_Click);
The Click event then calls the RemoveCurrent (just like the default does) but I can get the current entity and stash it in an arrraylist for use on save.
private void toolStripButton1_Click (object sender, EventArgs e)
{
var currentclient = (Client) clientBindingSource.Current;
clientstodelete.Add (currentclient);
clientBindingSource.RemoveCurrent ();
}
I didn't need to create a new button, I just needed to have the this.clientBindingNavigator.DeleteItem not tied to a button. Because the DeleteItem creates a click event under the hood that calls the BindingSource.RemoveCurrent(). I might change the button back to the default one created but for illustration wanted everyone to see what was happening.
I agree it seemed odd to have to delete the record from the table directly if I had just deleted using RemoveCurrent(). But it is what it is... This took care of the record in the datagridview and the datasource in one clean sweep.
Here is how I solved the problem:
t_StaffDaysOff sdo = (t_StaffDaysOff)t_StaffDaysOffbindingSource.Current;
t_StaffDaysOffbindingSource.RemoveCurrent();
t_StaffDaysOffbindingSource.EndEdit();
db.t_StaffDaysOff.Remove(sdo);
db.SaveChanges();