views:

562

answers:

3

I need to have "Where" Linq functionality in my own code.

Let me explain: we have an application that lets users write the so-called "user code", which is code in C# that will be picked up by the app and compiled and run at runtime.

In user code, I need to be able to specify an SQL condition. A special case is the condition on a date column. We may have conditions like [DateColumn] = '1/1/2001' which are easy to implement, but also [DateColumn] = GetDate() + 1. In the latter case I would need to implement an parser to "understand" the expression. I don't want to blindly send to SQL whatever the user enters (avoid SQL injection).

The user would be most pleased to write a Linq-like query like this:

xxx.Where(field => field == new DateTime(2001, 1, 1));
xxx.Where(field => field == DateTime.Now.AddDays(1));

Is it possible to leverage the Linq to SQL framework in any way? I would need the SQL generated in the back to further construct the whole query and send it to SQL Server.

Are there any 3rd party tools that may help me?

I don't require the whole IQueryable interfaces (to compose, join querys etc.). Only the ability to convert a System.Linq.Expressions.Expression to T-SQL (even with limitations).

What is the System.Linq.* namespace that exposes this conversion functionality for SQL? Maybe I can get a few hints with Reflector.

Thank you very much.

A: 

I imagine you can make use of Linq's lazy evaluation to acheive this. If I understand your question correctly, I think you can do something like this;

// Before user code:
var products = from p in db.products select p;

// Insert user-generated code here, such as i.e.
userProducts = products.Where(p => price < 1000);

// After user code:
for (var product in userProducts)
{
  Whatever(product);
}

Since linq is lazy, the actual SQL query will not be issued to the database before you enumerate the 'result', in this case, "userProducts".

Digitalex
Actually, I don't need anything other than the conversion mechanism from Expression to string (T-SQL).
Florin Sabau
+2  A: 

There is a Sample code in the SDK called dynamic linq.

\CS 2008 Samples\LinqSamples\DynamicQuery

You can do such things:

Northwind db = new Northwind(connString); 
db.Log = Console.Out;

var query =
    db.Customers.Where("City == @0 and Orders.Count >= @1", "London", 10).
    OrderBy("CompanyName").
    Select("New(CompanyName as Name, Phone)");

Another possibility is to implement a Custom Linq Provider. A very good example can be found at "THE WAYWARD WEBLOG" http://blogs.msdn.com/mattwar/pages/linq-links.aspx. This Blog helped me implementing my own custom Provider.

public interface IQueryable : IEnumerable {       
    Type ElementType { get; }
    Expression Expression { get; }
    IQueryProvider Provider { get; }
}

public interface IQueryProvider {
    IQueryable CreateQuery(Expression expression);
    IQueryable<TElement> CreateQuery<TElement>(Expression expression);
    object Execute(Expression expression);
    TResult Execute<TResult>(Expression expression);
}
Arthur
Hmm, not really what I need. I just need a quick way to convert an Expression => T-SQL string.
Florin Sabau
Well, maybe you'll need to write a Custom Linq Proivder. Here are some very good blogpost about it. I used them too for my custom linq provider. http://blogs.msdn.com/mattwar/pages/linq-links.aspx
Arthur
Yes, I'm beginning to think it's the only way. Unfortunately I don't have experience with writing the provider, that's why I was hoping for some 3rd party code. :D
Florin Sabau
Arthur, thanks for your link. The IQToolkit looks promising.
Florin Sabau
Arthur, if you want, rewrite your comment about IQToolkit and the "THE WAYWARD WEBLOG" as an answer and I'll flag it as the solution.
Florin Sabau
A: 

With all due respect, I think you're jumping out of the frying pan and into the fire. You don't want to permit SQL injection, but you're permitting code injection. C# code, even a lambda, allows side effects.

AFAIK, C# 4.0 does allow you to dynamically compile code. But you'll have to perform compiler-quality analysis to prevent the user from doing something stupid or malicious. It's much simpler to massage the WHERE clause to prevent SQL injection.

XXXXX
The application that I'm talking about is a large one, that has several layers of security enforcement, to make sure it doesn't behave in a malicious way. The code is run in a sandbox, only a very small group of people (administrators) have access to writing user code etc. So the "code injection" is by design.It's not relevant "why" I need this functionality and all the possible security implications, but my question was "how" to do it. I'm aware that I can create my own QueryProvider for this purpose, but I was looking for the easies, most tested way of doing it.
Florin Sabau
... Actually the SQL injection is not an issue. I can pretty much trust the users to not write 'drop table' commands, but it's more about not "tempting" them to mess around with the underlying implementation of the system (writing SQL code). It is about providing simple API for creating powerfull filters.
Florin Sabau
I understand. Regardless, I don't think you should expose either SQL or C# directly to the users. Ones you've put some abstract (UI) layer between the users and the data, it's an implementation detail whether you translate that abstraction to SQL or C#. In the latter case, I believe .NET exposes the compiler, either in 4.0 or 3. something.
XXXXX