Generic Method Help

R

rbjorkquist

I'm looking for help and understanding of generics; specifically, the
use of generic methods. I'm new to using generics and haven't found a
good example that applies to what I'm trying to do.

I have a web server, feeding data through web methods, to a windows
application. This is not a problem and all works. However, I am
trying to refactor my code and thought I could use generic methods.

I have two main types of web methods. One type always retrieves is
data from the database. This type does not do anything with the
application cache. The other first tries to retrieve its data from
the application cache. If this fails, it tries to retrieve its data
from an associated XML file. If that fails, it then retrieves the
data from the database, creates the associated XML file, and then
loads it into the application cache.

For all the methods that use the second type, the main difference is
the return type (a dataset), the cache name, the SQL command text, and
the source table. But the main syntax and code if the same. This is
why I thought I could use a generic method. But I get compile errors.

1 - Cannot create an instance of the variable type 'T' because it does
not have the new() constraint

2 - 'T' does not contain a definition for 'ReadXml'

3 - The best overloaded method match for
'System.Data.Common.DbDataAdapter.Fill(System.Data.DataSet, string)'
has some invalid arguments

4 - Argument '1': cannot convert from 'T' to 'System.Data.DataSet'

5 - 'T' does not contain a definition for 'WriteXml'

Even if I add "where T : new()" to the generic methods definition, I
still get errors 2, 3, 4, and 5. I just don't get what I'm missing.
Any help would be greatly appreciated.


//-------------------------------------------------------------------------------------------
[WebMethod]
public PasteViscosities GetPasteViscosities(){
StringBuilder CommandText = new StringBuilder(250);

CommandText.Append("SELECT");
CommandText.Append(" [PasteViscosityID]");
CommandText.Append(",[PasteViscosity]");
CommandText.Append(",[PasteViscosityMinimumValue]");
CommandText.Append(",[PasteViscosityMaximumValue] ");
CommandText.Append("FROM");
CommandText.Append(" [ccivw_PasteViscosities] ");
CommandText.Append("ORDER BY");
CommandText.Append(" [PasteViscosity] ASC");

PasteViscosities PasteViscosityList = null;

GetData<PasteViscosities>(ref PasteViscosityList,
"PasteViscosities",
"PasteViscosities.xml",
CommandText.ToString(),
"ccivw_PasteViscosities");

return(PasteViscosityList);
}

//-------------------------------------------------------------------------------------------
private void GetData<T>(ref T MyDataList, string CacheName, string
XMLFile, string SQLStatement, string SourceTable){

ConnectionStringSettings Cs =
ConfigurationManager.ConnectionStrings["EfficiencyConnectionString"];
MyDataList = (T)HttpRuntime.Cache[CacheName];

if(MyDataList == null){

//----------------------------------------------------
//ERROR # 1
//----------------------------------------------------
MyDataList = new T();
//----------------------------------------------------

SqlDataAdapter SqlDA = new SqlDataAdapter();
SqlConnection SqlCon = new SqlConnection();
SqlCommand SqlCmd = new SqlCommand();

try{
//----------------------------------------------------
//ERROR # 2
//----------------------------------------------------
MyDataList.ReadXml(Server.MapPath(XMLFile),
XmlReadMode.InferTypedSchema);
//----------------------------------------------------
}//END TRY-BLOCK
catch(System.IO.FileNotFoundException){
SqlCmd.CommandText = SQLStatement;
SqlCmd.Connection = SqlCon;

SqlCon.ConnectionString = (string)Cs.ConnectionString;
SqlCon.Open();

SqlDA.SelectCommand = SqlCmd;
//----------------------------------------------------
//ERROR # 3
//ERROR # 4
//----------------------------------------------------
SqlDA.Fill(MyDataList, SourceTable);
//----------------------------------------------------

//----------------------------------------------------
//ERROR # 5
//----------------------------------------------------
MyDataList.WriteXml(Server.MapPath(XMLFile));
//----------------------------------------------------

HttpRuntime.Cache.Insert(CacheName,
MyDataList,
new
CacheDependency(Server.MapPath(XMLFile)),

Cache.NoAbsoluteExpiration,

Cache.NoSlidingExpiration,
CacheItemPriority.Low,

null);
}//END CATCH-BLOCK
catch(Exception E){
//Writes error message to the event log
HandleWebException(E);
}//END CATCH-BLOCK
finally{
SqlCon.Close();
}//END FINALLY-BLOCK
}//END IF-STATEMENT "if(MyDataList == null)"
}
 
G

Guest

hi,
this is a good guide for generics
http://msdn2.microsoft.com/en-us/library/512aeb7t(VS.80).aspx


regards,
Husam Al-A'araj
www.aaraj.net

rbjorkquist said:
I'm looking for help and understanding of generics; specifically, the
use of generic methods. I'm new to using generics and haven't found a
good example that applies to what I'm trying to do.

I have a web server, feeding data through web methods, to a windows
application. This is not a problem and all works. However, I am
trying to refactor my code and thought I could use generic methods.

I have two main types of web methods. One type always retrieves is
data from the database. This type does not do anything with the
application cache. The other first tries to retrieve its data from
the application cache. If this fails, it tries to retrieve its data
from an associated XML file. If that fails, it then retrieves the
data from the database, creates the associated XML file, and then
loads it into the application cache.

For all the methods that use the second type, the main difference is
the return type (a dataset), the cache name, the SQL command text, and
the source table. But the main syntax and code if the same. This is
why I thought I could use a generic method. But I get compile errors.

1 - Cannot create an instance of the variable type 'T' because it does
not have the new() constraint

2 - 'T' does not contain a definition for 'ReadXml'

3 - The best overloaded method match for
'System.Data.Common.DbDataAdapter.Fill(System.Data.DataSet, string)'
has some invalid arguments

4 - Argument '1': cannot convert from 'T' to 'System.Data.DataSet'

5 - 'T' does not contain a definition for 'WriteXml'

Even if I add "where T : new()" to the generic methods definition, I
still get errors 2, 3, 4, and 5. I just don't get what I'm missing.
Any help would be greatly appreciated.


//-------------------------------------------------------------------------------------------
[WebMethod]
public PasteViscosities GetPasteViscosities(){
StringBuilder CommandText = new StringBuilder(250);

CommandText.Append("SELECT");
CommandText.Append(" [PasteViscosityID]");
CommandText.Append(",[PasteViscosity]");
CommandText.Append(",[PasteViscosityMinimumValue]");
CommandText.Append(",[PasteViscosityMaximumValue] ");
CommandText.Append("FROM");
CommandText.Append(" [ccivw_PasteViscosities] ");
CommandText.Append("ORDER BY");
CommandText.Append(" [PasteViscosity] ASC");

PasteViscosities PasteViscosityList = null;

GetData<PasteViscosities>(ref PasteViscosityList,
"PasteViscosities",
"PasteViscosities.xml",
CommandText.ToString(),
"ccivw_PasteViscosities");

return(PasteViscosityList);
}

//-------------------------------------------------------------------------------------------
private void GetData<T>(ref T MyDataList, string CacheName, string
XMLFile, string SQLStatement, string SourceTable){

ConnectionStringSettings Cs =
ConfigurationManager.ConnectionStrings["EfficiencyConnectionString"];
MyDataList = (T)HttpRuntime.Cache[CacheName];

if(MyDataList == null){

//----------------------------------------------------
//ERROR # 1
//----------------------------------------------------
MyDataList = new T();
//----------------------------------------------------

SqlDataAdapter SqlDA = new SqlDataAdapter();
SqlConnection SqlCon = new SqlConnection();
SqlCommand SqlCmd = new SqlCommand();

try{
//----------------------------------------------------
//ERROR # 2
//----------------------------------------------------
MyDataList.ReadXml(Server.MapPath(XMLFile),
XmlReadMode.InferTypedSchema);
//----------------------------------------------------
}//END TRY-BLOCK
catch(System.IO.FileNotFoundException){
SqlCmd.CommandText = SQLStatement;
SqlCmd.Connection = SqlCon;

SqlCon.ConnectionString = (string)Cs.ConnectionString;
SqlCon.Open();

SqlDA.SelectCommand = SqlCmd;
//----------------------------------------------------
//ERROR # 3
//ERROR # 4
//----------------------------------------------------
SqlDA.Fill(MyDataList, SourceTable);
//----------------------------------------------------

//----------------------------------------------------
//ERROR # 5
//----------------------------------------------------
MyDataList.WriteXml(Server.MapPath(XMLFile));
//----------------------------------------------------

HttpRuntime.Cache.Insert(CacheName,
MyDataList,
new
CacheDependency(Server.MapPath(XMLFile)),

Cache.NoAbsoluteExpiration,

Cache.NoSlidingExpiration,
CacheItemPriority.Low,

null);
}//END CATCH-BLOCK
catch(Exception E){
//Writes error message to the event log
HandleWebException(E);
}//END CATCH-BLOCK
finally{
SqlCon.Close();
}//END FINALLY-BLOCK
}//END IF-STATEMENT "if(MyDataList == null)"
}
 
I

Ignacio Machin \( .NET/ C# MVP \)

Hi,

rbjorkquist said:
I'm looking for help and understanding of generics; specifically, the
use of generic methods. I'm new to using generics and haven't found a
good example that applies to what I'm trying to do.

Take a look at the first links in this result
http://www.google.com/search?sourceid=navclient&ie=UTF-8&rlz=1T4GGIH_enUS235US235&q=generic+methods

For all the methods that use the second type, the main difference is
the return type (a dataset), the cache name, the SQL command text, and
the source table. But the main syntax and code if the same. This is
why I thought I could use a generic method. But I get compile errors.

Then maybe you have a overloaded method, and not a generic one.
1 - Cannot create an instance of the variable type 'T' because it does
not have the new() constraint

This means that the type should have a constructor with no paramets:
public GetPasteViscosities()
2 - 'T' does not contain a definition for 'ReadXml'

In general T is treated as Object, if you want that T implement a given
interface or be of certain type you ahve to declare as so.
Take a look at this link
http://msdn2.microsoft.com/en-us/library/d5x73970(VS.80).aspx
 
R

rbjorkquist

Hi,




Take a look at the first links in this resulthttp://www.google.com/search?sourceid=navclient&ie=UTF-8&rlz=1T4GGIH_...




Then maybe you have a overloaded method, and not a generic one.


This means that the type should have a constructor with no paramets:
public GetPasteViscosities()


In general T is treated as Object, if you want that T implement a given
interface or be of certain type you ahve to declare as so.
Take a look at this linkhttp://msdn2.microsoft.com/en-us/library/d5x73970(VS.80).aspx

Ignacio,

I will look into the links you gave. But in the mean time, I don't
believe I can use overloaded methods, because a method can not simply
be overloaded by its return type, correct?

Initially this is how I wanted to use it. Do you have any other
suggestions and/or examples? The variable, "PasteViscosities", is a
System.Data.DataSet object.

Method defintion:
private T GetData<T>(string, string, string, string){
T TmpVal = new T();
:
return(TmpVal);
}

Method Call:
{
:
PasteViscosities Viscosity =
GetData<PasteViscosities>("PasteViscosities", "PasteViscosities.xml",
CommandText.ToString(), "ccivw_PasteViscosities");
:
}
 
I

Ignacio Machin \( .NET/ C# MVP \)

Hi,

rbjorkquist said:
On Oct 9, 12:40 pm, "Ignacio Machin \( .NET/ C# MVP \)" <machin TA
laceupsolutions.com> wrote:
Ignacio,

I will look into the links you gave. But in the mean time, I don't
believe I can use overloaded methods, because a method can not simply
be overloaded by its return type, correct?

You are correct.Sorry for not read your post in more detail. But your post
was also misleading, the fact that the content of the dataset is different
does not make it a different return value, it's still a Dataset.
Initially this is how I wanted to use it. Do you have any other
suggestions and/or examples? The variable, "PasteViscosities", is a
System.Data.DataSet object.

Honestly, I do not think you need generics here, you will always return a
Dataset. You can accomplish the same using:
private DataSet GetData(string, string, string, string)


You use generics when the type is not know in advance AND you can treat all
those types in the same way.
 
R

rbjorkquist

Ignacio,

I will look into the links you gave. But in the mean time, I don't
believe I can use overloaded methods, because a method can not simply
be overloaded by its return type, correct?

Initially this is how I wanted to use it. Do you have any other
suggestions and/or examples? The variable, "PasteViscosities", is a
System.Data.DataSet object.

Method defintion:
private T GetData<T>(string, string, string, string){
T TmpVal = new T();
:
return(TmpVal);

}

Method Call:
{
:
PasteViscosities Viscosity =
GetData<PasteViscosities>("PasteViscosities", "PasteViscosities.xml",
CommandText.ToString(), "ccivw_PasteViscosities");
:

}

Ok, it looks like I've found a solution. I added the "where T :
DataSet, new()" to the generic method definition. I know would like
to know, why does this work? Is this a solution or does this just
happen to work, when really the result is undefined?

Method defintion:
private T GetData<T>(string, string, string, string) where T :
DataSet, new(){
T TmpVal = new T();
:
return(TmpVal);
}


Method Call:
public PasteViscosities GetPasteViscosities(){
:
return(GetData<PasteViscosities>("PasteViscosities",
"PasteViscosities.xml",
CommandText.ToString(), "ccivw_PasteViscosities"));
:
}
 
R

rbjorkquist

Hi,





You are correct.Sorry for not read your post in more detail. But your post
was also misleading, the fact that the content of the dataset is different
does not make it a different return value, it's still a Dataset.


Honestly, I do not think you need generics here, you will always return a
Dataset. You can accomplish the same using:
private DataSet GetData(string, string, string, string)

You use generics when the type is not know in advance AND you can treat all
those types in the same way.

Ignacio,

I made another omission. The datasets are typed-datasets. So each
dataset is a different object. Sorry for not paying enough attention
when I wrote my original post.
 
P

Peter Duniho

rbjorkquist said:
Ok, it looks like I've found a solution. I added the "where T :
DataSet, new()" to the generic method definition. I know would like
to know, why does this work? Is this a solution or does this just
happen to work, when really the result is undefined?

The result is well-defined, as near as I can tell. The reason it works
is that you've "constrained" the type T, requiring that it inherits from
DataSet, and has a default constructor. Those requirements allow you to
write the code you wrote.

However, as Ignacio has pointed out, it's not clear at all that this is
really a situation in which you need a generic method. For sure,
there's no need to have the return value be of the generic type. You
can just return a DataSet. The caller will have to cast it back to the
intended type (e.g. to PasteViscosities), but you have to specify the
type in the use of the generic method anyway, so it's not like this
really saves you anything.

So, with the return type being essentially a wash, it comes down to the
need to be able to instantiate one of these things in your method.
Assuming you really have that need, then maybe it does in fact makes
sense to use a generic method. But you should be clear of that need.
Architecturally, it could be the case that there's a better way to do
what you're doing than using a generic method (for example, some sort of
data-driven factory class that instantiates the right type based on
the XML being read, and which case be called from a non-generic method).

I'm not saying what you've got is wrong. I'm just saying you should
think about the design and make sure that a generic method is really
what you want here.

Pete
 
R

rbjorkquist

The result is well-defined, as near as I can tell. The reason it works
is that you've "constrained" the type T, requiring that it inherits from
DataSet, and has a default constructor. Those requirements allow you to
write the code you wrote.

However, as Ignacio has pointed out, it's not clear at all that this is
really a situation in which you need a generic method. For sure,
there's no need to have the return value be of the generic type. You
can just return a DataSet. The caller will have to cast it back to the
intended type (e.g. to PasteViscosities), but you have to specify the
type in the use of the generic method anyway, so it's not like this
really saves you anything.

So, with the return type being essentially a wash, it comes down to the
need to be able to instantiate one of these things in your method.
Assuming you really have that need, then maybe it does in fact makes
sense to use a generic method. But you should be clear of that need.
Architecturally, it could be the case that there's a better way to do
what you're doing than using a generic method (for example, some sort of
data-driven factory class that instantiates the right type based on
the XML being read, and which case be called from a non-generic method).

I'm not saying what you've got is wrong. I'm just saying you should
think about the design and make sure that a generic method is really
what you want here.

Pete

Hey Pete,

Thanks for the information. Honestly, I know this design isn't
great. Unfortunately, and I know this is no good excuse, but I'm
working on an enterprise level application, all by myself and I'm
still learning design patterns.

Basically, I'm writing an application that uses web services to do
everything, data related. I currently have over 200 web methods in 3
web services. I have finally decided that I really need to look at
refactoring my code.

But in any case, I think I understand what you're saying and will keep
it in mind for further projects.
 
I

Ignacio Machin \( .NET/ C# MVP \)

Ok, it looks like I've found a solution. I added the "where T :
DataSet, new()" to the generic method definition. I know would like
to know, why does this work? Is this a solution or does this just
happen to work, when really the result is undefined?

It works now because inside the method you make reference to members of
DataSet.

As I said, you do not need a generic method.
 
R

rbjorkquist

It works now because inside themethodyou make reference to members of
DataSet.

As I said, you do not need agenericmethod.

Ignacio,

I never said by any means, my design was perfect; and, I thought I
agreed with Peter (who re-emphasized your point about not needing
generics here), so I don't see why you felt the need to tell me
again. In any case; I rewrote the method, as you both suggested. It
now simply returns a System.Data.DataSet object.

I understand that I could have passed a parameter by "ref", but in my
testing the results are the same. I now have two problems to fix.
The first is that application cache does not seem to be being used.
And secondly, my web service now cannot display the resulting dataset.

Application cache issue:
When my new method is executed, the object represented by the
"Cachename" parameter, is never found. It always assigns "null" to
"MyDataList".

Dataset issue:
Every time I call my web method, which in turn call my standard
method, the resulting dataset if in an unknown format and cannot be
displayed.

I can resolve both of these issues simply by using the generic
method. Another way to solve this (which is how it was before I
started refactoring) is to copying and pasting my generic method
concept into each specific web method, and modifying it accordingly.

Can you explain to me how to correct these the way you'd prefer me
to? My current code is below.

Method Call:
//---------------------------------------------------------------------
[WebmMethod(Description="")]
public PasteViscosities GetPasteViscosities(){
return((PasteViscosities)GetData("ccisp_GetPasteViscosities",
"ccivw_PasteViscosities",
"PasteViscosities.xml",
"PasteViscosities"));
}

//---------------------------------------------------------------------
//STANDARD NON-WEBMETHOD METHOD
//---------------------------------------------------------------------
private DataSet GetData(string StoredProcedureName, string
SourceTable, string XMLFile, string CacheName){
ConnectionStringSettings Cs
=
ConfigurationManager.ConnectionStrings["EfficiencyConnectionString"];

DataSet MyDataList = (DataSet)HttpRuntime.Cache[CacheName];

if(MyDataList == null){
MyDataList = new DataSet();

SqlDataAdapter SqlDA = new SqlDataAdapter();
SqlConnection SqlCon = new SqlConnection();
SqlCommand SqlCmd = new SqlCommand();

try{
MyDataList.ReadXml(Server.MapPath(XMLFile),
XmlReadMode.InferTypedSchema);
}//END TRY-BLOCK
catch(System.IO.FileNotFoundException){
SqlCmd.CommandText = StoredProcedureName;
SqlCmd.CommandType = CommandType.StoredProcedure;
SqlCmd.Connection = SqlCon;

SqlCon.ConnectionString = (string)Cs.ConnectionString;
SqlCon.Open();

SqlDA.SelectCommand = SqlCmd;
SqlDA.Fill(MyDataList, SourceTable);

MyDataList.WriteXml(Server.MapPath(XMLFile));
HttpRuntime.Cache.Insert(CacheName,
MyDataList,
new
CacheDependency(Server.MapPath(XMLFile)),
Cache.NoAbsoluteExpiration,
Cache.NoSlidingExpiration,
CacheItemPriority.Low,
null);
}//END CATCH-BLOCK
catch(Exception E){
HandleWebException(E);
}//END CATCH-BLOCK
finally{
SqlCon.Close();
}//END FINALLY-BLOCK
}//END IF-STATEMENT "if(MyDataList == null)"

return(MyDataList);
}
 
C

Christof Nordiek

Peter Duniho said:
The result is well-defined, as near as I can tell. The reason it works is
that you've "constrained" the type T, requiring that it inherits from
DataSet, and has a default constructor. Those requirements allow you to
write the code you wrote.

However, as Ignacio has pointed out, it's not clear at all that this is
really a situation in which you need a generic method. For sure, there's
no need to have the return value be of the generic type. You can just
return a DataSet. The caller will have to cast it back to the intended
type (e.g. to PasteViscosities), but you have to specify the type in the
use of the generic method anyway, so it's not like this really saves you
anything.
Why not? By giving the return type as a type parameter, the compiler can
recognize it. Otherwise a runtime type check would be necessary. Also,
without the use of generics, the method couldn't create an instance of the
exact type.

Am I missing something?

Christof
 
P

Peter Duniho

Christof said:
[...]
However, as Ignacio has pointed out, it's not clear at all that this
is really a situation in which you need a generic method. For sure,
there's no need to have the return value be of the generic type. You
can just return a DataSet. The caller will have to cast it back to
the intended type (e.g. to PasteViscosities), but you have to specify
the type in the use of the generic method anyway, so it's not like
this really saves you anything.
Why not? By giving the return type as a type parameter, the compiler can
recognize it. Otherwise a runtime type check would be necessary.

I don't personally consider that slight performance difference to be
very significant. There are places where having a typed class or method
can be very useful. But really, I don't see much difference between:

SomeClass instance = (SomeClass)GetData();

and...

SomeClass instance = GetData<SomeClass>();

Sure, there's a very specific implementation difference it terms of what
code is generated, but I don't think it would matter in most cases. And
as far as reading and writing the code, they seem basically equivalent
to me, assuming all else is equal.
Also,
without the use of generics, the method couldn't create an instance of
the exact type.

This part is of course true, and I mentioned it in the post to which you
replied. You just didn't bother to quote that mention.

But it's not at all clear in this case that that's what the OP really
_needs_ or wants to do. Granted, it's not clear that it's not, but I
can certainly think of examples where the overall design would be served
better by using some mechanism other than generics, and I even mentioned
at least one such example in my post.

Pete
 

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