Creating deletecommand parameters at runtime

N

Nanda

hi,

I am trying to generate parameters for the updatecommand
at runtime.

this.oleDbDeleteCommand1.CommandText=cmdtext;
this.oleDbDeleteCommand1.Connection =this.oleDbConnection1;
this.oleDbDeleteCommand1.Parameters.Add(new
System.Data.OleDb.OleDbParameter("Original_ApplicantName",
dataset.Tables[tablname].Columns[pkey].DataType, 50,
System.Data.ParameterDirection.Input, false, ((System.Byte)
(0)), ((System.Byte)(0)), pkey,
System.Data.DataRowVersion.Original, null));

I got an error which says that unable to convert
System.Type to System.Data.OleDb.OleDbtype

how can i achive this.
Regds,
Nanda
 
C

codewriter

Why don't you use something like this OleDbType.Char instead of
dataset.Tables[tablname].Columns[pkey].DataType.
Don't you know what data type should be there?
 
T

Theo Yaung [Microsoft]

Hi Nanda,

This particular issue is an example of runtime types being different for
all databases and the programming languages that access them.

Luckily, in .NET, the runtime types are all unified for C#, VB, and any
other .NET language. However, for databases, often types are specialized,
namely for efficiency purposes. Hence, almost always will they use a
different type system than the .NET type system.

Your expression:
dataset.Tables[tablname].Columns[pkey].DataType

looks up a specific DataColumn instance and uses the contents of the
DataType property. In the .NET Framework documentation, you can see that
the type returned for the DataColumn.DataType property is System.Type.
This means that it returns the type object corresponding to the actual .NET
type used by that column. This information is great for creating .NET
objects at runtime that depend on this type object, such as arrays to hold
query results.

However, if you examine the OleDbParameter constructor that you call, you
notice that the second parameter is actually calling for a
System.Data.OleDb.OleDbType. This type is unrelated to System.Type
(neither type subclasses each other, and that the only common supertype
between them is System.Object).

In the .NET Framework docs for the DataSet, it describes it as an
"in-memory cache of data retrieved from a data source". By the time you
are pulling your data from the DataSet, your data has already been
converted to the relevant .NET system type.

If you used a OleDbDataReader to pull out your data in the first place, you
can find out the relevant OleDbType object from the columns as such:

Given a string name to your column, with the variable name myColumnName:

string myColumnName = << some value here >>;
OleDbDataReader myReader = << reader from query >>;
OleDbType columnType = (OleDbType)
Enum.Parse(typeof(OleDbType),myReader.GetDataTypeName(myReader.GetOrdinal(my
ColumnName)));

The third line basically asks the reader for the ordinal (which column
number) for the column name, and then the name of the backing OleDB data
type. If you are using values derived from a form, or some sort of
extraneous information (not from a query to that table), then you will need
to know the column type beforehand.

You can do this by creating some sort of lookup table (either an in-memory
hashtable, or a database table), or any function that will map the column
name to the proper OleDbType needed. Using the Columns property of the
DataTable won't work, because DataSets and DataTables are used primary for
manipulating tables of data already converted to .NET types.

Does this answer your question to your satisfaction? Feel free to reply to
the newsgroup on this post if you would like some further elaboration.

Hope this helps,

Theo Yaung
Visual Studio .NET
Microsoft Corp.

This posting is provided "AS IS" with no warranties, and confers no rights.

--------------------
Content-Class: urn:content-classes:message
From: "Nanda" <[email protected]>
Sender: "Nanda" <[email protected]>
References: <[email protected]>
Subject: Re: Creating deletecommand parameters at runtime
Date: Sun, 3 Aug 2003 23:25:39 -0700
Lines: 40
Message-ID: <[email protected]>
MIME-Version: 1.0
Content-Type: text/plain;
charset="iso-8859-1"
Content-Transfer-Encoding: 7bit
X-Newsreader: Microsoft CDO for Windows 2000
X-MimeOLE: Produced By Microsoft MimeOLE V5.50.4910.0300
Thread-Index: AcNaUTlAQNGpwbaUQQaS95qshQ+oTA==
Newsgroups: microsoft.public.dotnet.languages.csharp
Path: cpmsftngxa06.phx.gbl
Xref: cpmsftngxa06.phx.gbl microsoft.public.dotnet.languages.csharp:173917
NNTP-Posting-Host: TK2MSFTNGXA13 10.40.1.165
X-Tomcat-NG: microsoft.public.dotnet.languages.csharp

No i dont know the datatype of the field. bcoz i hav to
generate parameters so that the datatable may vary
according to the user's selection.

-----Original Message-----
Why don't you use something like this OleDbType.Char instead of
dataset.Tables[tablname].Columns[pkey].DataType.
Don't you know what data type should be there?

Nanda said:
hi,

I am trying to generate parameters for the updatecommand
at runtime.

this.oleDbDeleteCommand1.CommandText=cmdtext;
this.oleDbDeleteCommand1.Connection =this.oleDbConnection1;
this.oleDbDeleteCommand1.Parameters.Add(new
System.Data.OleDb.OleDbParameter ("Original_ApplicantName",
dataset.Tables[tablname].Columns[pkey].DataType, 50,
System.Data.ParameterDirection.Input, false, ((System.Byte)
(0)), ((System.Byte)(0)), pkey,
System.Data.DataRowVersion.Original, null));

I got an error which says that unable to convert
System.Type to System.Data.OleDb.OleDbtype

how can i achive this.
Regds,
Nanda


.


--

This posting is provided "AS IS" with no warranties, and confers no rights.
Use of included script samples are subject to the terms specified at
http://www.microsoft.com/info/cpyright.htm

Note: For the benefit of the community-at-large, all responses to this
message are best directed to the newsgroup/thread from which they
originated.
 
N

Nanda

Hi Theo Yaung,
Thanks for your clear explanation. I have managed
to get rid of the problem, by using a function to map the
System.Type to OleDbType.

private void ConvertToOleDb(System.Type type,ref
OleDbType datatype)
{
if(type.ToString().Equals("adVarWChar"))
datatype=OleDbType.VarWChar;
else if(type.ToString().Equals("adDouble"))
datatype=OleDbType.Double;
else if(type.ToString().Equals("adDBDate"))
datatype=OleDbType.DBDate;
else if(type.ToString().Equals("adCurrency"))
datatype=OleDbType.Currency;
}

Can u comment on this way. I even expect some other
better approach from u.
Regds.
Nanda
 
J

Jon Skeet

Nanda said:
Thanks for your clear explanation. I have managed
to get rid of the problem, by using a function to map the
System.Type to OleDbType.

private void ConvertToOleDb(System.Type type,ref
OleDbType datatype)
{
if(type.ToString().Equals("adVarWChar"))
datatype=OleDbType.VarWChar;
else if(type.ToString().Equals("adDouble"))
datatype=OleDbType.Double;
else if(type.ToString().Equals("adDBDate"))
datatype=OleDbType.DBDate;
else if(type.ToString().Equals("adCurrency"))
datatype=OleDbType.Currency;
}

Can u comment on this way. I even expect some other
better approach from u.

Firstly, I'd suggest returning the datatype rather than passing it by
reference - unless you really want it to have "defaulting" behaviour
(which you can also get by passing in the datatype by value and
returning it if necessary). I almost always avoid passing things by
reference.

Secondly, I'd suggest just using a Hashtable. Something like:

static readonly Hashtable TypeToOleDbTypeMap = new Hashtable();
static MyClassName()
{
TypeToOleDbType[typeof(adVarWChar)]=OleDbType.VarWChar;
TypeToOleDbType[typeof(adDouble)]=OleDbType.Double;
TypeToOleDbType[typeof(adDBDate)]=OleDbType.DBDate;
TypeToOleDbType[typeof(adCurrency)]=OleDbType.Currency;
}

static OleDbType ConvertToOleDb (Type type, OleDbType defaultValue)
{
OleDbType ret = (OleDbType) TypeToOleDbType[type];
if (ret==null)
ret=defaultValue;
return ret;
}
 
C

codewriter

Nanda,

Your function will not run as you expect it to run. Here is the correct
version that I sent you yesterday by email:

private OleDbType ConvertToOleDb(Type type)

{

OleDbType datatype = new OleDbType();

if(type.ToString()=="System.Int32")

datatype=OleDbType.Integer;

else if(type.ToString()=="System.Char")

datatype=OleDbType.Char;

else if(type.ToString()=="System.Decimal")

datatype=OleDbType.Decimal;

else if(type.ToString()=="System.Double")

datatype=OleDbType.Double;

else if(type.ToString()=="System.Single")

datatype=OleDbType.Single;

else if(type.ToString()=="System.String")

datatype=OleDbType.VarChar;

return datatype;

}
 
T

Theo Yaung [Microsoft]

Hi All,

"CodeWriter" is correct in that your function must compare the Type objects
against the .NET System types. However, using the string-formatted name is
probably not the best way to compare objects that inherit from System.Type.

In the .NET Framework docs, under System.Type, you can read that the
Equals(...) method will return true for comparing types. Thus, you could
write your function as:

private void ConvertToOleDb(System.Type type,
out OleDbType datatype) {
if(type.Equals(typeof(System.String)))
datatype=OleDbType.VarWChar;
else if(type.Equals(typeof(System.Double)))
datatype=OleDbType.Double;
else if(type.Equals(typeof(System.DateTime)))
datatype=OleDbType.DBDate;
else if(type.Equals(typeof(System.Decimal)))
datatype=OleDbType.Currency;
else {
// add "last ditch" catch-all, or error here

// you MUST assign datatype in order for this to compile

datatype = OleDbType.Error; // TODO: change this
}
}

Notice that I also changed the declaration of datatype to an "out"
parameter rather than a ref parameter. Ref parameters allow you to read
the value, or write on the variable accordingly. However, "out" parameters
are even more specialized in that they must be written to somewhere in the
function. Thus, you enter a contract with the compiler -- once you declare
a parameter as "out", the parameter will check for you that in any
circumstance imaginable, the parameter is always written to. "Out"
parameters (a.k.a. Output Parameters) are also different in that the
variable that is passed into a function with an out parameter need not be
initialized (it makes sense, since that function is guaranteed to write to
that variable).

If you use output parameters, you also have to write "out" in front of the
variable before you pass it to the function, instead of "ref".

Overall, this approach is okay for certain circumstances. However, it is
pretty limiting because it makes some serious assumptions. It assumes that
your database has only one OleDbType that is mapping to each .NET system
type. That is very rarely the case for large databases, and still seldom
the case except for the very smallest of databases.

In databases, each column has a type, based on that database's
architecture. Each type has its benefits and limitations, and thus, it's
worth it to break database types into many many different fundamental
datatypes. However, these benefits and limitations are often very
performance driven, and mostly applicable for optimizing storage. When you
access this data, it becomes converted to more convenient forms, and
multiple database types get converted into the same .NET System types.

Take for example a table that uses OleDb's char(8) to represent a username,
and a varchar field to represent the full name. When converted to .NET
system types, they both become System.String objects. Thus, if you try to
write function for this table to map from .NET system type to OleDb type,
then you have an ambiguity when you're mapping for System.String. In
either the char(8) or the varchar instance, something might break because
the types won't line up.

So, you need more information. The safest way is to make an entry for each
table and column pair with which you plan on using this function. Using
a hashtable, as suggested in Jon Skeet's post, is an excellent means of
mapping data in this scenario.

The most convenient way is to make a hashtable for each table that you plan
on using this "reverse lookup" (maybe you're only using it on one table,
for a particular application or web form).

Say I had the following table columns:
varwchar full_name;
char(8) user_name;
dbdate birth_date;
dbtimestamp last_login;

This would be a pathological case for trying to "reverse lookup" the .NET
system type, since both full_name and user_names would come back as
System.String objects, and birth_date and last_login would be
System.DateTime objects. Thus, knowing the column is critical to picking
the right OleDb type in this instance.

Let's say you used a Hashtable object named lookupTable. You can use the
following code to initialize it:

lookupTable = new System.Collections.Hashtable();
lookupTable["full_name"] = OleDbType.VarWChar;
lookupTable["user_name"] = OleDbType.Char;
lookupTable["birth_date"] = OleDbType.DBDate;
lookupTable["last_login"] = OleDbType.DBTimeStamp;

Then you could write a lookup function, similar to yours, but taking in the
string column name:

private void LookupOleDbType(string colName, out OleDbType datatype) {
datatype = (OleDbType) lookupTable[colName];
}

You really don't need the additional lookup function, except it may be
convenient to keep your cast to OleDbType in only one place. Also, Jon
Skeet raises a good point that output parameters or reference parameters
should probably be avoided in exchange for using a return value. This
keeps the code clear, in that if you are going to write a new value to the
variable, it should be done somewhere that it originally had scope.
Though, output parameters and reference parameters are sometimes the only
way to return more than one value, or to write swap functions, etc.

Hope this helps,

Theo Yaung
Visual Studio .NET
Microsoft Corp.

This posting is provided "AS IS" with no warranties, and confers no rights.

--------------------
From: "codewriter" <[email protected]>
Newsgroups: microsoft.public.dotnet.languages.csharp
Subject: Re: Creating deletecommand parameters at runtime
Date: Tue, 5 Aug 2003 11:12:40 -0400
Organization: Bell Sympatico

Nanda,

Your function will not run as you expect it to run. Here is the correct
version that I sent you yesterday by email:

private OleDbType ConvertToOleDb(Type type)

{

OleDbType datatype = new OleDbType();

if(type.ToString()=="System.Int32")

datatype=OleDbType.Integer;

else if(type.ToString()=="System.Char")

datatype=OleDbType.Char;

else if(type.ToString()=="System.Decimal")

datatype=OleDbType.Decimal;

else if(type.ToString()=="System.Double")

datatype=OleDbType.Double;

else if(type.ToString()=="System.Single")

datatype=OleDbType.Single;

else if(type.ToString()=="System.String")

datatype=OleDbType.VarChar;

return datatype;

}


--

This posting is provided "AS IS" with no warranties, and confers no rights.
Use of included script samples are subject to the terms specified at
http://www.microsoft.com/info/cpyright.htm

Note: For the benefit of the community-at-large, all responses to this
message are best directed to the newsgroup/thread from which they
originated.
 
C

codewriter

Hi everyone.
I gave Nanda this idea about comparing the datatypes in a separate function
for using in his stored procedure simply because he wanted to do it this
way. My opinion is that this approach is not good at all. Database tables
always have types defined, i.e. Integer, VarChar, etc. Passing a parameter
to a stored procedure without knowing what type this parameter is will
create problems and bugs. I'd prefer always to specify the type of a
parameter without relying on the user's choice. The idea of creating mapping
for different datatypes for different tables does not really attract me.
Imagine having 100 tables with average 10 fields in them in your database
and map them to different datatypes. It will be very tidious, timeconsuming,
and eventually produce bugs.

Theo Yaung said:
Hi All,

"CodeWriter" is correct in that your function must compare the Type objects
against the .NET System types. However, using the string-formatted name is
probably not the best way to compare objects that inherit from System.Type.

In the .NET Framework docs, under System.Type, you can read that the
Equals(...) method will return true for comparing types. Thus, you could
write your function as:

private void ConvertToOleDb(System.Type type,
out OleDbType datatype) {
if(type.Equals(typeof(System.String)))
datatype=OleDbType.VarWChar;
else if(type.Equals(typeof(System.Double)))
datatype=OleDbType.Double;
else if(type.Equals(typeof(System.DateTime)))
datatype=OleDbType.DBDate;
else if(type.Equals(typeof(System.Decimal)))
datatype=OleDbType.Currency;
else {
// add "last ditch" catch-all, or error here

// you MUST assign datatype in order for this to compile

datatype = OleDbType.Error; // TODO: change this
}
}

Notice that I also changed the declaration of datatype to an "out"
parameter rather than a ref parameter. Ref parameters allow you to read
the value, or write on the variable accordingly. However, "out" parameters
are even more specialized in that they must be written to somewhere in the
function. Thus, you enter a contract with the compiler -- once you declare
a parameter as "out", the parameter will check for you that in any
circumstance imaginable, the parameter is always written to. "Out"
parameters (a.k.a. Output Parameters) are also different in that the
variable that is passed into a function with an out parameter need not be
initialized (it makes sense, since that function is guaranteed to write to
that variable).

If you use output parameters, you also have to write "out" in front of the
variable before you pass it to the function, instead of "ref".

Overall, this approach is okay for certain circumstances. However, it is
pretty limiting because it makes some serious assumptions. It assumes that
your database has only one OleDbType that is mapping to each .NET system
type. That is very rarely the case for large databases, and still seldom
the case except for the very smallest of databases.

In databases, each column has a type, based on that database's
architecture. Each type has its benefits and limitations, and thus, it's
worth it to break database types into many many different fundamental
datatypes. However, these benefits and limitations are often very
performance driven, and mostly applicable for optimizing storage. When you
access this data, it becomes converted to more convenient forms, and
multiple database types get converted into the same .NET System types.

Take for example a table that uses OleDb's char(8) to represent a username,
and a varchar field to represent the full name. When converted to .NET
system types, they both become System.String objects. Thus, if you try to
write function for this table to map from .NET system type to OleDb type,
then you have an ambiguity when you're mapping for System.String. In
either the char(8) or the varchar instance, something might break because
the types won't line up.

So, you need more information. The safest way is to make an entry for each
table and column pair with which you plan on using this function. Using
a hashtable, as suggested in Jon Skeet's post, is an excellent means of
mapping data in this scenario.

The most convenient way is to make a hashtable for each table that you plan
on using this "reverse lookup" (maybe you're only using it on one table,
for a particular application or web form).

Say I had the following table columns:
varwchar full_name;
char(8) user_name;
dbdate birth_date;
dbtimestamp last_login;

This would be a pathological case for trying to "reverse lookup" the .NET
system type, since both full_name and user_names would come back as
System.String objects, and birth_date and last_login would be
System.DateTime objects. Thus, knowing the column is critical to picking
the right OleDb type in this instance.

Let's say you used a Hashtable object named lookupTable. You can use the
following code to initialize it:

lookupTable = new System.Collections.Hashtable();
lookupTable["full_name"] = OleDbType.VarWChar;
lookupTable["user_name"] = OleDbType.Char;
lookupTable["birth_date"] = OleDbType.DBDate;
lookupTable["last_login"] = OleDbType.DBTimeStamp;

Then you could write a lookup function, similar to yours, but taking in the
string column name:

private void LookupOleDbType(string colName, out OleDbType datatype) {
datatype = (OleDbType) lookupTable[colName];
}

You really don't need the additional lookup function, except it may be
convenient to keep your cast to OleDbType in only one place. Also, Jon
Skeet raises a good point that output parameters or reference parameters
should probably be avoided in exchange for using a return value. This
keeps the code clear, in that if you are going to write a new value to the
variable, it should be done somewhere that it originally had scope.
Though, output parameters and reference parameters are sometimes the only
way to return more than one value, or to write swap functions, etc.

Hope this helps,

Theo Yaung
Visual Studio .NET
Microsoft Corp.

This posting is provided "AS IS" with no warranties, and confers no rights.
--------------------
From: "codewriter" <[email protected]>
Newsgroups: microsoft.public.dotnet.languages.csharp
Subject: Re: Creating deletecommand parameters at runtime
Date: Tue, 5 Aug 2003 11:12:40 -0400
Organization: Bell Sympatico

Nanda,

Your function will not run as you expect it to run. Here is the correct
version that I sent you yesterday by email:

private OleDbType ConvertToOleDb(Type type)

{

OleDbType datatype = new OleDbType();

if(type.ToString()=="System.Int32")

datatype=OleDbType.Integer;

else if(type.ToString()=="System.Char")

datatype=OleDbType.Char;

else if(type.ToString()=="System.Decimal")

datatype=OleDbType.Decimal;

else if(type.ToString()=="System.Double")

datatype=OleDbType.Double;

else if(type.ToString()=="System.Single")

datatype=OleDbType.Single;

else if(type.ToString()=="System.String")

datatype=OleDbType.VarChar;

return datatype;

}


--

This posting is provided "AS IS" with no warranties, and confers no rights.
Use of included script samples are subject to the terms specified at
http://www.microsoft.com/info/cpyright.htm

Note: For the benefit of the community-at-large, all responses to this
message are best directed to the newsgroup/thread from which they
originated.
 
N

Nanda

Hi all,
Thanks for your nice inputs.
First I should explain the actual scenario of this
problem.
Actually the database(MSAccess) is being created at
runtime by another guy in my team.
Now this database comes to me. I have no knowledge of
this database. My aim is to show this database in the
datagrid and allow users to modify the data.
So, to allow the users to modify the data, i've
generated commands to insert,update,delete. Upto this
i've no problem, since i can read the columnname from my
dataset for building these insert,update,delete commands.

But when i try to add parameters for these commands, i
need to know the datatype of each column. There comes the
problem. And i've managed this from suggestions given by
"codewriter" and "Theo Yaung".
I hope now i have given the complete scenario of the
problem.

Now it works fine,

But i need some clarifications from codewriter.

if(type.ToString().Equals("adVarWChar"))
type.ToString() will give string representation of
System.Type "type"
so it will return a string type, now
string.Equals("advarwchar") will compare the value
returned by ToString() with "advarwachar", so i think i am
not wrong here.
So
if(type.ToString().Equals("adVarWChar"))
this statement didnt gives me problem.

if i am wrong, correct me.

Fortunately, the type of columns in the databse would be
limited (while creating the
database)to only few types.
For example., the types used will be mostly,
varchar,double,currency,datetime,boolean,integer
So, i think using hashtable suggested by "Jon Skeet" wont
harm the performance. And no "ambiguty" situation would
arise as said by "Theo Yaung".

So now my requirment is to map these System.Types
(varchar,double,currency,datetime,boolean,integer) to
appropriate OledbType.
i like to know is there any performance issue in using
hashtable over a separate function.

I would like to post some of the code which i have tried.

for(int i=0;i<dataset.Tables[tablname].Columns.Count;i++)
{
ConvertToOleDb(dataset.Tables[tablname].Columns
,DataType,out datatype);
this.oleDbUpdateCommand1.Parameters.Add(new
System.Data.OleDb.OleDbParameter(cat.Tables
[tablname].Columns.Name,datatype, 50,
dataset.Tables[tablname].Columns.Name));
}

By this way, i've generated parameters for
update,insert,delete commands.

Thanks and Regards,
Nanda
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Top