Using .NET classes



You use .NET assembly classes the same way you use Java and other objects that you create using the cfobject tag or CreateObject function. In the simplest case, your application code only has to use the following format to include a local .NET class method:

<cfobject type = ".NET" name = "mathInstance" class = "mathClass" 
        assembly = "C:/Net/Assemblies/math.dll"> 
<cfset myVar = mathInstance.multiply(1,2)>

Using CFScript and the CreateObject function, you can do the following:

<cfscript> 
    mathInstance = CreateObject(".NET", "mathClass",  
        "C:/Net/Assemblies/math.dll"); 
    myVar = mathInstance.multiply(1,2); 
</cfscript>
Note: You cannot load two DLLs with same fully qualified name. ColdFusion always uses the first DLL that it accesses until the server is restarted. For example, if page1.cfm uses c:\dev\a.dll and page2.cfm uses c:\dev2\a.dll, and both DLLs have the same fully qualified name, the first DLL file to be loaded remains loaded, and both CFML pages use it.

When you create objects and access class methods and fields, and convert data types between ColdFusion and .NET, be aware of the following considerations and limitations:

Instantiating objects and calling class constructors

When you use the cfobject tag to create a .NET object, ColdFusion does not create an instance of the object. ColdFusion creates the object instance in either of the following cases:

  • If the class has a default constructor, ColdFusion automatically calls the constructor when you first invoke a non-static method of the object.

  • If the class does not have a default constructor, or if the class has multiple constructors and you do not want to use the default, call the special init method of the ColdFusion object. The cfobject tag automatically creates init methods for all class constructors. Using the init method causes ColdFusion to call the class constructor with the corresponding number and types of parameters. For example, the following tags cause ColdFusion to call the MyClass constructor that takes two integer parameters:

    <cfobject type=".NET" name="myObj" class="com.myCo.MyClass" 
            assembly="c:\assemblies\myLib.dll"> 
    <cfset myObj.init(10, 5)>
Note: ColdFusion does not create instances of objects if you use only their static methods.

Calling methods

You call .NET methods in the same way that you use any other ColdFusion object methods. For example, if the MyClass class has a getName method that takes a numeric ID and returns a name, you would call the method as follows:

<cfset theID="2343"> 
<cfset userName=mObj.getName(theID)>

Getting and setting fields

You can access and change public fields of any .NET class by calling the following methods:

Get_fieldName() 
Set_fieldName(value)

For example, if the .NET class has a public field named accountID, you can access and change its value by using the Get_accountID() and Set_accountID() methods, as follows:

<cfobject type=".NET" class="com.myCo.MyClass" 
        assembly="c:\assemblies\myLib.dll" name="myObj"> 
<cfset theAccount=myObj.Get_accountID()> 
<cfset myObj.Set_accountID(theAccount + 1)>

You can access, but not modify final fields, so you can only call Get_fieldName() for these fields.

Converting between .NET and ColdFusion data types

Accessing .NET classes requires a Java proxy on the ColdFusion system and .NET code on the target system, so data must be converted among ColdFusion, Java, and .NET (to be exact, Microsoft Intermediate Language, or MSIL) data types. ColdFusion converts data types automatically. Usually, you do not have to take any special steps to ensure correct conversion. Some conversion limitations exist, and in some cases you must explicitly specify a data type when you call a method in a .NET proxy object.

The following paragraphs describe data conversion issues and how to handle them. For a detailed specification of how ColdFusion converts among ColdFusion data, Java data types, and .NET data types, see cfobject: .NET object in the CFML Reference.

Data type conversion rules and techniques

ColdFusion converts data automatically among ColdFusion, Java, and CLR data types. The following table indicates how ColdFusion converts among .NET Common Language Runtime (CLR) primitive and standard data types, the Java data types used in the proxies to represent CLR data types, and ColdFusion data types in your CFML application.

.NET type

Java type

ColdFusion type

sbyte

byte

Integer

byte

short

Integer

short

short

Integer

ushort

int

Integer

int

int

Integer

uint

long

Number

char

char

Integer or string

long

long

Number

ulong

float

Number

float

float

Number

double

double

Number

The returned number retains greater precision than is normally displayed in ColdFusion. Use the PrecisionEvaluate function to access and display the full precision of a returned double value. You can also pass a value with full double precision to a .NET method.

bool

boolean

Boolean

enum

 

Not converted, but enumerator elements can be accessed directly by using the format Enumerator_variable.enumerator, as in MyColor.Red

array

array

Array

string

String

String

System.Collections.ArrayList

java.util.ArrayList

Array

Note: ColdFusion converts from .NET type to ColdFusion type only, it does not convert ColdFusion Arrays to .NET ArrayLists.

System.Collections.Hashtable

java.util.Hashtable

Structure

Note: ColdFusion converts from .NET type to ColdFusion type only, it does not convert ColdFusion Structures to .NET Hashtables

System.Data.DataTable

 

Query

Note: ColdFusion converts from .NET type to ColdFusion type only, it does not convert ColdFusion Queries to .NET DataTables

System.DateTime

java.util.Date

Date/time

decimal

System.Decimal

java.math.BigDecimal

String representation of the decimal number.

For details on using decimal numbers, see Using decimal numbers.

System.Object

 

If a .NET argument is of type System.Object, ColdFusion Strings are converted directly. Other types require using the JavaCast function.

ColdFusion cannot convert System.object instances returned by .NET methods to ColdFusion types, but you can access them using the Object methods.

For detailed information, see Converting data to System.Object type.

Using decimal numbers

Use the JavaCast function to convert ColdFusion data into BigDecimal format before you pass the value to a .NET function, as in the following example:

<cfset netObj.netFunc(javacast("bigdecimal","439732984732048"))>

ColdFusion automatically converts returned decimal and System.Decimal values to ColdFusion string representations.

Ensuring decimal and date/time conversions

ColdFusion converts .NET decimal or System.Decimal types only if the proxy for System.Decimal is a value type proxy. Similarly, it converts .NET System.DateTime values to ColdFusion Date-time values only if the proxy for System.DateTime is a value type proxy. The ColdFusion server always uses value proxies when it generates these proxies. If you use the JNBProxyGUI.exe tool to generate the proxy, however, make sure to generate the proxy for System.Decimal as value type.

Converting data to System.Object type

When a .NET method specifies System.Object (as opposed to a specific Object subclass, such as System.Boolean) as the argument type, and you want to pass primitive values as arguments to that method, use the javacast function to identify the data conversion. Once ColdFusion knows the data type, it automatically converts to the appropriate .NET type. Here is the table that describes the conversion rule from ColdFusion type to .NET type.

.NET Type

Type used in javacast

bool / System.Boolean

boolean

bool[] / System.Boolean[]

boolean[]

char / System.Char

char

char[] / System.Char[]

char[]

double / System.Double

double

double[] / System.Double[]

double[]

float / System.Single

float

float[] / System.Single[]

float[]

int / System.Int32

int

int[] / System.Int32[]

int[]

long / System.Int64

long

long[] / System.Int64[]

long[]

sbyte / System.Sbyte

byte

sbyte []/ System.Sbyte[]

byte []

short / System.Int16

short

short[] / System.Int16[]

short[]

System.Decimal

bigdecimal

System.String

String

Note: You do not have to use a JavaCast function to convert ColdFusion string variables. They are automatically converted to .NET System.String.

Create special objects for .NET primitive unsigned data types, such as byte (unsigned byte), ushort (unsigned short), uint (unsigned int) and ulong (unsigned long), for which no corresponding java types exist. The following table lists the .NET primitive types and the corresponding class you must use.

.NET type

Class used in cfobject/createObject

byte / System.Byte

System.BoxedByte

ushort / System.UInt16

System.BoxedUShort

uint / System.UInt32

System.BoxedUInt

ulong / System.UInt64

System.BoxedULong

Use the createObject function or cfobject tag to create these special objects, in the same manner as you create other .NET classes, before you use them in your assignment statement. For example, the following line creates a ushort representation of the value 100:

<cfset boxedUShort = createObject(".NET". "System.BoxedUShort").init(100)>

The following example creates a System.Hashtable object and populates it with examples of all types of primitives.

<!--- create a .NET Hashtable ---> 
<cfset table = createObject(".NET", "System.Collections.Hashtable")> 
 
<!--- call HashTable.add(Object, Object) method for all primitives ---> 
<cfset table.add("shortVar", javacast("short", 10))> 
<cfset table.add("sbyteVar", javacast("byte", 20))> 
<cfset table.add("intVar", javacast("int", 123))> 
<cfset table.add("longVar", javacast("long", 1234))> 
<cfset table.add("floatVar", javacast("float", 123.4))> 
<cfset table.add("doubleVar", javacast("double", 123.4))> 
<cfset table.add("charVar", javacast("char", 'c'))> 
<cfset table.add("booleanVar", javacast("boolean", "yes"))> 
<cfset table.add("StringVar", "Hello World")> 
<cfset table.add("decimalVar", javacast("bigdecimal", 123234234.505))> 
 
<!--- call HashTable.add(Object, Object) for unsigned primitive types. ---> 
<cfset boxedByte = createObject(".NET", "System.BoxedByte").init(10)> 
<cfset table.add("byteVar", boxedByte)> 
 
<cfset boxedUShort = createObject(".NET", "System.BoxedUShort").init(100)> 
<cfset table.add("ushortVar", boxedUShort)> 
 
<cfset boxedUInt = createObject(".NET", "System.BoxedUInt").init(123)> 
<cfset table.add("uintVar", boxedUInt)> 
 
<cfset boxedULong = createObject(".NET", "System.BoxedULong").init(123123)> 
<cfset table.add("ulongVar", boxedULong)> 
 
<cfdump var="#DotNetToCFType(table)#">

Any other .NET objects can be passed as it is.

Handling ambiguous type conversions

ColdFusion cannot determine the correct data type conversion if a method has multiple signatures with the same number of parameters that differ only in the parameter data types. In this case, use the JavaCast method to convert the ColdFusion data to the Java type that corresponds to the .NET type.

For example, if a .NET class has methods myFunc(ulong) and myFunc(int), use the JavaCast method to convert your ColdFusion variable to the Java float or int data type, as the following line shows:

myFunc(JavaCast(int, MyVar));

Similarly, if a .NET class has methods myFunc(int) and myFunc(String), use the JavaCast method to convert your ColdFusion variable to the Java int or String data type, as shown in the following line:

myFunc(JavaCast(String, "123");

In some cases, the JavaCast function cannot eliminate ambiguity because a single Java type corresponds to multiple .NET types. In these cases, ColdFusion creates a proxy with only one method, which uses the .NET data type that corresponds directly to a Java type.

For example, if the .NET class has methods myFunc(ulong) and myFunc(float), the generated proxy has only one method. This method calls myFunc(float), because the Java float type used to handle ColdFusion floating-point numbers corresponds directly to the .NET float type. In this case, you can never call the .NET myFunc(ulong) method.

Working with complex .NET data types

When you use complex .NET data such as Hashtable, ArrayList and DataTable, ColdFusion normally automatically converts the data to the corresponding ColdFusion data type: structure, array, and query, respectively. When you work with this data you take specific actions to enable the proper access and conversion of the data, as follows:

  • Use associative array notation to properly access .NET Hashtable data from ColdFusion

  • You cannot use ColdFusion variables directly in parameters that take Hashtable, ArrayList, or DataTable input.

  • You can disable automatic conversion of complex .NET data to ColdFusion types.

  • You can manually convert complex .NET data to ColdFusion types.

Using Hashtable data in ColdFusion

.NET Hashtables are case sensitive, but most methods of ColdFusion structure access are not case sensitive. Only associative array notation of the form structName["keyName"] is case sensitive. When .NET Hashtables are converted to CF structure, the entire data set is converted, even if the element keys differ only in case. Therefore, to get the values of the keys that differ only in case, use associative array notation.

The following example shows this issue. It creates a Hashtable object with three entries whose key values vary only in case. In the example, output using dot-delimited structure notation always returns the same value, corresponding to the all-uppercase key, but associative array notation returns the correct result.

<!--- Create a Hashtable and convert it to a ColdFusion structure. ---> 
<cfset table = createObject(".NET", "System.Collections.Hashtable")> 
<cfset table.add("Key", "Value1")> 
<cfset table.add("KEY", "Value2")> 
<cfset table.add("key", "Value3")> 
<cfset cftable = DotNetToCFType(table)> 
 
<cfdump var="#cftable#"> 
 
<h3>Using dot notation</h3> 
Key : <cfoutput>#cftable.Key#</cfoutput><br> 
KEY : <cfoutput>#cftable.KEY#</cfoutput><br> 
key : <cfoutput>#cftable.key#</cfoutput><br> 
<p> 
 
<h3>Using associative array notation</h3> 
Key : <cfoutput>#cftable["Key"]#</cfoutput><br> 
KEY : <cfoutput>#cftable["KEY"]#</cfoutput><br> 
key : <cfoutput>#cftable["key"]#</cfoutput><br>

Using .Net ArrayList in ColdFusion

ColdFusion converts System.Collections.ArrayList objects to ColdFusion arrays, and you can perform all standard ColdFusion array operations on them. The following example shows this usage:

.Net Code:

public ArrayList getList(){ 
    ArrayList myAL = new ArrayList(); 
    myAL.Add("Hello"); 
    myAL.Add(1); 
    myAL.add(true); 
    Return AL; 
}

ColdFusion Code:

<cfset cflist = netObject.getList()> 
<cfloop array="#cflist#" index="item"> 
    <cfoutput>#item#</cfoutput><br> 
</cfloop> 
     
<cfif cflist[3]> 
        <cfoutput>Third element in the list is true</cfoutput> 
</cfif>

Using ADO.Net DataTable in ColdFusion

ColdFusion converts System.Data.DataTable objects to ColdFusion query objects, and you can perform all standard ColdFusion query operations on them. The following example shows this usage:

.Net code:

public DataTable datasetMethod() 
{ 
    //conn string 
    string connectionString = "..."; 
     
    //connection 
    using (SqlConnection connection = new SqlConnection(connectionString)) 
    { 
        SqlCommand cmd = new SqlCommand(@"SELECT * FROM [tblEmployees]", connection); 
        connection.Open(); 
        SqlDataReader reader = cmd.ExecuteReader(); 
        DataTable dt = new DataTable(); 
        dt.Load(reader); 
        return dt; 
} 
}

ColdFusion code:

<cfset query1 = netObject.datasetMethod()> 
<cfoutput query="query1"> 
    Query1.CurrentRow = #query1.CurrentRow#<br> 
</cfoutput>

Using ColdFusion complex types in .NET input parameters

When a .NET method returns an ArrayList, Hashtable, or DataTable, ColdFusion automatically converts it to a ColdFusion array, structure, or query, respectively. However ColdFusion does not automatically convert from ColdFusion data types to these .NET types. (ColdFusion does automatically convert ColdFusion arrays to .Net array types.) Therefore, you cannot use ColdFusion variables directly as input parameters to .NET object instance methods that require .NET System.Collection.ArrayList, System.Collection.Hashtable, or System.Data.DataTable types. Instead create instances of these .NET types and populate them with the required data before you pass them to the .NET method. For an example of creating and populating a System.Collection.Hashtable object, see the example at the end of the “Converting data to System.Object type” section.

Disabling automatic conversion of complex .NET data

You can disable automatic conversion of .NET System.Collections.Hashtable, System.Collections.ArrayList or System.Data.DataTable objects to the corresponding ColdFusion structure, array, or query objects. You could want to disable the conversion under the following circumstances:

  • If a collection or DataTable returned by a .NET method is large and you only want a small subset of the data. If auto conversion is enabled, ColdFusion creates a data structure with all the object’s fields. Creating the structure could take significant time and resources, because ColdFusion must invoke .NET methods internally to get each of the fields. You can disable the automatic conversion and retrieve the fields or data from .NET objects like any other objects.

  • If you invoke a .NET method that returns a complex variable, and then pass the variable to another .NET method as argument. If automatic conversion is enabled, you cannot pass the Hashtable object from the first method directly to the second method.

To disable automatic conversion, set the JVM coldfusion.dotnet.disableautoconversion system property to true. For example, in a ColdFusion stand-alone server, or if you use JRun as your J2EE server, include the following setting in the JVM.config file:

-Dcoldfusion.dotnet.disableautoconversion=true

Manually converting complex .NET objects

Use the DotNetToCFType function to convert a System.Collections.Hashtable, System.Collections.ArrayList or System.Data.DataTable object to a ColdFusion structure, array, or query respectively when either of the following circumstances are true:

  • You have set the coldfusion.dotnet.disableautoconversion system property to true.

  • Automatic conversion is enabled, you created the complex .NET object by using the createObject function or cfobject tag, and you want to convert this object into the corresponding ColdFusion representation.

For an example of using the function, see DotNetToCFType in the CFML Reference.

Using .NET objects

.NET fields and return values with class types are available in ColdFusion as .NET objects. You can use the object’s methods to access object data and make it available to ColdFusion using supported data types.

The following example gets information about a system’s drives. It calls the System.IO.DriveInfo.GetDrives() method to get an array of System.IO.DriveInfo objects, one per drive. It then calls the object methods to get specific information about the drives, and displays the information. The example uses a cfdump tag to simplify the code.

Note: The System.IO.DriveInfo is not included in the .NET 1.x framework. It is included in .NET 2.0 and later frameworks. For information on determining the .NET framework, see Determining and changing the .NET version.
<!--- Create a query for the drive information results. --->  
<cfset result=QueryNew("name,type,isready,format,label,totalsize,freespace" 
                    ,"varchar,varchar,bit,varchar,varchar,double,double")> 
<!--- Create a .NET System.IO.DriveInfo object. ---> 
<cfobject type=".NET" name="sidiClass" class="System.IO.DriveInfo"> 
<!--- Get the drives. ---> 
<cfset drives=sidiClass.GetDrives()> 
<!--- Loop through drives. ---> 
<cfloop from="1" to="#ArrayLen(drives)#" index="i"> 
    <!--- Add a row to the query.---> 
    <cfset QueryAddRow(result)> 
    <!--- Get the drive name, type, and ready flag. ---> 
    <cfset QuerySetCell(result, "name", drives[i].Get_Name())> 
    <cfset QuerySetCell(result, "type", 
                drives[i].Get_DriveType().ToString())> 
    <cfset QuerySetCell(result, "isready", drives[i].Get_IsReady())> 
    <!--- Get extra details ONLY if the drive is ready. ---> 
    <cfif drives[i].Get_IsReady()> 
        <cfset QuerySetCell(result, "format", drives[i].Get_DriveFormat())> 
        <cfset QuerySetCell(result, "label", drives[i].Get_VolumeLabel())> 
        <cfset QuerySetCell(result, "totalsize", drives[i].Get_TotalSize())> 
        <cfset QuerySetCell(result, "freespace", 
                    drives[i].Get_AvailableFreeSpace())> 
    </cfif> 
</cfloop> 
<cfdump var="#result#">