I'm drawing inspiration from this question:
Convert Linq to Sql Expression to Expression Tree
The original poster asked how to convert this to an Expression tree and got a good answer which can be seen in the above link.
List<Region> lst = (from r in dc.Regions
where r.RegionID > 2 && r.RegionDescription.Contains("ern")
select r).ToList();
How would I got about making a property with a get method that returns a bool that uses the ExpressionTree? I'd like to be able to do something like this (obviously I don't need the == true):
List<Region> lst = (from r in dc.Regions
where (r.AwesomeProperty == true)
select r).ToList();
How would I go about defining AwesomeProperty?
You would define AwesomeProperty just like any other property on your LINQ to SQL object. Assuming it is typed as bool (since you compare it to true), you would build the Where query like this:
// Build the parameter to the where clause predicate and access AwesomeProperty
var regionParameter = Expression.Parameter(typeof(Region), "region");
var awesomeProperty = Expression.Property(regionParameter, "AwesomeProperty");
// Build the where clause predicate using the AwesomeProperty access
var predicate = Expression.Lambda<Func<Region, bool>>(awesomeProperty);
// Get the table, which serves as the base query
var table = dc.Regions.AsQueryable();
// Call the Where method using the predicate and the table as the base query
var whereCall = Expression.Call(
typeof(Queryable),
"Where",
new[] { table.ElementType },
table.Expression,
predicate);
// Get an IQueryable<Region> which executes the where call on the table
var query = table.Provider.CreateQuery<Region>(whereCall);
var results = query.ToList();
Related
Newb to C#.Net / LINQ
I have to convert some dynamic stored procs to LINQ. I'm new to LINQ, and looking for an example to follow. I have something like the following:
sql1 VARCHAR2(32000) := ' SELECT a,b,c FROM ( ';
from1 VARCHAR2(32000) := ' SELECT x,y,z FROM t1, t2,
(SELECT xx FROM aTable WHERE foo = 'bar' )
WHERE x=1
AND y=2';
Anyway, hope this gets the example across.
I was hoping to do something like:
var subSelect1 = (from val1 in aTable
where val1.foo = "bar"
select new {
val1.foobar
}).AsEnumerable();
var mainSelect = (from <how do I use the subSelect1 as a virtual table here?>
Is this possible in LINQ?
The actual query is quite long and complex...having several subqueries in the FROM clause of the main select, and some of the subqueries in the FROM have themselves nested subqueries in -their- FROM clause.
What may help is going through a couple sample linq queries and look at the results. For example, the following code will create an IEnumerable of val1's.
var subSelect1 = (from val1 in aTable
where val1.foo = "bar"
);
Note the above is equivalent to the below.
var subSelect1 = (from val1 in aTable
where val1.foo = "bar"
select val1 /* this select statement is implied in the above */
);
Adding the select new {val1.foobar} after the where clause creates an IEnumerable of an anonymous type, with one property named foobar. This means that you'll only be able to join against the one property foobar.
var subSelect1 = (from val1 in aTable
where val1.foo == "bar"
select new {val1.foobar}
);
var mainSelect = (from f in subSelect1
where f.foobar == "test")
By leaving out the select new, you'll have access to all the fields in val1.
var subSelect1 = (from val1 in aTable
where val1.foo == "bar"
);
var mainSelect = (from v in subSelect1
where v.foobar == "test"
and v.bar == "status"
)
I am not very clear regarding your exact requirement, but my understanding is you need a chained Linq Query such that the parent data can be filtered using the Subquery result. Also the Fluent syntax is a set of extension methods on IEnumerable<T>, therefore a DataTable needs AsEnumerable() to be called so that operations can be done on IEnumerable<DataRow> and later on CopyToDataTable in the System.Data.DataSetExtensions can be called for re-conversion. Also since we are dealing with DataRows, therefore value access need indexer, like dataRow["ColumnName"] or dataRow[ColumnIndex]
// dr is the DataRow type (fetching all the Datarows, instead of specific Column like foobar)
Creating Subselect1 IEnumerable<DataRow>
var subSelect1 = aTable.AsEnumerable()
.Where(dr => dr["foo"] == "bar")
var mainSelect = (from <how do I use the subSelect1 as a virtual table here?
This shall be simple, depending on exactly what you need to do I assume, you need to match the DataRows that exist in subSelect1, then so something like:
var mainSelect = MainDataTable.AsEnumerable()
.Where(dr => subSelect1.Contains(dr))
var mainSelectDataTable = mainSelect.CopyToDataTable();
Idea remains, its very simple to create complex chained queries in Linq, provided you know exactly what you need, which is not very clear from the question
I have a DataClassesDataContext containing a group of tables, and I am trying to do lambda expression filtering dynamically using only the name of the tables and the names of the fields. Basically I want to find for each table if a row with a specific ID already exists.
If I knew the table ahead of time, I would use :
if (dataClassesDataContext.MYTABLEXs.SingleOrDefault(m => m.MYTABLEX_ID == MyId))
DoExists();
But as I am getting tables names MYTABLEX and MYTABLEY (and fields names MYTABLEX_ID and MYTABLEY_ID) as strings on the fly, I am trying to build the above filter at runtime.
I can access the table dynamically using :
Type tableType = Type.GetType(incommingtableName); // incommingtableName being looped over MYTABLEX, MYTABLEY , ...
var dbTable = dataClassesDataContext.GetTable(tableType);
But then I am stuck. How can I build a lambda expression that will behave something like :
if (dbTable.SingleOrDefault(m => m.incommingtableName_id == MyId))
DoExists();
Any idea ?
You can build an expression in runtime. And also you would need to have generic version of SingleOrDefault method. Here is example:
Type tableType = typeof (incommingtableName); // table type
string idPropertyName = "ID"; // id property name
int myId = 42; // value for searching
// here we are building lambda expression dynamically. It will be like m => m.ID = 42;
ParameterExpression param = Expression.Parameter(tableType, "m");
MemberExpression idProperty = Expression.PropertyOrField(param, idPropertyName);
ConstantExpression constValue = Expression.Constant(myId);
BinaryExpression body = Expression.Equal(idProperty, constValue);
var lambda = Expression.Lambda(body, param);
// then we would need to get generic method. As SingleOrDefault is generic method, we are searching for it,
// and then construct it based on tableType parameter
// in my example i've used CodeFirst context, but it shouldn't matter
SupplyDepot.DAL.SupplyDepotContext context = new SupplyDepotContext();
var dbTable = context.Set(tableType);
// here we are getting SingleOrDefault<T>(Expression) method and making it as SingleOrDefault<tableType>(Expression)
var genericSingleOrDefaultMethod =
typeof (Queryable).GetMethods().First(m => m.Name == "SingleOrDefault" && m.GetParameters().Length == 2);
var specificSingleOrDefault = genericSingleOrDefaultMethod.MakeGenericMethod(tableType);
// and finally we are exexuting it with constructed lambda
var result = specificSingleOrDefault.Invoke(null, new object[] { dbTable, lambda });
As possible optimization constructed lambda can be cached, so we wont need to build it each time, but it should work the same
actually I have an Expression like this that work very well in the case of Linq to Entity
public static Expression<Func<Tender, bool>> GetPublic()
{
var now = DateTime.Now.GetEasternStandardDateTime();
return tender => tender.EndDate > DateTime.Now &&
tender.IsClosed == false &&
tender.IsCancel == false &&
tender.PrivacyLevelId == 1;
}
I can use the expression like this : _repositoryObject.Where(GetPublic()) and I will get results.
But I cannot use the same expression when I have to do Linq to SQL pattern like this
var results = from t in Tenders
where Tender.GetPublic()
select t;
I have this error
Cannot implicity convert type
System.Linq.Expression> to bool for Linq to
SQL
and I don't know why...
Karine
Firstly, I suspect you actually mean you can call it as:
_repositoryObject.Where(GetPublic())
It's important that you call the method to get the expression tree.
Your query expression is being converted into:
var results = Tenders.Where(t => Tender.GetPublic());
... which isn't what you're looking for. Basically, query expressions are translated into method calls using lambda expressions. You don't want that in this case, so you shouldn't use a query expression.
If you want to do other things with a query expression, you can always mix and match, e.g.
var results = from t in Tenders.Where(Tender.GetPublic())
where t.SomeOtherProperty
select t.SomethingElse;
If you change your method to take an IQueryable<Tender>, and return another IQueryable<Tender> where your criteria are added, then it might work.
public static IQueryable<Tender> AddPublicCriteria(this IQueryable<Tender> tenderQuery) {
// I've omitted your call to DateTime.Now.GetEasternStandardDateTime()
// since you do not use it.
return tenderQuery
.Where(tender => tender.EndDate > DateTime.Now &&
tender.IsClosed == false &&
tender.IsCancel == false &&
tender.PrivacyLevelId == 1
);
}
var query = Tenders; // Original table or data-set
query = query.AddPublicCriteria(); // Add criteria
var results = query.ToList(); // Run query and put the results in a list
I'm attempting to do a dynamic join in linq. Meaning that I only know at runtime what field the join will occur on.
I've done the following:
var itemParam = Expression.Parameter(typeof(E), "obj");
var entityAccess = Expression.MakeMemberAccess(Expression.Parameter(typeof(E), "obj"), typeof(E).GetMember(Field).First());
var lambda = Expression.Lambda(entityAccess, itemParam);
var q = dbSet.Join(context.Acl, lambda, acl => acl.ObjectID, (entity, acl) => new { Entity = entity, ACL = acl });
However this throws at compile time, even though lambda appears to be the right syntax telling me that it cannot convert from LambdaExpression to Expression<System.Func<E, int>>.
How do I get it to create the right expression dynamically that uses my field (i.e. property "Field" above in the typeof(E).GetMember(Field).First()) line?
Use Expression.Lambda<TDelegate>, so that you end up with the line
// obj => obj.Field
var lambda = Expression.Lambda<Func<E, int>>(entityAccess, itemParam);
Update
As per your comment, the reason the expression fails is because you are using two different parameters. You define itemParam, but then do not use it in Expression.MakeMemberAccess
Try the following instead:
// obj
var itemParam = Expression.Parameter(typeof(E), "obj");
// obj.Field
var entityAccess = Expression.MakeMemberAccess(itemParam, typeof(E).GetMember(Field).First());
I am writing a dynamic linq query to realize paging, and I am now facing a problem, I need the System.Linq.Expressions.Expression.Like function, but it does not exist in System.Linq.Expressions.Expression, here is my code.
Expression mWhereFunc; // Filter clause
ParameterExpression mLinqParam; // Linq param
// Get current request page
string mCurPage = this.Request.QueryString["page"];
if (String.IsNullOrEmpty(mCurPage))
{
mCurPage = "1";
}
mLinqParam = Expression.Parameter(typeof(ORD_Order), "p");
mWhereFunc = Expression.Equal(Expression.Property(mLinqParam,
typeof(ORD_Order).GetProperty("ItemIsValid")),
Expression.Constant(true));
string mOrderSN = this.Request.QueryString["txtOrderSN"];
if (!String.IsNullOrEmpty(mOrderSN))
{
mWhereFunc = Expression.And(mWhereFunc,
**Expression.Equal**(Expression.Property(mLinqParam,
typeof(ORD_Order).GetProperty("OrderSN")),
Expression.Constant(mOrderSN)));
}
var mLambdaWhere = (Expression<Func<ORD_Order,
bool>>)Expression.Lambda<Func<ORD_Order, bool>>(mWhereFunc,
new ParameterExpression[] { mLinqParam });
Func<ORD_Order, Int32> mLambdaOrder = p => p.OrderID;
IORD_OrderRepository rptOrder = new ORD_OrderRepository();
ICTM_CustomerRepository rptCtm = new CTM_CustomerRepository();
var list = from o in rptOrder.GetAll()
.Where(mLambdaWhere)
.OrderBy(mLambdaOrder)
.Skip((int.Parse(mCurPage) - 1) * mPageSize).Take(mPageSize)
join c in rptCtm.GetAll()
on o.CustomerID equals c.CustomerID
select new
{
o.OrderID,
o.OrderSN,
o.CustomerID,
c.ContactName,
o.Status,
o.CreateDate,
o.Description
};
Expression.Equal is the place where I want to change it to Expression.Like
Any help will be appreciated.
I modified my code like this
mWhereFunc = Expression.And(mWhereFunc, Expression.Call(
Expression.Property(mLinqParam, typeof(ORD_Order).GetProperty("OrderSN")),
typeof(String).GetMethod("Contains"),
new Expression[] { Expression.Constant(mOrderSN) }));
and it works, many thanks to all of you.
Like equivalents for linq are String.Contains, String.StartsWith etc. http://www.simonrhart.com/2008/06/using-like-in-linq-to-sql-under-c.html
if you are using NHibernate you can create a Like method and use a Expression to call that
You can check this post on how to do the "Like" extension here: NHibernate Linq Provider Extension
And then you build the "Like" Expression like this:
Expression.Call(typeof(MyLinqExtensions).GetMethod("IsLike"), Expression.Property(mLinqParam,
typeof(ORD_Order).GetProperty("OrderSN")),
Expression.Constant(mOrderSN)));
EDIT: Just as an additional comment, you should use Expression.AndAlso instead of Expression.And since the first one is the && operator and the last one is the & operator
EDIT 2: For Entity Framework check this post (Linq To Entities scenario): SQL User-Defined Functions in Entity Framework 4, I have no experience with it but it seems the same as a NH provider, after doing that then building the Linq Expression should be the same i posted before