LinqPad dynamic sql and displaying results
Asked Answered
E

3

5

I'm using LinqPad to execute some dynamic sql and it is returning IEnumerable when I call .Dump(). I would like it to display the results of the anonymous type its returning. Any help on executing a dynamic sql statement in LinqPad and displaying the results would be appreciated.

Here is a code snippet of what I am trying to do:

// Any sql string for example.
var query = "SELECT DISTINCT [CustomerId] FROM Customers Where CustomerId = 2";

var dyn = this.ExecuteQuery<dynamic>(query);

LINQPad.Extensions.Dump(dyn);
Explanation answered 29/12, 2014 at 14:27 Comment(1)
See the answers to #4118213Nationalist
A
5

You're on the right track with IDataRecord. To make the output dynamic, use DynamicObject:

static class Extensions
{
    public static IEnumerable<dynamic> ExecuteSQL (this DataContext dc, string sql)
    {
        var cx = new SqlConnection (dc.Connection.ConnectionString);
        cx.Open();
        return new SqlCommand (sql, cx).ExecuteReader (CommandBehavior.CloseConnection).Cast<IDataRecord>().Select (r => new DynamicDataRecord (r));
    }
}

class DynamicDataRecord : System.Dynamic.DynamicObject
{
    readonly IDataRecord _row;
    public DynamicDataRecord (IDataRecord row) { _row = row; }

    public override bool TryConvert (System.Dynamic.ConvertBinder binder, out object result)
    {
        if (binder.Type == typeof (IDataRecord))
        {
            result = _row;
            return true;
        }
        return base.TryConvert (binder, out result);
    }

    public override bool TryInvokeMember (System.Dynamic.InvokeMemberBinder binder, object [] args, out object result)
    {
        if (binder.Name == "Dump")
        {
            if (args.Length == 0)
                _row.Dump ();
            else if (args.Length == 1 && args [0] is int)
                _row.Dump ((int)args [0]);
            else if (args.Length == 1 && args [0] is string)
                _row.Dump ((string)args [0]);
            else if (args.Length == 2)
                _row.Dump (args [0] as string, args [1] as int?);
            else
                _row.Dump ();
            result = _row;
            return true;
        }
        return base.TryInvokeMember (binder, args, out result);
    }

    public override bool TryGetMember (System.Dynamic.GetMemberBinder binder, out object result)
    {
        result = _row [binder.Name];
        if (result is DBNull) result = null;
        return true;
    }

    public override bool TryGetIndex (System.Dynamic.GetIndexBinder binder, object [] indexes, out object result)
    {
        if (indexes.Length == 1)
        {
            result = _row [int.Parse (indexes [0].ToString ())];
            return true;
        }
        return base.TryGetIndex (binder, indexes, out result);
    }

    public override IEnumerable<string> GetDynamicMemberNames ()
    {
        return Enumerable.Range (0, _row.FieldCount).Select (i => _row.GetName (i));
    }
}

This will allow the following:

this.ExecuteSQL ("select * from customer").GroupBy (c => c.Name).Dump();

EDIT: this functionality is now available within LINQPad as of v4.53.02. You can now go:

ExecuteQueryDynamic ("SELECT DISTINCT * FROM Customer WHERE ID = {0}", 2)
Abrahamsen answered 3/1, 2015 at 0:49 Comment(1)
I ran "Check for Updates" on my v4.51.03 version and LINQPad reported that there aren't any updates. When will the v4.53.02 update come thru?Antepast
E
2

So what I've done to get a result is this, but I think there must be a better way.

using (SqlConnection connection = new SqlConnection(this.Connection.ConnectionString))
{
  connection.Open();

  SqlCommand command = new SqlCommand(query, connection);
  SqlDataReader reader = command.ExecuteReader();

  reader.Cast<IDataRecord>().AsQueryable().Dump();      
}
Explanation answered 29/12, 2014 at 18:47 Comment(0)
D
2

In addition to Joe's answer:

It is important that you're using a Linq-to-SQL connection, because ExecuteQueryDynamic
is not available in the Entity Framework.

Here's how you can handle different data types as parameters (based on a Northwind database) in LinqPad 5:

void Main()
{
    // Boolean
    ExecuteQueryDynamic(@"DECLARE @Discontinued bit={0}; 
                        SELECT DISTINCT * FROM Products 
                        WHERE Discontinued = @Discontinued", true).Dump();
    // Int
    ExecuteQueryDynamic(@"DECLARE @OrderId Int={0}; 
                        SELECT DISTINCT OrderId, CustomerId, ShipName FROM Orders 
                        WHERE OrderID = @OrderId", 10248).Dump();
    // String
    ExecuteQueryDynamic(@"DECLARE @CustomerId nvarchar(max)={0}; 
                        SELECT DISTINCT * FROM Customers 
                        WHERE CustomerId = @CustomerId", "VINET").Dump();
}

I recommend that you're using a DECLARE statement for your SQL variables. This way, you can try it out first in T-SQL (or LinqPad SQL mode) with assigned fixed values - where you will receive meaningful error messages if the data types are not matching, then you can insert it into ExecuteQueryDynamic and insert {0}, {1}, {2} ... for the first, second, third, ... parameter as follows:

    ExecuteQueryDynamic(@"DECLARE @Discontinued bit={0}; DECLARE @ProductID Int={1}; 
                          DECLARE @CategoryID Int={2}; 
                          SELECT DISTINCT * FROM Products 
                          WHERE Discontinued = @Discontinued AND ProductId = @ProductID 
                          AND CategoryID = @CategoryID;                     
                         ", true, 5, 2).Dump();

NOTE: ExecuteQueryDynamic does not support multiple resultsets. This means, only one SELECT statement is allowed, additional ones are being ignored.

Dabbs answered 27/6, 2017 at 8:57 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.