how do i execute a stored procedure with vici coolstorage? - stored-procedures

I'm building an app around Vici Coolstorage (asp.net version). I have my classes created and mapped to my database tables and can pull a list of all records fine.
I've written a stored procedure where the query jumps across databases that aren't mapped with Coolstorage, however, the fields in the query result map directly to one of my classes. The procedure takes 1 parameter.
so 2 questions here:
how do i execute the stored procedure? i'm doing this
CSParameterCollection collection = new CSParameterCollection();
collection.Add("#id", id);
var result = Vici.CoolStorage.CSDatabase.RunQuery("procedurename", collection);
and getting the exception "Incorrect syntax near 'procedurename'." (i'm guessing this is because it's trying to execute it as text rather than a procedure?)
and also, since the class representing my table is defined as abstract, how do i specify that result should create a list of MyTable objects instead of generic or dynamic or whatever objects? if i try
Vici.CoolStorage.CSDatabase.RunQuery<MyTable>(...)
the compiler yells at me for it being an abstract class.

There's a shortcut in CoolStorage to run a stored procedure. Simply prefix the stored procedure name with "!":
CSDatabase.RunQuery("!procedurename", collection);

Related

How to specify a parameter as collection in Commandtext

I am using SqlCommandProvider and I need to get some data for each id in an id collection
let ids=["B058A99-C4B2-4CC3-BA9F-034B1F1ECCBD";"A09C01C-D51B-41C1-B44C-0995DD285088"]
[<Literal>]
let qLogo ="""SELECT Id,LogoUrl FROM Hotels WHERE Id IN (#IDS)"""
let queryLogo = new SqlCommandProvider<qLogo,constring>()
queryLogo .Execute(ids)//i need to be able to pass a collection here
`
Long story short, SqlCommandProvider is not even correct type provider to do this (assuming you don't consider string concatenation to build query). The reason is SQL Server only accepts array parameters through calling stored procedure and passing a Table Valued Parameter.
So, to call a stored procedure you need an SqlProgrammabilityProvider to achieve this. But you'll have to create Table Type and Stored Procedure up-front, as described in type provider documentation (scroll down to "Table-valued parameters (TVPs)" section).
Relevant discussion: How to use SQL IN statement in fsharp.data.sqlclient?

Error selecting from large data table causing time out in EF + code first

I am using EF code first model to get the data from data base table in which i have 400,000 records.
But when i use the LINQ query something like:
var urer = context.UserEntity.Where(c => c.FirstName.Contains('s'));
The above statement gives me all the user to whose first name contains 's'. But since this is a huge data base table, it is giving me the following error:
An existing connection was forcibly closed by the remote host
Please suggest me the best way to do it. I am assigning this data to gridview. I am thinking to get the first 500 each time. Is there any way to do it from EF side, so that i won't need to do it in sql.
Thanks
1.add index on your column
2. increase timeout connection
You can create Store procedure
USE LINQ call Store procedure
LINQ to SQL (Part 6 - Retrieving Data Using Stored Procedures)
http://weblogs.asp.net/scottgu/archive/2007/08/16/linq-to-sql-part-6-retrieving-data-using-stored-procedures.aspx
See this answer as well
Calling a SQL Server stored procedure with linq service through c#
Get rid of EF
set key in web.config common key for timeout replace 600
try
{
conn.Open();
mySqlCommand.Connection = conn;
mySqlCommand.CommandTimeout=600;

Delphi map database table as class

A friend of mine asked me how he can at runtime to create a class to 'map' a database table. He is using ADO to connect to the database.
My answer was that he can fill an ADOQuery with a 'select first row from table_name', set the connection to database, open the query, and after that by using a cycle on the ADOQuery.Fields he can get FieldName and FieldType of all the fields from the table. In this way he can have all the fields from the table and their type as members of the class.
There are other solutions to his problem?
#RBA, one way is to define the properties of the class you want to map as "published", then use RTTI to cycle through properties and assign the dataset rows to each property.
Example:
TMyClass = class
private
FName: string;
FAge: Integer;
published
property Name: string read FName write FName;
property Age: Integer read FAge write FAge;
end;
Now, do a query:
myQuery.Sql.Text := 'select * from customers';
myQuery.Open;
while not myQuery.Eof do
begin
myInstance := TMyClass.create;
for I := 0 to myQuery.Fields.Count - 1 do
SetPropValue(myInstance, myQuery.Fields[I].FieldName, myQuery.Fields[I].Value);
// now add myInstance to a TObjectList, for example
myObjectList.Add(myInstance);
Next;
end;
This simple example only works if all fields returned by the query have an exact match in the class.
A more polished example (up to you) should first get a list of properties in the class, then check if the returned field exists in the class.
Hope this helps,
Leonardo.
Not a real class, but something quite similar. Sometime ago I blogged about a solution that might fit into your needs here. It uses an invokeable custom variant for the field mapping that lets you access the fields like properties of a class.
The Delphi Help can be found here and the two part blog post is here and here. The source code can be found in CodeCentral 25386
This is what is called ORM. That is, Object-relational mapping. You have several ORM frameworks available for Delphi. See for instance this SO question.
Of course, don't forget to look at our little mORMot for Delphi 6 up to XE2 - it is able to connect to any database using directly OleDB (without the ADO layer) or other providers. There is a lot of documentation available (more than 600 pages), including general design and architecture aspects.
For example, with mORMot, a database Baby Table is defined in Delphi code as:
/// some enumeration
// - will be written as 'Female' or 'Male' in our UI Grid
// - will be stored as its ordinal value, i.e. 0 for sFemale, 1 for sMale
TSex = (sFemale, sMale);
/// table used for the Babies queries
TSQLBaby = class(TSQLRecord)
private
fName: RawUTF8;
fAddress: RawUTF8;
fBirthDate: TDateTime;
fSex: TSex;
published
property Name: RawUTF8 read fName write fName;
property Address: RawUTF8 read fAddress write fAddress;
property BirthDate: TDateTime read fBirthDate write fBirthDate;
property Sex: TSex read fSex write fSex;
end;
By adding this TSQLBaby class to a TSQLModel instance, common for both Client and Server, the corresponding Baby table is created by the Framework in the database engine. Then the objects are available on both client and server side, via a RESTful link (over HTTP, using JSON for transmission). All SQL work ('CREATE TABLE ...') is done by the framework. Just code in Pascal, and all is done for you. Even the needed indexes will be created by the ORM. And you won't miss any ' or ; in your SQL query any more.
My advice is not to start writing your own ORM from scratch.
If you just want to map some DB tables with objects, you can do it easily. But the more time you'll spend on it, the more complex your solution will become, and you'll definitively reinvent the wheel! So for a small application, this is a good idea. For an application which may grow in the future, consider using an existing (and still maintained) ORM.
Code generation tools such as those used in O/RM solutions can build the classes for you (these are called many things, but I call them Models).
It's not entirely clear what you need (having read your comments as well), but you can use these tools to build whatever it is, not just models. You can build classes that contain lists of field / property associations, or database schema flags, such as "Field X <--> Primary Key Flag", etc.
There are some out there already, but if you want to build an entire O/RM yourself, you can (I did). But that is a much bigger question :) It generally involves adding the generation of code which knows how to query, insert, delete and update your models in the database (called CRUD methods). It's not hard to do, but then you take away your ability to integrate with Delphi's data controls and you'll have to work out a solution for that. Although you don't have to generate CRUD methods, the CRUD support is needed to fully eliminate the need for manual changes to adapt to database schema changes later on.
One of your comments indicated you want to do some schema querying without using the database connection. Is that right? I do this in my models by decorating them with attributes that I can query at runtime. This requires Delphi 2010 and its new RTTI. For example:
[TPrimaryKey]
[TField('EmployeeID', TFieldType.Integer)]
property EmployeeID: integer read GetEmployeeID write SetEmployeeID;
Using RTTI, I can take an instance of a model and ask which field represents the primary key by looking for the one that has the TPrimaryKeyAttribute attribute. Using the TField attribute above provides a link between the property and a database field where they do not have to have the same name. It could even provide a conversion class as a parameter, so that they need not have the same type. There are many possibilities.
I use MyGeneration and write my own templates for this. It's easy and opens up a whole world of possibilities for you, even outside of O/RM.
MyGeneration (free code generation tool)
http://www.mygenerationsoftware.com/
http://sourceforge.net/projects/mygeneration/
MyGeneration tutorial (my blog)
http://interactiveasp.net/blogs/spgilmore/archive/2009/12/03/getting-started-with-mygeneration-a-primer-and-tutorial.aspx
I've taken about 15 mins to write a MyGeneration script that does what you want it to. You'll have to define your Delphi types for the database you're using in the XML, but this script will do the rest. I haven't tested it, and it will probably want to expand it, but it will give you an idea of what you're up against.
<%# reference assembly = "System.Text"%><%
public class GeneratedTemplate : DotNetScriptTemplate
{
public GeneratedTemplate(ZeusContext context) : base(context) {}
private string Tab()
{
return Tab(1);
}
private string Tab(int tabCount)
{
System.Text.StringBuilder sb = new System.Text.StringBuilder();
for (int j = 0; j < 1; j++)
sb.Append(" "); // Two spaces
return sb.ToString();
}
//---------------------------------------------------
// Render() is where you want to write your logic
//---------------------------------------------------
public override void Render()
{
IDatabase db = MyMeta.Databases[0];
%>unit ModelsUnit;
interface
uses
SysUtils;
type
<%
foreach (ITable table in db.Tables)
{
%>
<%=Tab()%>T<%=table.Name%>Model = class(TObject)
<%=Tab()%>protected
<% foreach (IColumn col in table.Columns)
{
%><%=Tab()%><%=Tab()%>f<%=col.Name%>: <%=col.LanguageType%>;
<% }%>
<%=Tab()%>public
<% foreach (IColumn col in table.Columns)
{
%><%=Tab()%><%=Tab()%>property <%=col.Name%>: <%=col.LanguageType%> read f<%=col.Name%> write f<%=col.Name%>;
<% }%>
<%=Tab()%><%=Tab()%>
<%=Tab()%>end;<%
}
%>
implementation
end.
<%
}
}
%>
Here is one of the table classes that was generated by the script above:
TLOCATIONModel = class(TObject)
protected
fLOCATIONID: integer;
fCITY: string;
fPROVINCE: string;
public
property LOCATIONID: integer read fLOCATIONID write fLOCATIONID;
property CITY: string read fCITY write fCITY;
property PROVINCE: string read fPROVINCE write fPROVINCE;
end;
Depending on the database, you could query the INFORMATION_SCHEMA tables/views for what you need. I've done this in an architecture I created and still use in DB applications. When first connecting to a database it queries "data dictionary" type information and stores it for use by the application.

Can an Oracle stored procedure that has a nested table parameter be called from ODP.NET?

I've got a stored procedure that has a couple parameters that are nested tables.
CREATE TYPE FOO_ARRAY AS TABLE OF NUMBER;
CREATE TYPE BAR_ARRAY AS TABLE OF INTEGER;
CREATE PROCEDURE Blah(
iFoos IN FOO_ARRAY,
iBars IN BAR_ARRAY,
oResults OUT SOMECURSORTYPE
) AS
BEGIN
OPEN oResults FOR
SELECT *
FROM SomeTable T
JOIN TABLE(iFoos) foos ON foos.column_value = T.foo
JOIN TABLE(iBars) bars ON bars.column_value = T.bar;
END
Using ODP.NET (Oracle.DataAccess.dll), is there a way to call this stored procedure and pass arrays into these parameters? The only way I've found to pass arrays is if the parameter type is an associative array (a different type of collection that isn't accessible within SQL).
I made it work this way:
Create a type in the database like "create or replace TYPE NT_LNG IS TABLE OF varchar(2);"
Create a Class implementing IOracleCustomType and INullable (SimpleStringArray)
Create a Class implementing IOracleCustomTypeFactory (SimpleStringArrayFactory). Mark it with this attribute "[OracleCustomTypeMappingAttribute("KNL.NT_LNG")]"
and you pass the parameter like this:
SimpleStringArray sa1 = new SimpleStringArray();
sa1.Array = new String[]{"aaa","bbb"};
OracleParameter param = new OracleParameter("p_lngsrc", OracleDbType.Array, sa1, ParameterDirection.Input);
param.UdtTypeName = "KNL.NT_LNG";
Good Luck
Oracle also offers a free tool to generate a custom .NET class that maps to your nested table type:
Download "Oracle Developer Tools for Visual Studio" (free), open Server Explorer, open "User Defined Types" node, find your user defined type, right click and choose "Generate Custom Class".
Here's a walkthrough to get you started with UDTs in general:
http://www.oracle.com/webfolder/technetwork/tutorials/obe/db/hol08/dotnet/udt/udt_otn.htm

Delphi ClientDataset Read-only

I am currently testing with:
A SQLConnection which is pointed towards an IB database.
A SQLDataset that has a SQLConnection field set to the one above.
A DatasetProvider that has the SQLDataset in (2) as its Dataset field value.
A ClientDataset, with the ProviderName field pointing to the provider in (3).
I use the following method (borrowed from Alister Christie) to get the data...
function TForm1.GetCurrEmployee(const IEmployeeID: integer): OleVariant;
const
SQLSELEMP = 'SELECT E.* FROM EMPLOYEE E WHERE E.EMPLOYEEID = %s';
begin
MainDM.SQLDataset1.CommandText := Format(SQLSELEMP, [Edit1.Text]);
Result := MainDM.DataSetProvider1.Data;
end;
Which populates the DBGrid with just one record. However, when I manually edit the record, click on Post, then try to commit the changes, using
MainDM.ClientDataset1.ApplyUpdates(0); // <<<<<<
It bombs, with the message "SQLDataset1: Cannot modify a read-only dataset."
I have checked the ReadOnly property of the Provider, and of the ClientDataset, and the SQL has no joins.
What could be causing the error?
It appears that your ClientDataSet.Data property is being populated from the Data property of the DataSetProvider. With the setup you described, you should be able to simply call ClientDataSet.Open, which will get the data from the DataSetProvider.
BTW, the default behavior of the DataSetProvider when you call the ClientDataSet.ApplyUpdates method is to send a SQL query to the connection object, and not the DataSet from which the data was obtained (assuming a homogeneous query). Make sure that your DataSetProvider.ResolveToDataSet property is not set to true.
Finally, on an unrelated note, your code above appears to be open to a SQL injection attack (though I have not tested this). It is safer to use a parameter to define the WHERE clause. If someone enters the following into Edit1 you might be in trouble (assuming the InterBase uses the drop table syntax): 1;drop table employee;
Check the LiveMode property of the TIBDataSet.

Resources