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.
Related
I am using EF to call stored procedures like this:
db.Database.ExecuteSqlCommand(sql, parameters);
These procedures do multiple inserts and updates. I added, a TRY region and a COMMIT at the end of the TRY and a ROLLBACK (inside a CATCH) logic but this is giving me an error which I traced back to this:
Stackoverflow Explanation
If the case is that EF wraps the stored procedure calls then does this mean there is no reason to do that inside the stored procedure or should I leave the COMMIT and ROLLBACK in the SP and do this:
db.Database.ExecuteSqlCommand(TransactionalBehavior.DoNotEnsureTransaction, sql, parameters);
If possible, you should not handle the transaction in the stored procedures and go for something like this, with DbContexts relying on existing connection and transaction :
using (var conn = new SqlConnection("YourConnectionString"))
{
conn.Open();
using (var tran = conn.BeginTransaction())
{
using (var ctx1 = new DbContext(conn, false))
{
ctx1.Database.UseTransaction(tran);
ctx1.Database.ExecuteSqlCommand("Exec YourStoredProc1");
ctx1.Database.ExecuteSqlCommand("Exec YourStoredProc2");
}
using (var ctx2 = new DbContext(conn, false))
{
ctx2.Database.UseTransaction(tran);
ctx2.Database.ExecuteSqlCommand("Exec YourStoredProc3");
}
tran.Commit();
}
}
As the transaction is already provided, Entity Framework will not wrap yur calls in new transactions.
You can handle the rollback logic for errors with try{}catch(){}, of course, but also with any business code you want.
You can do like this:
using (TransactionScope scope = new TransactionScope())
{
//EF context
using (TestDatabaseEntities contect = new TestDatabaseEntities())
{
//TODO:`enter code here`
}
}
The rollback will rollback all opening (active) transaction. Incase using EF, EF always auto create an root (outermost) transaction, so that the rollback inside a stored proc will rollback the transaction created by EF as well and it will cause error.
There is a way to get over, using Save point -> seee https://msdn.microsoft.com/en-us/library/ms188378.aspx
I've applied the template save point for EF and it works well.
I am moving my datamodel to Azure Elastic Scale.
After some testing and some experiences I fall in love if it, it is simple and with that kind of approach the code remains clean and easy to maintain.
I just have one big question, where is the Sharding key defined? I cannot find info on the sample downloaded from Visual Studio and I can beat that this is a straight forward answer.
In the sample offered by Microsoft the default sharding key is CustomerId but i cannot find where the reference to that key takes place.
Could it be in ShardMapName from configuration file?
Thanks in advance.
There is no explicit link between the the sharding key in the SQL schema and its usage (in code).
So in the Getting Started sample, the Customers and Orders table both contain a CustomerId column, and you can see that in DataDependentRoutingSample.cs when we access these tables we make sure to provide the same customerId value to the shardMap.OpenConnectionForKey method that we then use for customerId column (both in the SELECT and INSERT statements) in the following query.
// Looks up the key in the shard map and opens a connection to the shard
using (SqlConnection conn = shardMap.OpenConnectionForKey(customerId, credentialsConnectionString))
{
// Create a simple command that will insert or update the customer information
SqlCommand cmd = conn.CreateCommand();
cmd.CommandText = #"
IF EXISTS (SELECT 1 FROM Customers WHERE CustomerId = #customerId)
UPDATE Customers
SET Name = #name, RegionId = #regionId
WHERE CustomerId = #customerId
ELSE
INSERT INTO Customers (CustomerId, Name, RegionId)
VALUES (#customerId, #name, #regionId)";
cmd.Parameters.AddWithValue("#customerId", customerId);
cmd.Parameters.AddWithValue("#name", name);
cmd.Parameters.AddWithValue("#regionId", regionId);
cmd.CommandTimeout = 60;
// Execute the command
cmd.ExecuteNonQuery();
}
In other words, when you provide a certain key value in the OpenConnectionForKey call, it's your responsibility to make sure that all SQL queries with that connection are restricted to that key value, otherwise you may end up with incorrect results (e.g. if it was a SELECT query) or rows living on the wrong shard (e.g. if it was an INSERT query).
It's possible to fix this safety issue by using the new Row-Level Security feature. We have a sample called Entity Framework Multi-Tenant Shards that demonstrates how to combine Shard Maps with Row-Level Security. The relevant code is in ElasticScaleContext.cs:
SqlConnection conn = null;
try
{
// Ask shard map to broker a validated connection for the given key
conn = shardMap.OpenConnectionForKey(shardingKey, connectionStr, ConnectionOptions.Validate);
// Set CONTEXT_INFO to shardingKey to enable Row-Level Security filtering
SqlCommand cmd = conn.CreateCommand();
cmd.CommandText = #"SET CONTEXT_INFO #shardingKey";
cmd.Parameters.AddWithValue("#shardingKey", shardingKey);
cmd.ExecuteNonQuery();
return conn;
}
catch (Exception)
{
if (conn != null)
{
conn.Dispose();
}
throw;
}
Thanks for your great question!
I am working in asp.net and SQL Server 2005.
I want to know how to return a value from stored procedure or how can we know if a query has been successfully executed in a stored procedure. For example I am inserting some data into a SQL Server database, I want to check if it is inserted correctly or not by using a return value.Here is the code
stored procedure..
ALTER PROCEDURE [dbo].[practice]
-- Add the parameters for the stored procedure here
#thename varchar(50),
#thedate datetime,
AS
BEGIN
-- SET NOCOUNT ON added to prevent extra result sets from
-- interfering with SELECT statements.
SET NOCOUNT ON;
-- Insert statements for procedure here
insert into tblpractice (name,date) values (#thename,#thedate)
END
codebehind..
string name=txtname.Text.Trim();
DateTime date = Convert.ToDateTime(txtdate.Text);
try
{
SqlCommand cmd = new SqlCommand("practice", lcon);
cmd.CommandType = CommandType.StoredProcedure;
cmd.Parameters.AddWithValue("#thename", name);
cmd.Parameters.AddWithValue("#thedate", date);
lcon.Open();
cmd.ExecuteNonQuery();
}
catch
{
throw;
}
finally
{
if (lcon != null)
{
lcon.Close();
}
}
using output parameters you can get values from store procedure. Here is a good example
Stored procedure output parameter asp.net c#
Another example
http://www.c-sharpcorner.com/UploadFile/rohatash/get-out-parameter-from-a-stored-procedure-in-Asp-Net/
Well, simmilar to C#/java sql server can have try catch blocks
here is the link:
http://msdn.microsoft.com/en-us/library/ms175976.aspx
You can preform error handling (oracle have this better solved), or you can relativly easy do IF block to solve your problem with a procedure. F.E. if you return some number (that have to be >0) you can preform check on it, and return -1 or what ever if your select within procedure didnt return any entry.
Those are most common ways to solve your kind of problems.
In your case, you can query DB once more for an entry with values you allready have in procedure and return 0/1 if select count(*) FROM table where .... returns value
ExecuteNonQuery returns an int which is the number of rows affected by the query. In your example, if the stored proc executes successfully, one record will be inserted, so ExecuteNonQuery should return 1.
http://msdn.microsoft.com/en-us/library/system.data.sqlclient.sqlcommand.executenonquery.aspx
Also you should use using when you're dealing with IDisposable objects e.g.
using (var lcon = new SqlConnection(connectionString))
{
using (var cmd = new SqlCommand("practice", lcon))
{
cmd.CommandType = CommandType.StoredProcedure;
cmd.Parameters.AddWithValue("#thename", name);
cmd.Parameters.AddWithValue("#thedate", date);
lcon.Open();
return cmd.ExecuteNonQuery();
}
}
This is the equivalent of try catch finally block i.e. it will close your connection whether there is an error or not.
this is a method i was reading about #MSDN ,
my question is if for an example i would like to use it on a stored procedure
with the fact that the query of that stored procedure is already specifying columns to select from the table like following :
SELECT Columnsome, columnother, , , , ...FROM thisSQLdbTable
though i would like to implement the approach of that specific method , it seems very advanced from a little research i have made on
"the best way" available to extract data from SQL Server into Asp.net DataTable.
public static DataTable GetCustomerData(string dataSetName,
string connectionString)
{
DataTable table = new DataTable(dataSetName);
using (SqlConnection connection = new SqlConnection(connectionString))
{
SqlDataAdapter adapter = new SqlDataAdapter(
"SELECT CustomerID, CompanyName, ContactName FROM dbo.Customers", connection);
DataTableMapping mapping = adapter.TableMappings.Add("Table", "Customers");
mapping.ColumnMappings.Add("CompanyName", "Name");
mapping.ColumnMappings.Add("ContactName", "Contact");
connection.Open();
adapter.FillSchema(table, SchemaType.Mapped);
adapter.Fill(table);
return table;
}
}
or is it not the method to use if i am querying via SP that specifies the selected column
i could actually drop that stored procedure if it is not requiered to select /specify columns
the stored procedure is doing a specific calculation and updates the table with results of calculation then i am switching it's "MODE" to select results from the table that was updated.
what i did is recyceling (; giving it a parameter (bit type)
stored procedure then asks for the value of supplied bool / bit Parameter,
if its is status true it updates (doing its original task it was made for)
if its false its doing a select oporation so i am using it as i would with 2 separated commands
but now that i have search for a better way to extract data from db into a Data table
i give up on the two way SP and i will make a selection via the exaple above if they're not intended to be used thogether as with my current SP that does preselection when servs the GetCustomersData() above.
So the question is do i need to let the function to make the selection or can i serve it with my sp ready made selection to implemet it with GetCustomersData() in the way that it will only do rest of task and only mapp the columns that was preselected
Still a bit confused on your actual requirement but here goes:
I See you are using a direct query in your C# code, 'best way' would be to make a SP out of it then say:
SqlCommand command = conn.CreateCommand();
SqlDataAdapter sqlAdapter = new SqlDataAdapter(command);
command.CommandType = System.Data.CommandType.StoredProcedure;
command.CommandText = "sp_GetCustomerData";
Then after you have added parameters if needed do:
conn.Open();
sqlAdapter.Fill(dtResult);
conn.Close();
Where dtResult is Datatable.
So you do not need to do any mapping in this case, and since you are using a SP from the Database it will work faster than your direct query and you can change the query logic any time without the need of re deploying your code.
Stored procedures are perfectly valid in this use case. however, if you want more of a properly mapped table, you have several options, some of which go beyond the use of DataTables.
You can use strongly typed DataSets or perhaps use an ORM (object relational mapper).
ref: Typed Datasets: http://msdn.microsoft.com/en-us/library/esbykkzb(v=vs.71).aspx
ref: What is an ORM : http://en.wikipedia.org/wiki/Object-relational_mapping
EXAMPLES OF ORM'S
ref: Entity Framework : http://msdn.microsoft.com/en-us/data/ef.aspx
ref: NHibernate: http://nhforge.org/
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.