I've been building this project as the solo dev for a while, and while I'm comfortable in the front end and middle tier, I don't really think I'm doing the database the way I should be, and the reason why is because I simply don't really know of any other way. The way I'm currently getting data is by testing out queries in my MySQL workbench and copying and pasting the SQL as a string literal into a method that makes a call to the DB, pulls the data and hydrates my objects.
This hasn't really been a problem until recently, when I had to create a monster of a query and it got me thinking that maybe there's a better way to do this. I don't have a formal DAL separated out, so I know there's room for improvement there, but I was curious about what the correct way would be to store SQL strings. I assume there is a tool somewhere built into VS10 where I can manipulate and work with SQL as SQL instead of as a string.
You should be doing this in stored procedures. That will basically format and store your query. You set parameters that are passed in from your code, then read out the results.
Example:
The C# method:
private void SetNote()
{
const string sql = "sp_SelectControllerNoteByID";
using (var conn = MocSystem.GetMocDbConnection())
{
using (var comm = new SqlCommand(sql, conn))
{
comm.CommandType = CommandType.StoredProcedure;
comm.Parameters.Add(new SqlParameter("#ControllerNoteID", ControllerNoteId));
try
{
conn.Open();
using (var rdr = comm.ExecuteReader())
{
while (rdr.Read())
{
CommentText = rdr["NoteText"].ToString();
_commentor = new User(new Guid(rdr["NoteAuthor"].ToString()));
CommentDate = (DateTime)rdr["NoteDate"];
MocRequestId = (int)rdr["MocRequestID"];
}
}
}
catch (Exception ex)
{
HasError = true;
ErrorMessage += "\nThere was a problem building the note: " + ex.Message;
}
}
}
}
The stored procedure on the DBMS (sql server in this example):
ALTER proc [dbo].[sp_SelectControllerNoteByID]
#ControllerNoteID int
AS
SELECT
ControllerNoteID,
NoteText,
NoteDate,
NoteAuthor,
MocRequestID
FROM
ControllerNotes
WHERE
ControllerNoteID = #ControllerNoteID
So here we call the stored procedure which in this case is just a simple select statement, then we read it out into an object via ADO. Now, this way, you can modify your query without recompiling. Unless you add parameters, in which case you'll have to update those in your code as well.
Related
I am new in database using Delphi. Actually I need to display all the tables and their data in a gridview using firedac. Several videos on youtube and docwiki teaches how to access database through visual components i.e. throwing FDConnetion, FDTable, DataSource, etc. on the form.
But I would like to do the same thing programmatically so that I can focus more on coding and separate form from the business logic.
Here is what I have done in C# while accessing MSAccess data:
public void LoadUsersInfo()
{
try {
OleDbConnection Connection = new OleDbConnection();
Connection.ConnectionString = #"Provider=Microsoft.ACE.OLEDB.12.0;DataSource=dbCyberNet.accdb;Persist Security Info=False;";
Connection.Open();
OleDbCommand command = new OleDbCommand();
command.Connection = Connection;
String query = "SELECT * FROM tblUsersInfo";
command.CommandText = query;
OleDbDataAdapter adapter = new OleDbDataAdapter(command);
DataTable dt = new DataTable();
adapter.Fill(dt);
dbGrid.DataSource = dt;
}
catch (Exception ex) {
MessageBox.Show("Error occured while loading users information . " + ex.Message.ToString());
}
finally { Connection.Close(); }
}
I would like to do similar in delphi for sqlite database. Here is what I have tried (edited).
procedure TForm1.Button1Click(Sender: TObject);
var
con: TFDConnection;
table: TFDTable;
ds: TDataSource;
begin
con := TFDConnection.Create(nil);
con.LoginPrompt := False;
table := TFDTable.Create(nil);
table.TableName := 'Students';
ds := TDataSource.Create(nil);
ds.DataSet := table;
try
con.Open('DriverID=SQLite;Database=‪studentsDB.sqlite;');
table.Connection := con;
table.Active := True;
con.Connected := True;
DBGrid1.DataSource := ds;
finally
con.Close;
con.Free;
table.Free;
ds.Free;
end;
end;
Whenever I run this code, error dialog appears showing: First chance exception at $759AF097. Exception class ESQLiteNativeException with message '[FireDAC][Phys][SQLite] ERROR: no such table: STUDENTS'. Process Project1.exe (3836)
whereas the same table name exists inside the table and using visual firedac components, I can see the data grid fills easily.
You can do the same as with C#: assign your TFDQuery instance as DataSource of the VCL grid!
Add a TDataSource to the form, set its DataSet property to your TFDQuery, and assign it to your grid.
See the official Delphi documentation and other SO questions.
You can execute your Select query by calling the query's Open method so your code will look like this :-
procedure TStudents.executeQuery;
var
con: TFDConnection;
query: TFDQuery;
begin
con := TFDConnection.Create(nil);
query := TFDQuery.Create(con);
con.LoginPrompt := False;
con.Open('DriverID=SQLite;Database=studentsDB.sqlite;');
query.Connection := con;
query.SQL.Text := 'SELECT * FROM Students';
query.Open;
query.First;
While Not Query.EOF Do
Begin
// Populate your object or do whatever processing you like here.
Query.Next;
End;
end;
You can then programatically create a TDataSource and a Grid and hook those up in code in a similar fashion. You'll need to move the Connection and Query objects out of your object's executeQuery method as they are local variables and will not be accessible to anything outside that method.
Note that Delphi does not garbage collect like C# does, so as it stand, the code leaks memory.
As an aside, your code appears to be an object method and it looks like you're attempting to create a set of business objects that know how to load and save themselves. Rather than roll your own, you might be better served by looking at one of the existing frameworks that exist (tiOPF, InstantObjects, mORMot or TMS Aurelius) to achieve this. These will allow you to concentrate on solving the problem at hand and not worry about the minutae of creating, loading and saving of objects.
I have two tables in my databse viz.PurchaseOrderMST and SiteTRS. The primary key of PurchaseOrderMST is foreign key in SiteTRS. The case is first data is inserted in PurchaseOrderMST and then SiteTRS using two individual SPs. I want to maintain transaction during insertion on these two tables from the asp.net(C#). If data in the first table is inserted successfully then and only then data in the second tables should be inserted. If insertion fails in second tables, the insertion in the first tables should also rollback.
How can I do this using transaction mechanism of asp.net???
This is not really related to ASP.NET, but generally how transactions work in the .NET framework. With SqlClient, this is how you do it:
using (var connection = new SqlConnection("your connectionstring"))
{
connection.Open();
using (var transaction = connection.BeginTransaction())
{
try
{
using (var command1 = new SqlCommand("SP1Name", connection, transaction))
{
command1.ExecuteNonQuery();
}
using (var command2 = new SqlCommand("SP2Name", connection, transaction))
{
command2.ExecuteNonQuery();
}
transaction.Commit();
}
catch
{
transaction.Rollback();
throw;
}
}
}
You of course need to add Parameters to the SqlCommand objects before you execute them, and which execute method you use (ExecuteNonQuery(), ExecuteScalar() or ExecuteReader()) depends on whether your stored procedures actually returns any data or not.
Can you guys let me know the way of handling transactions in asp.net?
i.e. I have a few queries (Present in different functions and called under various situations) that have to be executed as a whole. So, how should I go about it?
Not sure of the syntax and the method/practice for writing the statements in .net (commit, rollback etc).
Kindly let me know. Also, plz point me to some good articles if possible. Thanks!!!
I recommend using TransactionScope, because you can use it no mater what DB you are using. You can even do distributed transactions (operations against multiple databases within the same transaction) with it.
You can refer to a link for a code example, but in general, you do this:
try
{
using (TransactionScope scope = new TransactionScope())
{
using (MySqlConnection connection1 = new MySqlConnection (connectionString))
{
// Opening the connection automatically enlists it in the
// TransactionScope as a lightweight transaction.
connection1.Open();
// create the DB commands and perform the DB operations
.
.
.
// The Complete method commits the transaction. If an exception has been thrown,
// Complete is not called and the transaction is rolled back.
scope.Complete();
}
}
}
catch (Exception e)
{
// something went wrong, handle the exception accordingly. Note
// that since we did not call TransactionScope.Complete, nothing
// gets committed to the DB.
}
Here's another starter for TransactionScope: Implementing an Implicit Transaction using Transaction Scope
Don't know much about TransactionScope, but I just use the normal IDbTransaction like this:
IDbConnection conn = null;
IDbCommand cmd = null;
IDbTransaction tran = null;
try
{
conn = DatabaseUtil.GetConnection(); //Get the connection somehow
cmd = conn.CreateCommand();
tran = conn.BeginTransaction();
cmd.Transaction = tran;
//Do your DB Work
tran.Commit();
}
catch (SystemException ex)
{
tran.Rollback();
}
finally
{
if (conn != null) conn.Close();
}
With the IDb classes you are DB independent too to a certain degree.
If its a local transaction you can also use ado.net's transaction object. TransactionScope will handle distributed transactions if needed but requires MSDTC to be configured if a transaction is promoted to a distributed transaction.
http://msdn.microsoft.com/en-us/library/2k2hy99x.aspx
Both are in the System.Transactions Namespace http://msdn.microsoft.com/en-us/library/system.transactions.aspx
I have this problem in my ASP.NET application where I'm seeing some of my Oracle queries fired off to the server then not returning. Ever. It happens in several places in my app and I can't explain it. Here's one specific scenario where I'm seeing this behavior:
During application start-up I am pre-fetching data asynchronously into the application state (the choice was made to use app state instead of cache b/c the data never changes during the lifetime of the app).
Action<string, object> AddApplicationState = (string name, object data) =>
{
Application.Lock();
Application.Add(name, data);
Application.UnLock();
};
Func<DataTable> GetFullNames = () => Database.GetAllNames();
Func<DataTable> GetProvinceNames = () => Database.GetProvinceNames();
Func<DataTable> GetTribeNames = () => Database.GetTribeNames();
GetFullNames.BeginInvoke(result => AddApplicationState("AllNames", GetFullNames.EndInvoke(result)), null);
GetProvinceNames.BeginInvoke(result => AddApplicationState("ProvinceNames", GetProvinceNames.EndInvoke(result)), null);
GetTribeNames.BeginInvoke(result => AddApplicationState("TribeNames", GetTribeNames.EndInvoke(result)), null);
The second two return just fine, but the first either never returns or returns after about 10 minutes. After firing up Oracle SQL Developer I go to the 'monitor sessions' tool and can see a single session for the query. It looks like it has completed, b/c the wait time is (null) and the session is inactive. Here's the ADO.NET code used to query the database:
public static DataTable GetAllNames()
{
using (OracleConnection oraconn = GetConnection())
{
using (OracleCommand oracmd = GetCommand(oraconn))
{
var sql = new StringBuilder();
sql.AppendLine("SELECT NAME_ID, NATIVE_NAME, NVL(FREQUENCY,0) \"FREQUENCY\", CULTURE_ID,");
sql.AppendLine("ENGLISH_NAME, REGEXP_REPLACE(ENGLISH_NAME, '[^A-Za-z]', null) \"ENGLISH_NAME_STRIPPED\"");
sql.AppendLine("FROM NAMES");
oracmd.CommandText = sql.ToString();
var orada = new OracleDataAdapter(oracmd);
var dtAllNames = new DataTable();
orada.Fill(dtAllNames);
return dtAllNames;
}
}
}
public static DataTable GetTribeNames()
{
using (OracleConnection oraconn = GetConnection())
{
using (OracleCommand oracmd = GetCommand(oraconn))
{
var sql = new StringBuilder();
sql.AppendLine("SELECT DISTINCT NAME_ID, English_Name \"TRIBE_NAME_ENGLISH\",");
sql.AppendLine("REGEXP_REPLACE(English_Name, '[^A-Za-z]',null) \"TRIBE_ENGLISH_NAME_STRIPPED\",");
sql.AppendLine("NATIVE_NAME \"TRIBE_NATIVE_NAME\"");
sql.AppendLine("FROM NAMES");
sql.AppendLine("WHERE NAME_ID IN ");
sql.AppendLine("(SELECT NAME_ID_TRIBE FROM TRIBES UNION SELECT NAME_ID_FAMILY FROM TRIBES)");
sql.AppendLine("ORDER BY English_Name");
oracmd.CommandText = sql.ToString();
var orada = new OracleDataAdapter(oracmd);
var dt = new DataTable();
orada.Fill(dt);
return dt;
}
}
}
public static DataTable GetProvinceNames()
{
using (OracleConnection oraconn = GetConnection())
{
using (OracleCommand oracmd = GetCommand(oraconn))
{
oracmd.CommandText = "SELECT DISTINCT PROVINCE_ID, PROVINCE_NAME_NATIVE, PROVINCE_NAME_ENGLISH FROM PROVINCES";
var orada = new OracleDataAdapter(oracmd);
var dtRC = new DataTable();
orada.Fill(dtRC);
return dtRC;
}
}
}
As you can see, the ADO.NET code is pretty standard (and boring!) stuff. When run in SQL Developer, the queries return less than a second. The first query returns x rows, the second x rows, and the third x rows. But this problem of queries being fired off then never returning happens often and I can't seem to track down the issue. Anyone have any thoughts?
And finally, since I realize it could be something completely unrelated to code, I am running the app locally (from Visual Studio) on a Windows XP SP3 machine and connecting via VPN to a remote Oracle 10g Enterprise instance running on Windows 2003 Server. Locally, I have installed Oracle Data Access Components v11.1.0.6.20.
Thanks!
Are you watching your output window for any exceptions? I don't see any catch blocks in your code.
Oracle's ODP.net has almost exactly the same syntax as ADO, but performs better in many situations. If you're only using Oracle, it might be worth a look.
Is there a reason to use StringBuilder? A single string variable will perform better and makes code easier to read.
It seems that the queries were actually returning, just taking a very long time due to poor query performance, low bandwidth, and the enormous amount of rows being returned. The VS debugger seems to give up after a few seconds for these long-running queries. However, if I let it sit for a few minutes my breakpoints would get hit and things would work as expected.
Thanks for the replies / comments!
I was just wondering about the correct way to return a reader from a class?
My code below works, but I'm unsure if this is correct.
Also. I can't close the the connection in my class method and still access it from my ascx page, is
that OK?
// In my class I have the following method to return the record/reader -- it's a single record in this case.
public SqlDataReader GetPost()
{
SqlConnection conn = new SqlConnection(connectionString);
SqlCommand cmd = new SqlCommand("con_spPost", conn);
cmd.CommandType = CommandType.StoredProcedure;
cmd.Parameters.AddWithValue("#blogid", blogid);
try
{
conn.Open();
return cmd.ExecuteReader();
}
finally
{
// conn.Close();
}
}
//I then call the GetPost method in my ascx page like so:
protected void Page_Load(object sender, EventArgs e)
{
//instantiate our class
MyClass DB = new MyClass();
//pass in the id of the post we want to view
DB.PostID = Int32.Parse(Request.QueryString["p"]);
///call our GetPost method
SqlDataReader reader = DB.GetPost();
//output the result
reader.Read();
this.viewpost.InnerHtml = "<span id='post1_CreatedDate'>" + reader["CreatedDate"].ToString() + "</span><br>";
this.viewpost.InnerHtml += "<span class='blogheads'>" + reader["BlogTitle"].ToString() + "</span><p><p>";
this.viewpost.InnerHtml += reader["BlogText"].ToString();
reader.Close();
}
I'd appreciate any comments on my code or tips, thanks.
Melt
Generally speaking it's fine to return a reader from a method, but the reader's consumer needs to take control of all the disposable objects that will be used during the reader's lifetime.
To do this, you'd pass an IDbConnection into the GetPost method, then make sure your caller disposes both the connection and reader. The using keyword is the most convenient way to do this:
protected void Page_Load(object sender, EventArgs e) {
// Create the DB, get the id, etc.
using (IDbConnection connection = new SqlConnection(connectionString))
using (IDataReader reader = DB.GetPost(connection)) {
reader.Read();
this.viewpost.InnerHtml = reader["BlogText"].ToString();
// finishing doing stuff with the reader
}
}
As others have pointed out, this starts to clutter your application's presentation layer with way too much data-access infrastructure - so it isn't appropriate here. Until you find yourself with a performance problem or need to display an unreasonable amount of data, you shouldn't be handling data readers in your presentation layer. Just make DB.GetPost return a string, and encapsulate all the data-access code in there.
To make sure that the connection is closed, replace the ExecuteReader call with the following:
return cmd.ExecuteReader(CommandBehavior.CloseConnection);
You should also remove te try / finally block.
Also, in your Page_Load handler, you should use a using statement, like this:
using (SqlDataReader reader = DB.GetPost()) {
//output the result
reader.Read();
this.viewpost.InnerHtml = "<span id='post1_CreatedDate'>" + reader["CreatedDate"].ToString() + "</span><br>"
+ "<span class='blogheads'>" + reader["BlogTitle"].ToString() + "</span><p><p>"
+ reader["BlogText"].ToString();
}
Also, you should check that the SQL query actually returned something, like this:
if (!reader.Read()) {
Something's wrong
}
Finally, and most important by far, you should escape your HTML to prevent XSS holes by calling Server.HtmlEncode.
For example:
this.viewpost.InnerHtml = "<span id='post1_CreatedDate'>" + reader["CreatedDate"].ToString() + "</span><br>"
+ "<span class='blogheads'>" + Server.HtmlEncode(reader["BlogTitle"].ToString()) + "</span><p><p>"
+ Server.HtmlEncode(reader["BlogText"].ToString());
You really shouldn't be mixing data access with the presentation layer.
Consider returning a typed DataSet, or building business objects and returning those to your control.
Here is a tutorial:
http://www.asp.net/learn/data-access/tutorial-01-cs.aspx
There is a problem. Your connection is not being closed. As you know you can't close it within your GetPost because then you won't have the data any more, due to the nature of the DataReader. One way to address this is to include a parameter in your ExecuteReader method like this:
cmd.ExecuteReader(CommandBehavior.CloseConnection)
Then, when your reader is closed, the connection will close.
There is a fundamental problem with having a datareader returned by encapsulated code, in that the connection has to be open through all this, making error handling tricky. Consider (A) using a datatable instead, which is almost as efficient for small sets of data. This way you can close your connection right away in you GetPost method and not worry about it, with very simple error handling. Or (B) Pass the connection into GetPost, so all the Using/Dispose syntax and error handling for the connection is explicit in one place. I would suggest option A.
This is a very simple architecture. As CSharpAtl has suggested, you could make it more sophisticated. However, this seems to work for you.
One important addition I would make would be to use try-finally blocks. Putting the Close in the finally will ensure that the connection is released even if an exception occurs during processing.
SqlDataReader reader;
try
{
///call our GetPost method
reader = DB.GetPost();
//output the result
reader.Read();
this.viewpost.InnerHtml = "<span id='post1_CreatedDate'>" + reader["CreatedDate"].ToString() + "</span><br>";
this.viewpost.InnerHtml += "<span class='blogheads'>" + reader["BlogTitle"].ToString() + "</span><p><p>";
this.viewpost.InnerHtml += reader["BlogText"].ToString();
}
finally
{
reader.Close();
}
Why are you having the web page know about the database at all? Why not abstract away the database knowledge and just return a list or an object with the data from the database? Just seems like a lot of responsibility mixing, and you could make it easier on yourself.
This article by Dan Whalin might be a good resource for you to read through. It shows the basics of creating an n-layered application. You create a data access component, an entity object, a business layer and a presentation layer. He also uses sql data readers like you're asking about, and he shows a nice way of having a object build helper method.
If you don't like reading article, he also has a pretty good video on the same subject and a code example which you can download and see the different variations of this method of creating data driven applications.
Good luck and hope this helps some.