Welcome to MSDN Blogs Sign in | Join | Help

does not contain a definition for

Have you heard about extension methods yet? If you have then you probably know exactly what the title of this blog means. I can see this becoming one of the most asked about exceptions in the coming release. At least until the dust settles.

 

<Edit>

If you are running into the above exception, you can fix this without reading the entire post. Just add a "using System.Linq;" to your using block

 

if you are seeing the "cannot be inferred from the usage." exception mentioned below  and you are trying to use Linq over DataSets you need to add a reference to System.Data.Entity. You will have to hunt for it under the 3.5 framework install directory. 

</Edit>

 

So what is going on? Let’s write some code, a simple Linq over DataSets example that assumes you have the latest ctp installed.

 

using System;

using System.Data;

using System.Data.Common;

 

namespace ConsoleApplication3

{

    class Program

    {

        static DataSet GetDataSet()

        {

            DataSet ds = new DataSet();

            DataTable dt = new DataTable("Customer");

            dt.Columns.Add("Name");

            dt.Columns.Add("Country");

            dt.Rows.Add(new string[] { "Name1", "Country1" });

            dt.Rows.Add(new string[] { "Name2", "Country1" });

            dt.Rows.Add(new string[] { "Name3", "Country3" });

            ds.Tables.Add(dt);

            return ds;

        }

        static void Main(string[] args)

        {

            DataSet ds = GetDataSet();

            var query = ds.Tables["Customer"].AsEnumerable().Where(c => c.Field<string>("Country") == "Country1")

                .Select(c => c.Field<string>("Name"));

            foreach (string s in query)

            {

                Console.WriteLine(s);

            }

        }

    }

}

 

For completeness sake I have added a very crude GetDataSet method, please excuse this code bloat but I hate code samples that are not self contained. Once we get a DataSet we do a very simple Linq query to return all the Names of customers living in Country1.  

 

The code looks fairly reasonable, we hit compile and… Exception!:

 

'System.Data.DataTable' does not contain a definition for 'AsEnumerable' and no extension method 'AsEnumerable' accepting a first argument of type 'System.Data.DataTable' could be found (are you missing a using directive or an assembly reference?)    

 

This is a fairly nice exception, readable text and it asks you whether you are missing a using or assembly reference (it turns out we are missing both). If you are reading this blog it is almost certain that you know exactly what the problem is, but let’s pretend that you don’t know what is going on here and do a quick “Microsoft live” search on the exception above. A quick search will almost certainly tell you that you are missing a “using System.Linq” statement. Let’s add this to our code above:

 

using System.Linq; //add this to the code above.

 

So, are we done? Not really, this is where things get interesting. When we go to compile the code we get the following gem:

 

The type arguments for method 'System.Linq.Enumerable.AsEnumerable<TSource>(System.Collections.Generic.IEnumerable<TSource>)' cannot be inferred from the usage. Try specifying the type arguments explicitly

 

This is a particularly useless exception. Not only does it make almost no sense, the suggestions is horrible (do not try to specify the type arguments explicitly). What went wrong?

 

The problem is that for the nice linq over DataSet code above to work we need to use the System.Data.Entity.dll EnumerableDataTable. To do this we need to add a reference to System.Data.Entity.dll in our project. Unfortunatelly there is no good way for System.Linq to throw a better exception here, it just can't know that you are trying to use the wrong AsEnumerable.

 

//Add System.Data.Entity.dll to the list of References of your project.

 

Finally your project will compile and run as expected, congratulations!

 

 

 

Ok, let’s go for some extra credit. Now that we have determined that we really needed to add the System.Data.Entity.dll reference why don’t we just remove the “using System.Linq” statement?

 

Very simple, because if we remove System.Linq we get the following exception:

 

'System.Collections.Generic.IEnumerable<System.Data.DataRow>' does not contain a definition for 'Where' and no extension method 'Where' accepting a first argument of type 'System.Collections.Generic.IEnumerable<System.Data.DataRow>' could be found (are you missing a using directive or an assembly reference?)

 

The reason for this is left as an exercise to the reader (I have always wanted to say that *grin*)

 

Rambling out

Posted by angelsb | 2 Comments
Filed under:

Blogging about link Extensions

There are not a lot of guidelines about blogging, the one that I like to adhere to is that I only blog about bits that you can go and play with. This ends up creating gaps in my blogging history as I move to new unpublished products. I have moved, oh not far I am still in the same team just switched products. The Jdbc driver is doing great so I started looking for new challenges. In this case the new challenges may well have gotten the better of me... we shall see. In any case I am now working on the Linq extensions for ado.net, this will be Linq over Dataset and Linq over Entities for now, I am sure more will come. I am really biting at the bit here, this is really cool tech. Here is a blog on Linq extensions over DataSet to get things started:

http://blogs.msdn.com/adonet/archive/2007/01/26/querying-datasets-introduction-to-linq-to-dataset.aspx

Posted by angelsb | 2 Comments

JDBC V1.1 Distributed Transactions with Read Committed and Snapshot isolation.

One of the biggest changes you may not have noticed in the v1.1 2005 JDBC driver is that we have changed the default isolation level for Distributed Transactions from Serializable to Read Committed. This is in line with other JDBC drivers like our 2000 jdbc driver.

 

In this blog I am going to discuss the two questions that I had when I first heard about this change.

 

Q1>How do I set the isolation level of distributed transactions back to Serializable for those applications that require it (boring!)

Answer> To change the isolation level you just need to set the following connection property:

conn.setTransactionIsolation(Connection.TRANSACTION_SERIALIZABLE);

Make sure you set this _before_  calling XAResource start!

 

Of course you can set any isolation level you want here, transaction_none will just give you the default isolation level . To specify snapshot isolation level (_after_ you have set up the database to use snapshot with ALTER DATABASE <database name> SET ALLOW_SNAPSHOT_ISOLATION ON) you can do the following:

import com.microsoft.sqlserver.jdbc.*;

/…/

conn.setTransactionIsolation(SQLServerConnection.TRANSACTION_SNAPSHOT);

 

If you don’t want to import the sqlserver.jdbc namespace you can use the following:

            conn.setTransactionIsolation(4096);

 

 

Q2>how do I set the default isolation level of Distributed Transactions to Snapshot (woo!)

Answer> This is what you are most likely going to want if you are looking to minimize lock contention.

The first thing you need to do is to set the _default_ isolation level of the database to snapshot like this:

ALTER DATABASE <databasename> SET READ_COMMITTED_SNAPSHOT ON

 

Once this is done all Read Committed (the default isolation level for v1.1 2005 JDBC driver!) transactions will behave as snapshot. You are done.

 

 

Here is some code for you to play with, I realize that it may look a little intimidating since we have to roll our own XIDs, but you can pretty much just ignore anything outside of the DoDTC method.

 

import java.net.Inet4Address;

import java.sql.*;

import com.microsoft.sqlserver.jdbc.*;

 

import javax.sql.XAConnection;

import javax.transaction.xa.XAResource;

import javax.transaction.xa.Xid;

import com.microsoft.sqlserver.jdbc.SQLServerXADataSource;

import java.util.*;

 

 

public class transaction2 {

      public static void main(String[] args) {

            try{

                  transaction2 t = new transaction2();

                  t.DoDTC(1);

                  System.out.println("done");

            }catch(Exception e){

                  e.printStackTrace();

            }

      }

 

      private void DoDTC(int id) throws Exception

      {

            SQLServerXADataSource ds = new SQLServerXADataSource();

            ds.setURL("jdbc:sqlserver://servername;user=username;password=password");

            XAConnection xaConn = ds.getXAConnection();

            Connection conn = xaConn.getConnection();

            System.out.println("connected");

            //This is a good place to change the default isolation level of the transaction. Ex:

            //conn.setTransactionIsolation(Connection.TRANSACTION_SERIALIZABLE);

           

            XAResource xaRes = null;

            Xid xid = null;

            try{

                  xaRes = xaConn.getXAResource();

                  xid = getUniqueXID();

                  System.out.println("[a] xid=" + xid.toString());

 

                  xaRes.start(xid,XAResource.TMNOFLAGS);

 

                  ResultSet rs = conn.createStatement().executeQuery("DBCC USEROPTIONS");

                  while (rs.next()) {

                        if (rs.getString(1).equals("isolation level")) {

                              System.out.println("isolation level " + rs.getString(2));

                        }

                  }

                  xaRes.end(xid,XAResource.TMSUCCESS);

                  xaRes.commit(xid,true);

                  //xaRes.rollback(xid);

            }catch(Exception ex){

                  //Important. On certain exceptions we may leak transactions!

                  //Make sure to reclaim them.

                  xaRes.forget(xid);

                  throw ex;

            }

      }

 

 

//    Returns a globally unique transaction id

      byte [] localIP = null;

      private int txnUniqueID = 0;

      private int tid=1;

      private Xid getUniqueXID()

      {

            Random rnd = new Random(System.currentTimeMillis());

            txnUniqueID++;

            int txnUID = txnUniqueID;

            int tidID = tid; 

            int randID = rnd.nextInt();

            byte[] gtrid = new byte[64];

            byte[] bqual = new byte[64];

            if ( null == localIP)

            {

                  try

                  {

                        localIP = Inet4Address.getLocalHost().getAddress();

                  }

                  catch ( Exception ex )

                  {

                        localIP =  new byte[] { 0x01,0x02,0x03,0x04 };

                  }

            }

            System.arraycopy(localIP,0,gtrid,0,4);

            System.arraycopy(localIP,0,bqual,0,4);

 

            // Bytes 4 -> 7 - unique transaction id (unique to our class instance).

            // Bytes 8 ->11 - thread id (unique to our thread).

            // Bytes 12->15 - random number generated using seed from current time in milliseconds.

            for (int i=0; i<=3; i++)

            {

                  gtrid[i+4] = (byte)(txnUID%0x100);

                  bqual[i+4] = (byte)(txnUID%0x100);

                  txnUID >>= 8;

                  gtrid[i+8] = (byte)(tidID%0x100);

                  bqual[i+8] = (byte)(tidID%0x100);

                  tidID >>= 8;

                  gtrid[i+12] = (byte)(randID%0x100);

                  bqual[i+12] = (byte)(randID%0x100);      

                  randID >>= 8;

            }

            return new XidImpl(0x1234, gtrid, bqual);

      }

}

class XidImpl implements Xid

{

      public int formatId;

      public byte[] gtrid;

      public byte[] bqual;

 

      public byte[] getGlobalTransactionId()

      {

            return gtrid; 

      }

      public byte[] getBranchQualifier()    

      {

            return bqual; 

      }

      public int    getFormatId()            {return formatId; }

 

      XidImpl(int formatId, byte[] gtrid, byte[] bqual)

      {

            this.formatId = formatId;

            this.gtrid = gtrid;

            this.bqual = bqual;

      }

      public String toString()

      {

            int hexVal;

            StringBuffer sb = new StringBuffer(512);

            sb.append("formatId=" + formatId);

            sb.append(" gtrid(" + gtrid.length + ")={0x");

            for (int i=0; i<gtrid.length; i++)

            {

                  hexVal = gtrid[i]&0xFF;

                  if ( hexVal < 0x10 )

                        sb.append("0" + Integer.toHexString(gtrid[i]&0xFF));

                  else

                        sb.append(Integer.toHexString(gtrid[i]&0xFF));

            }

            sb.append("} bqual(" + bqual.length + ")={0x");

            for (int i=0; i<bqual.length; i++)

            {

                  hexVal = bqual[i]&0xFF;

                  if ( hexVal < 0x10 )

                        sb.append("0" + Integer.toHexString(bqual[i]&0xFF));

                  else

                        sb.append(Integer.toHexString(bqual[i]&0xFF));

            }

            sb.append("}");

            return sb.toString();

      }

}

 

Posted by angelsb | 4 Comments

JDBC Snapshot isolation level

I have gotten some questions about using Sql Server 2005 with the 2005 JDBC driver. There are unfortunately a lot of misconceptions in this area. Here are some notes to make this a little bit easier to understand.

 

The first thing you need to understand about Snapshot isolation level is that it only works in a Sql Server 2005 database that has been setup for Snapshot isolation level. The second thing that you need to understand is that you can only see a snapshot of the data locked by other transactions when running from inside a transaction with snapshot isolation level. This is different from Oracle where you can always get a snapshot of the data currently locked.

 

The basics:

Setting up a Sql Server 2005 database for Snapshot goes something like this:

ALTER DATABASE <database name> SET ALLOW_SNAPSHOT_ISOLATION ON

 

Another option is to modify the default isolation level to behave like Snapshot isolation. The ALTER DATABASE statement has been upgraded to allow you to get snapshots benefits for free:

ALTER DATABASE <database name> SET READ_COMMITTED_SNAPSHOT ON

 

Once you have set up the database you can test it with the following code, note the following line:

conn2.setTransactionIsolation(4096); //4096 corresponds to SQLServerConnection.TRANSACTION_SNAPSHOT

This is where we set the JDBC isolation level to Snapshot, remove this line and the code below will deadlock!

 

create table foo (myid int primary key)

insert into foo values (1)

insert into foo values (2)

insert into foo values (3)

insert into foo values (4)

 

import java.sql.*;

 

public class test

{

      public static void main(String args[])

      throws Exception

      {

            Class.forName("com.microsoft.sqlserver.jdbc.SQLServerDriver");

            Connection conn = DriverManager.getConnection("jdbc:sqlserver://yourserver;integratedsecurity=true;");

            DatabaseMetaData m = conn.getMetaData();

            System.out.println("getDriverVersion  "+m.getDriverVersion());

           

            //<Lock the table foo>

            conn.setAutoCommit(false);

            Statement stmt = conn.createStatement();

            stmt.executeQuery("select * from foo");

            stmt.execute("update foo  set myid=100 where myid =1");

            //</Lock the table foo>