SqlDependency Data Flood when using COUNT_BIG() in query - sql-server

FIXED: Code updated that now works.
Trying to setup a websocket for a management dashboard where I need queries using count_big() fields and GROUP BY clauses. Standard recordset lists work great, but once I add the count_big() the websocket doesn't stop sending data. I have read this post about limitations and count_big() appears OK to use. TIA
using Microsoft.Web.WebSockets;
using System;
using System.Collections.Generic;
using System.Configuration;
using System.Data;
using System.Data.SqlClient;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Web;
using System.Web.Http;
namespace DatabaseChangeNotification.Controllers
{
public class DatabaseNotificationController : ApiController
{
public HttpResponseMessage Get()
{
HttpContext.Current.AcceptWebSocketRequest(new ChatWebSocketHandler());
return Request.CreateResponse(HttpStatusCode.SwitchingProtocols);
}
class ChatWebSocketHandler : Microsoft.Web.WebSockets.WebSocketHandler
{
public string wsData = null;
public SqlCommand gblCommand = null;
public ChatWebSocketHandler()
{
SetupNotifier();
}
protected void SetupNotifier()
{
using (var connection = new SqlConnection(ConfigurationManager.ConnectionStrings["DefaultConnection"].ConnectionString))
{
connection.Open();
// DO NOT USE any "*" in queries
// WHen using count the variable was converted to string. Got Data flood
//
// Testing count_big data type
// getString failed
//
using (SqlCommand command = new SqlCommand(#"select [address], count_big(*) as [CurrentTotal] from dbo.users where address = 'main st' group by address", connection))
{
command.Notification = null;
SqlDependency dependency = new SqlDependency(command);
dependency.OnChange += new OnChangeEventHandler(dependency_OnChange);
if (connection.State == ConnectionState.Closed)
{
connection.Open();
}
//SqlCommand gblCommand = command;
wsData = null;
using (SqlDataReader reader = command.ExecuteReader())
{
while (reader.Read())
{
/* MUST MATCH column count and column data type */
// wsData += reader.GetString(0) + " " + reader.GetString(1) + " " + reader.GetString(2);
/* THIS WORKS FOR GETTING NUMERIC VARIABLES */
wsData += reader.GetValue(0) + " " + int.Parse(reader.GetValue(1).ToString());
// wsData += reader.GetString(0) + "</br>"; //works but we get data flood and no numbers
}
// reader.Close();
}
_chatClients.Broadcast("data: " + wsData);
}
}
} //SetupNotifier
private static WebSocketCollection _chatClients = new WebSocketCollection();
public override void OnOpen()
{
_chatClients.Add(this);
} // OnOpen
public override void OnMessage(string msg)
{
} // OnMessage
private void dependency_OnChange(object sender, SqlNotificationEventArgs e)
{
if (e.Type != SqlNotificationType.Change)
{
_chatClients.Broadcast("Returning, not a change notification ");
return;
}
/*
* Must remove dependency. Only works once.
*/
SqlDependency dependency = sender as SqlDependency;
dependency.OnChange -= dependency_OnChange;
//reset for next message.
SetupNotifier();
} // dependency_OnChange
} // ChatWebSocketHandler
} // DatabaseNotificationController
}
NOTE: This was happening before code was fixed.
Web Socket returns infinite listing:
data: main st 1
data: main st 1
data: main st 1
data: main st 1
data: main st 1
data: main st 1
data: main st 1
data: main st 1
data: main st 1
data: main st 1
data: main st 1
data: main st 1
data: main st 1
data: main st 1
data: main st 1
data: main st 1
data: main st 1
data: main st 1
{ .....}

You must check the SqlNotificationEventArgs members. Not all notifications indicate a update. Some notifications (like the ones you're getting) indicate invalid conditions or invalid query. You are getting a notification for invalid query and resubmit, just to be immediately notified for the same reason. Ad-nauseam.
Inspecting the notification would point toward the problem. In your case the problem is listed in the very first bullet point in the link yourself posted:
table names must be qualified with two-part names

Related

SQLite Database is not working after standalone build in Unity3D

I am trying to make a Standalone Application using SQLite in Unity3D,
I am getting a strange problem.
I created a database using sqliteadmin, and created a Table named Admin, having field: id, email, password.
I am able to Login using email and password but in Unity Edit Mode.
Its working fine but when i build it and then run it, its not working, I have no idea why?
Reference
Here is my code:
using UnityEngine;
using System.Collections;
using Mono.Data.Sqlite;
using System.Data;
using System;
using UnityEngine.UI;
public class DatabaseConnection : MonoBehaviour {
public Text em;
public Text pas;
public static int id;
public static string email ="";
public static string password="";
public static string wrong="Wrong Email/Password !!!";
public Text Wrong;
public GameObject loading;
private ButtonsController bc;
public GameObject loginPanel;
void Start () {
string conn = "URI=file:" + Application.dataPath + "/Database/TMDB.s3db";
IDbConnection dbconn;
dbconn = (IDbConnection)new SqliteConnection (conn);
dbconn.Open ();
IDbCommand dbcmd = dbconn.CreateCommand ();
string sqlQuery = "SELECT id, email, password " + "FROM Admin";
dbcmd.CommandText = sqlQuery;
IDataReader reader = dbcmd.ExecuteReader ();
while (reader.Read()) {
id = reader.GetInt32 (0);
email = reader.GetString(1);
password = reader.GetString(2);
}
reader.Close ();
reader = null;
dbcmd.Dispose ();
dbcmd = null;
dbconn.Close ();
dbconn = null;
loading.SetActive (false);
}
public void login()
{
if ((em.text == email) && (pas.text == password)) {
Debug.Log ("Success");
loading.SetActive (true);
loginPanel.SetActive(false);
Application.LoadLevel(1);
} else {
Debug.Log ("Error");
Wrong.text = wrong.ToString ();
}
}
}
Application.datapath is readonly.
What you need is Application.persistentDataPath
Checkout this link
http://answers.unity3d.com/questions/209108/when-to-use-persistentdatapath-versus-datapath.html
Create StreamingAssets folder into your Assets, and use this connection string:
string conn = "URI=file:" +
System.IO.Path.Combine(Application.streamingAssetsPath, "Database/TMDB.s3db");
Using streaming asset is necessary, it places files into the normal filesystem on the target machine to make them accessible via a pathname.
More info:
https://docs.unity3d.com/Manual/StreamingAssets.html
dude ,,, just check the files bro,,, after building the database is empty so go and replace the database file with the one u been working on with the same database name .

using Microsoft.SqlServer.TransactSql.ScriptDom for parse query with errors

I use the following code to get a list of statements in the query:
using System;
using System.Collections.Generic;
using System.IO;
using System.Windows.Forms;
using Microsoft.SqlServer.TransactSql.ScriptDom;
namespace SqlTokenazer
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void Form1_Load(object sender, EventArgs e)
{
Tokenaze();
}
private void Tokenaze()
{
rtbLog.Clear();
string script = "select * from dbo.Mytable where columnName = 0 delete from dbo.Mytable where columnName = 0";
var sqlScript = ParseScript(script);
PrintStatements(sqlScript);
}
public TSqlScript ParseScript(string script){
IList<ParseError> parseErrors;
TSql100Parser tsqlParser = new TSql100Parser(true);
TSqlFragment fragment;
using (StringReader stringReader = new StringReader(script))
{
fragment = (TSqlFragment)tsqlParser.Parse(stringReader, out parseErrors);
}
if (parseErrors.Count > 0)
{
var retMessage = string.Empty;
foreach (var error in parseErrors)
{
retMessage += error.Number + " - " + error.Message + " - position: " + error.Offset + ";\r\n";
}
rtbLog.Text += retMessage;
}
return (TSqlScript)fragment;
}
public void PrintStatements(TSqlScript tsqlScript)
{
if (tsqlScript != null)
{
foreach (TSqlBatch batch in tsqlScript.Batches)
{
if (batch.Statements.Count == 0) continue;
foreach (TSqlStatement statement in batch.Statements)
{
rtbLog.Text += string.Format("{0}\r\n", statement.GetType().ToString());
}
}
}
}
}
}
Results:
Microsoft.SqlServer.TransactSql.ScriptDom.SelectStatement
Microsoft.SqlServer.TransactSql.ScriptDom.DeleteStatement
But when I make a mistake in query, a list of statements is empty :(
string script = "select * from dbo.Mytable where ...
delete from dbo.Mytable where columnName = 0";
how can I get a list of statements, if the query is wrong?
Thanks!
I know this is an old question, but I came across it while Googling so I figured I'd answer it.
If your question is, how to get a list of statements if the SQL can't be parsed, the short answer is that you can't - the parser has no idea what the list of statements would be. You'd have to look at the errors and figure it out.
If your question is, what's wrong with the input code, it's that the select and delete statements are all on the same line. If you separate them with a semicolon or break them into two lines, it'll work and you can get your two statements.

Excel doc contents to webservice

I have a wpf staff creation window in which I can create basic information like first name, last name etc this creates the staff in my REST web service. An example:
Client side:
private void CreateStaffMember_Click(object sender, RoutedEventArgs e)
{
string uri = "http://localhost:8001/Service/Staff";
StringBuilder sb = new StringBuilder();
sb.Append("<Staff>");
sb.AppendLine("<FirstName>" + this.textBox1.Text + "</FirstName>");
sb.AppendLine("<LastName>" + this.textBox2.Text + "</LastName>");
sb.AppendLine("<Password>" + this.passwordBox1.Password + "</Password>");
sb.AppendLine("</Staff>");
string NewStudent = sb.ToString();
byte[] arr = Encoding.UTF8.GetBytes(NewStudent);
HttpWebRequest req = (HttpWebRequest)WebRequest.Create(uri);
req.Method = "POST";
req.ContentType = "application/xml";
req.ContentLength = arr.Length;
Stream reqStrm = req.GetRequestStream();
reqStrm.Write(arr, 0, arr.Length);
reqStrm.Close();
HttpWebResponse resp = (HttpWebResponse)req.GetResponse();
MessageBox.Show("Staff Creation: Status " + resp.StatusDescription);
reqStrm.Close();
resp.Close();
}
Web Service side:
#region POST
[OperationContract]
[WebInvoke(Method = "POST", BodyStyle = WebMessageBodyStyle.Bare, RequestFormat = WebMessageFormat.Xml, ResponseFormat = WebMessageFormat.Xml, UriTemplate = "/Staff")]
void AddStaff(Staff staff);
#endregion
public void AddStaff(Staff staff)
{
staff.StaffID = (++eCount).ToString();
staff.Salt = GenerateSalt();
byte[] passwordHash = Hash(staff.Password, staff.Salt);
staff.Password = Convert.ToBase64String(passwordHash);
staffmembers.Add(staff);
}
All fine on that side, but Im looking to "import" the staff details from an excel spreadsheet, not sure if import is the correct word but I want to take the first names and last names contained in such n such spreadsheet and add them to the web service from the client side wpf application.
How would I go about it? I have my open file dialog:
private void Import_Click(object sender, RoutedEventArgs e)
{
Microsoft.Win32.OpenFileDialog dlg = new Microsoft.Win32.OpenFileDialog();
// Show open file dialog box
Nullable<bool> result = dlg.ShowDialog();
// Process open file dialog box results
if (result == true)
{
// Open document
string filename = dlg.FileName;
}
}
So I open my excel spread sheet then how would I go about taking the inner contents and sending it to the web service? Really stuck on the code or how to go about it :/
Just looking for an automated way of adding staff members rather than manually typing the names, but seeing as the staff excel doc could be named anything I wanted the open file dialog box. The structure inside will always be the same first name then last name.
First, here is my test Excel file that contains the Staff you want to import:
(Column 'A' if first name, column 'B' is last name and column 'C' is the password...)
Ok, so assuming that your code calling your web service works, here is my version of the Import_Click method (and a generic method to save new staff):
private void Import_Click(object sender, RoutedEventArgs e)
{
Microsoft.Win32.OpenFileDialog dlg = new Microsoft.Win32.OpenFileDialog();
// Show open file dialog box
Nullable<bool> result = dlg.ShowDialog();
// Process open file dialog box results
if (result == true)
{
// Open document
string filename = dlg.FileName;
Microsoft.Office.Interop.Excel.Application vExcelObj = new Microsoft.Office.Interop.Excel.Application();
try
{
Workbook theWorkbook = vExcelObj.Workbooks.Open(filename, Type.Missing, true);
Worksheet sheet = theWorkbook.Worksheets[1]; // This is assuming that the list of staff is in the first worksheet
string vFirstName = "temp";
string vLastName = "temp";
string vPassword = "temp";
int vIndex = 1;
while (vFirstName != "")
{
// Change the letters of the appropriate columns here!
// In my example, 'A' is first name, 'B' is last name and 'C' is the password
vFirstName = sheet.get_Range("A" + vIndex.ToString()).Value.ToString();
vLastName = sheet.get_Range("B" + vIndex.ToString()).Value.ToString();
vPassword = sheet.get_Range("C" + vIndex.ToString()).Value.ToString();
this.SaveNewStaff(vFirstName, vLastName, vPassword);
vIndex++;
}
}
catch (Exception ex)
{
MessageBox.Show("Error processing excel file : " + ex.Message);
}
finally {
vExcelObj.Quit();
}
}
}
private void SaveNewStaff(string firstName, string lastName, string password) {
string uri = "http://localhost:8001/Service/Staff";
StringBuilder sb = new StringBuilder();
sb.Append("<Staff>");
sb.AppendLine("<FirstName>" + firstName + "</FirstName>");
sb.AppendLine("<LastName>" + lastName + "</LastName>");
sb.AppendLine("<Password>" + password + "</Password>");
sb.AppendLine("</Staff>");
string NewStudent = sb.ToString();
byte[] arr = Encoding.UTF8.GetBytes(NewStudent);
HttpWebRequest req = (HttpWebRequest)WebRequest.Create(uri);
req.Method = "POST";
req.ContentType = "application/xml";
req.ContentLength = arr.Length;
Stream reqStrm = req.GetRequestStream();
reqStrm.Write(arr, 0, arr.Length);
reqStrm.Close();
HttpWebResponse resp = (HttpWebResponse)req.GetResponse();
//MessageBox.Show("Staff Creation: Status " + resp.StatusDescription);
reqStrm.Close();
resp.Close();
}
Note: I have REMed out the MessageBox in the call to the web service to make sure you are not annoyed by it if the list is long, but you are free to "unREM" it if you need confirmation for every staff creation. In the same line of taught, there is not validation that the creation has occurred successfully. I would need more details to create a decent validation process.
Also VERY important, this does not validate if the staff you are saving already exists in the list. If you re-run this import procedure multiple times, it may (and probably will) create duplicate entries.
Cheers

how to give c# Rdlc report, untyped dataset in c# code

I have a small windows application with some calculations and have my untyped dataset populated namely "progDS". I have used MS Access Database. If I use typed dataset, the report picks each and every row and displays it on the reportviewer (that is not my requirement). What I had been trying to do is display only three rows that must contain the new calculated fields.
The application is for my department that receives forms of students, specific programs (that dataset 'progDS' contains) are offered. RDLC report, and ReportViewer only takes typed dataset, how to over-ride or change the dataset and give mine i.e. 'progDS'.
I have the ReportViewer on another form, and I am able to get the dataset 'progDS' into that form.
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Data.OleDb;
namespace FreshFormReport
{
public partial class Form3 : Form
{
Form1 ObjFomr1 = new Form1();
DataSet dsF3 = new DataSet();
DataSet2 dataSet = new DataSet2();
DataTable dtF3 = new DataTable();
BindingSource bs = new BindingSource();
public Form3()
{
InitializeComponent();
}
private void Form3_Load(object sender, EventArgs e)
{
try
{
// TODO: This line of code loads data into the 'DataSet2.RpTable' table. You can move, or remove it, as needed.
// this.RpTableTableAdapter.Fill(this.DataSet2.RpTable);
reportViewer1.LocalReport.DataSources.Clear();
// this.reportViewer1.RefreshReport();
Microsoft.Reporting.WinForms.ReportDataSource rptSource = new Microsoft.Reporting.WinForms.ReportDataSource("dsF3", dsF3.Tables["Porgrams"]);
reportViewer1.Reset();
reportViewer1.ProcessingMode = Microsoft.Reporting.WinForms.ProcessingMode.Local;
Microsoft.Reporting.WinForms.LocalReport localRep = reportViewer1.LocalReport;
localRep.Refresh();
localRep.ReportEmbeddedResource = "C:\\Users\\******* \\Documents\\Visual Studio 2010\\Projects\\FreshFormReport\\FreshFormReport\\MyReport.rdlc";
rptSource.Name = "MyReport";
//RpTableTableAdapter.Fill(DataSet2.RpTable);
rptSource.Value = DataSet2.RpTable;
reportViewer1.LocalReport.DataSources.Clear();
reportViewer1.LocalReport.DataSources.Add(rptSource);
//string name = localRep.GetDataSourceNames().ToString();
reportViewer1.LocalReport.ReportPath = "C:\\Users\\****** \\Documents\\Visual Studio 2010\\Projects\\FreshFormReport\\FreshFormReport\\MyReport.rdlc";
//reportViewer1.LocalReport.ReportEmbeddedResource = "MyReport.rdlc";
//reportViewer1.Refresh();
reportViewer1.Refresh();
reportViewer1.RefreshReport();
}
catch (Exception x)
{
MessageBox.Show(x.Message);
}
}
public void F3function(DataSet ds, DataTable dt)
{
try
{
dsF3 = ds;
dtF3 = dt;
bs.DataSource = dt;
}
catch (Exception x)
{
MessageBox.Show(x.Message);
}
}
}
}

Is it possible to use `SqlDbType.Structured` to pass Table-Valued Parameters in NHibernate?

I want to pass a collection of ids to a stored procedure that will be mapped using NHibernate. This technique was introduced in Sql Server 2008 ( more info here => Table-Valued Parameters ). I just don't want to pass multiple ids within an nvarchar parameter and then chop its value on the SQL Server side.
My first, ad hoc, idea was to implement my own IType.
public class Sql2008Structured : IType {
private static readonly SqlType[] x = new[] { new SqlType(DbType.Object) };
public SqlType[] SqlTypes(NHibernate.Engine.IMapping mapping) {
return x;
}
public bool IsCollectionType {
get { return true; }
}
public int GetColumnSpan(NHibernate.Engine.IMapping mapping) {
return 1;
}
public void NullSafeSet(DbCommand st, object value, int index, NHibernate.Engine.ISessionImplementor session) {
var s = st as SqlCommand;
if (s != null) {
s.Parameters[index].SqlDbType = SqlDbType.Structured;
s.Parameters[index].TypeName = "IntTable";
s.Parameters[index].Value = value;
}
else {
throw new NotImplementedException();
}
}
#region IType Members...
#region ICacheAssembler Members...
}
No more methods are implemented; a throw new NotImplementedException(); is in all the rest. Next, I created a simple extension for IQuery.
public static class StructuredExtensions {
private static readonly Sql2008Structured structured = new Sql2008Structured();
public static IQuery SetStructured(this IQuery query, string name, DataTable dt) {
return query.SetParameter(name, dt, structured);
}
}
Typical usage for me is
DataTable dt = ...;
ISession s = ...;
var l = s.CreateSQLQuery("EXEC some_sp #id = :id, #par1 = :par1")
.SetStructured("id", dt)
.SetParameter("par1", ...)
.SetResultTransformer(Transformers.AliasToBean<SomeEntity>())
.List<SomeEntity>();
Ok, but what is an "IntTable"? It's the name of SQL type created to pass table value arguments.
CREATE TYPE IntTable AS TABLE
(
ID INT
);
And some_sp could be like
CREATE PROCEDURE some_sp
#id IntTable READONLY,
#par1 ...
AS
BEGIN
...
END
It only works with Sql Server 2008 of course and in this particular implementation with a single-column DataTable.
var dt = new DataTable();
dt.Columns.Add("ID", typeof(int));
It's POC only, not a complete solution, but it works and might be useful when customized. If someone knows a better/shorter solution let us know.
A simpler solution than the accepted answer would be to use ADO.NET. NHibernate allows users to enlist IDbCommands into NHibernate transactions.
DataTable myIntsDataTable = new DataTable();
myIntsDataTable.Columns.Add("ID", typeof(int));
// ... Add rows to DataTable
ISession session = sessionFactory.GetSession();
using(ITransaction transaction = session.BeginTransaction())
{
IDbCommand command = new SqlCommand("StoredProcedureName");
command.Connection = session.Connection;
command.CommandType = CommandType.StoredProcedure;
var parameter = new SqlParameter();
parameter.ParameterName = "IntTable";
parameter.SqlDbType = SqlDbType.Structured;
parameter.Value = myIntsDataTable;
command.Parameters.Add(parameter);
session.Transaction.Enlist(command);
command.ExecuteNonQuery();
}
For my case, my stored procedure needs to be called in the middle of an open transaction.
If there is an open transaction, this code works because it is automatically reusing the existing transaction of the NHibernate session:
NHibernateSession.GetNamedQuery("SaveStoredProc")
.SetInt64("spData", 500)
.ExecuteUpdate();
However, for my new Stored Procedure, the parameter is not as simple as an Int64. It's a table-valued-parameter (User Defined Table Type)
My problem is that I cannot find the proper Set function.
I tried SetParameter("spData", tvpObj), but it's returning this error:
Could not determine a type for class: …
Anyways, after some trial and error, this approach below seems to work.
The Enlist() function is the key in this approach. It basically tells the SQLCommand to use the existing transaction. Without it, there will be an error saying
ExecuteNonQuery requires the command to have a transaction when the
connection assigned to the command is in a pending local transaction…
using (SqlCommand cmd = NHibernateSession.Connection.CreateCommand() as SqlCommand)
{
cmd.CommandText = "MyStoredProc";
NHibernateSession.Transaction.Enlist(cmd); // Because there is a pending transaction
cmd.CommandType = CommandType.StoredProcedure;
cmd.Parameters.Add(new SqlParameter("#wiData", SqlDbType.Structured) { Value = wiSnSqlList });
int affected = cmd.ExecuteNonQuery();
}
Since I am using the SqlParameter class with this approach, SqlDbType.Structured is available.
This is the function where wiSnList gets assigned:
private IEnumerable<SqlDataRecord> TransformWiSnListToSql(IList<SHWorkInstructionSnapshot> wiSnList)
{
if (wiSnList == null)
{
yield break;
}
var schema = new[]
{
new SqlMetaData("OriginalId", SqlDbType.BigInt), //0
new SqlMetaData("ReportId", SqlDbType.BigInt), //1
new SqlMetaData("Description", SqlDbType.DateTime), //2
};
SqlDataRecord row = new SqlDataRecord(schema);
foreach (var wi in wiSnList)
{
row.SetSqlInt64(0, wi.OriginalId);
row.SetSqlInt64(1, wi.ShiftHandoverReportId);
if (wi.Description == null)
{
row.SetDBNull(2);
}
else
{
row.SetSqlString(2, wi.Description);
}
yield return row;
}
}
You can pass collections of values without the hassle.
Example:
var ids = new[] {1, 2, 3};
var query = session.CreateQuery("from Foo where id in (:ids)");
query.SetParameterList("ids", ids);
NHibernate will create a parameter for each element.

Resources