tags:

views:

92

answers:

3
public IEnumerable<T> ExecuteStoredProcedure<T>(params object[] parameters)
        {
            Type genericType = typeof(T);

            string commandthing = genericType.Name.Replace("Result", "");
            //_db is my Linq To Sql database
            return _db.ExecuteQuery<T>(commandthing, parameters).AsEnumerable();
        }

The stored procedure is named GetOrder and has a single int parameter of orderid. I'm calling the above like so:

SqlParameter parm1 = new SqlParameter("@orderid", SqlDbType.Int);
                parm1.Value = 123;
 var results =
                    _session.ExecuteStoredProcedure<GetOrderResult>(parm1).Single();

I'm receiving the following error: A query parameter cannot be of type 'System.Data.SqlClient.SqlParameter'

Thoughts? Or am I just missing something obvious?

Update: I'm trying to make this as generic as possible...my current thinking is that I'm going to have to do some string trickery to create the ExecuteQuery text and parameters.

Update: Posting below my Session Interface and my Linq to Sql Implementation of the interface...hopefully that will clarify what I'm attempting to do

 public interface ISession : IDisposable
    {
        void CommitChanges();
        void Delete<T>(Expression<Func<T, bool>> expression) where T : class;
        void Delete<T>(T item) where T : class;
        void DeleteAll<T>() where T : class;
        T Single<T>(Expression<Func<T, bool>> expression) where T : class;
        IQueryable<T> All<T>() where T : class;
        void Add<T>(T item) where T : class;
        void Add<T>(IEnumerable<T> items) where T : class;
        void Update<T>(T item) where T : class;
        IEnumerable<T> ExecuteStoredProcedure<T>(params object[] parameters);

    }
public class LinqToSqlSession : ISession
    {
        public readonly Db _db;
        public LinqToSqlSession()
        {
            _db = new Db(ConfigurationManager.ConnectionStrings[Environment.MachineName].ConnectionString);
        }

        public void CommitChanges()
        {
            _db.SubmitChanges();
        }
        /// <summary>
        /// Gets the table provided by the type T and returns for querying
        /// </summary>
        private Table<T> GetTable<T>() where T : class
        {
            return _db.GetTable<T>();
        }


        public void Delete<T>(Expression<Func<T, bool>> expression) where T : class
        {

            var query = All<T>().Where(expression);
            GetTable<T>().DeleteAllOnSubmit(query);
        }

        public void Delete<T>(T item) where T : class
        {
            GetTable<T>().DeleteOnSubmit(item);
        }

        public void DeleteAll<T>() where T : class
        {
            var query = All<T>();
            GetTable<T>().DeleteAllOnSubmit(query);
        }



        public void Dispose()
        {
            _db.Dispose();
        }

        public T Single<T>(Expression<Func<T, bool>> expression) where T : class
        {
            return GetTable<T>().SingleOrDefault(expression);
        }

        public IEnumerable<T> ExecuteStoredProcedure<T>(params object[] parameters)
        {
            Type genericType = typeof(T);

            string commandstring = genericType.Name.Replace("Result", "");
            //_db is my Linq To Sql database

            return _db.ExecuteQuery<T>(commandstring, parameters).AsEnumerable();
        }

        public IQueryable<T> All<T>() where T : class
        {
            return GetTable<T>().AsQueryable();
        }

        public void Add<T>(T item) where T : class
        {
            GetTable<T>().InsertOnSubmit(item);
        }
        public void Add<T>(IEnumerable<T> items) where T : class
        {
            GetTable<T>().InsertAllOnSubmit(items);
        }
        public void Update<T>(T item) where T : class
        {
            //nothing needed here
        }
}
A: 

That isn't how you're supposed to wire up Stored Procedures with Linq-to-SQL. You should extend the DataContext and use ExecuteMethodCall instead:

Taken from MSDN:

public partial class MyDataContext
{
    [Function()]
    public IEnumerable<Customer> CustomerById(
        [Parameter(Name = "CustomerID", DbType = "NChar(5)")]
        string customerID)
    {
        IExecuteResult result = this.ExecuteMethodCall(this,
            ((MethodInfo)(MethodInfo.GetCurrentMethod())),
            customerID);
        return (IEnumerable<Customer>)(result.ReturnValue);
    }
}

If you really must execute a sproc as a query (highly not recommended), then you have to preface the command with EXEC, and don't use SqlParameter either, the call would look like:

var results = context.ExecuteQuery<MyResult>("EXEC usp_MyProc {0}, {1}",
    custID, custName);

(And I'll note, pre-emptively, that this is not a SQL injection vector because Linq to SQL turns the curly braces into a parameterized query.)

Aaronaught
Correct me if I'm wrong...that would require me to make a new method for each Sproc I added. I'm attempting to make it generic so that I can eliminate noise.
Webjedi
@Webjedi: You can drag stored procedures onto the Linq to SQL designer surface. It looks to me like this attempt to eliminate noise is just hurting readability and design coherence; you lose the abstraction provided by the `DataContext`, you lose strong typing of parameters, you lose the ability to validate your DB schema/mapping at design time... and every class *other* than the `DataContext` will actually require *more* code to execute stored procedures this way. Compare `DataContext.GetCustomerById(custID)` vs. `DataContext.ExecuteStoredProcedure<GetCustomerByIdResult>(custID)`.
Aaronaught
I updated the question to make it a little more clear. I'm trying to hide Linq to Sql behind a "session" interface. That's why I really want it to be generic...so if I say make an Nhibernate implementation of the interface my changes can be isolated.
Webjedi
@Webjedi: If you're trying to create an abstraction over Linq to SQL then you ought to be hiding implementation details such as stored procedures as well. Why wouldn't you just add *specific* methods to your "session" object that run these stored procedures, so that it doesn't break whenever somebody changes the name of a stored procedure or refactors a class? How are consumers of this session even supposed to know what parameters to pass in, when all it takes is just a `params object[]`?
Aaronaught
You are totally right...I was chewing on that over night. But even in that, there still seems like something is a little amiss...though better admittedly.
Webjedi
+1  A: 

Read about how to call sprocs in linq to sql

http://weblogs.asp.net/scottgu/archive/2007/08/16/linq-to-sql-part-6-retrieving-data-using-stored-procedures.aspx

Raj Kaimal
Not exactly what I'm trying to do. I need it to be generic because I am using an abstraction/interface layer.
Webjedi
A: 

I'm sure there is a better way to do this...but this is presently working:

    public IEnumerable<T> ExecuteStoredProcedure<T>(params object[] parameters)
    {
        Type genericType = typeof(T);

        StringBuilder sb=new StringBuilder();
        sb.Append("EXEC ");
        sb.Append(genericType.Name.Replace("Result", " " ));
        for (int i = 0; i < parameters.Count(); i++)
        {
            sb.Append("{" + i.ToString() + "} ");
        }
        string commandstring = sb.ToString();

        return _db.ExecuteQuery<T>(commandstring, parameters);
    }

It's a little bit brittle in that your parameters must be set up in the proper order, and it's probably offensive to some...but it does accomplish the goal.

Webjedi