I need my implementation of IObjectSet<T> to be able to validate expressions as the "real" Linq2Entity provider would. Is it possible?
Background:
I've changed my EF model (model-first) to return IObjectSet<T> instead of ObjectSet<T>So that I can fake my model, and have it return the data I want.
Problem:
I have written an implementation of IObejctSet<T> that is bacally a wrapper for a List<T>.
This is all good - but when I query my implementation of IObjectSet<T> im using LinqToObject - so there is no guarantee that I have valid Linq2Entity expression.
Proposed solution:
use the real
In my implementaion i just use List<T>'s provider:
private class IObjectSetUnitTestImplementation<T> : IObjectSet<T> where T : EntityObject
{
private IQueryable _qry;
public IObjectSetUnitTestImplementation(List<T> lst)
{
_inner = lst;
_qry = _inner.AsQueryable();
Expression = _qry.Expression;
ElementType = _qry.ElementType;
Provider = _qry.Provider; //<--should be ObjectSet<T>'s provider
}
//Rest of implementaion omitted
}
So I tried this:
ObjectContext context = null;
var _qry = new ObjectQuery<T>(typeof(T).Name, context) as IQueryable;
Expression = _qry.Expression;
ElementType = _qry.ElementType;
Provider = _qry.Provider;
But I have no context to feed it with, and even if I had, it would just try to access the database.
Is there any other way?
so there is no guarantee that I have valid Linq2Entity expression"
Congratulations, you've just discovered a reason, why IObjectSet<T>, IRepository<T> and etc. shouldn't be implemented for IQyeryable<T>. And the reason is simple: query, which is valid for one LINQ provider, can be invalid for another one (even if you just change databases from MS SQL to something other).
And there's no generic way to verify those queries.
So, unit-testing isn't a good choice for IQyeryable<T>, but integration testing is a definitely the thing that you need.
Related
I'm trying to mock some DB access code using simple lists. It generally works except for Async. which causes the error saying the provider does not support Iqueriable. I tries a few suggested solutions but they seem to cause an expression error when executing the linq query
[Fact]
public async Task TestAsyncFromList()
{
List<int> items = new List<int>() { 1,2,3,4,5,6,7,8,9,10};
var queryable = items.AsQueryable();
var totalSync = queryable.Count();
var totalAsync = await queryable.CountAsync(); // The provider for the source 'IQueryable' doesn't implement 'IAsyncQueryProvider'
Assert.True(totalSync==totalAsync);
}
I was expecting that there is an 'AsAsyncQueiable' solution
Tried this The provider for the source IQueryable doesn't implement IAsyncQueryProvider - but got Expression error
I'm creating a class to perform CRUD functionality with Azure Table Storage.
I'm using generic types in this.
I have the following method, which i'm trying to pass in an expression to get used in the TableQuery, but am having some issues.
The line TableQuery<T> query = new TableQuery<T>().Where<T>(criteria); won't compile, and gives me the message
Cannot implicitly convert type 'System.Linq.IQueryable<T>'
to 'Microsoft.WindowsAzure.Storage.Table.TableQuery<T>'.
An explicit conversion exists (are you missing a cast?)
I understand the message and know it's telling me i'm missing a cast, though I'm unsure how to code it correctly.
My full method is:
public List<T> GetSome<T>(Expression<Func<T, bool>> criteria) where T : ITableEntity, new()
{
TableQuery<T> query = new TableQuery<T>().Where<T>(criteria); // <---- This line isn't working
List<T> results = table.ExecuteQuery<T>(query).ToList<T>();
return results;
}
Ok, so I figured it out - how i can pass in a lambda expression for Azure Table Storage to use.
I changed my method to the following:
public List<T> GetSome<T>(Expression<Func<T, bool>> criteria) where T : ITableEntity, new()
{
// table, in this case, is my `CloudTable` instance
List<T> results = table.CreateQuery<T>().Where(criteria).ToList();
return results;
}
I can now pass in an expression. For example, to search against a DynamicTableEntity I can use:
// my table storage class
TableStorage ts = new TableStorage("ContactData");
// search with expression
List<DynamicTableEntity> results = ts.GetSome<DynamicTableEntity>(t => t.Properties["FirstName"].StringValue == "Darren");
If this is something you wouldn't/shouldn't do against an Azure Table Store, please do let me know.
In the meantime, this is how I have met the requirement.
The error is because the value returned by the extension method Where is of type IQueryable<T>, but you're assigning to a variable of type TableQuery<T> and there is no implicitly valid type conversion between these types.
I'm busy with a LINQ to SQL project that basically creates multiple threads for each entity type in my database, which constantly queries information from the DB in a thread.
Here's a pseudo example:
streamer.DefineDataExpression<Contacts>(x => x.FirstName == "Bob");
while(true)
{
List<Contacts> MyContactsResult = streamer.ResultList;
// do whatever with MyContactsResult
}
The above code doesn't exist, but this is what I have so far for the 'streamer' class (it obviously doesn't work, but you can see what I'm trying to achieve above):
public void DefineExpression(System.Linq.Expressions.Expression<System.Func<T, bool>> expression)
{
using (var db = new LINQDataContext())
{
ResultList = db.GetTable<T>().Where(expression);
}
}
How do I go about creating a method like 'DefineExpression' that will allow me to query a LINQ type dynamically?
Why not use the Dynamic LINQ provider, as mentioned by Scott Guthrie. I think that would give you everything you are looking for, because you can define the query as a string. Therefore, you can more easily build a string representation of your query, and execute on the fly.
I have a linq query written in method syntax. I need to create a very similar method with just some changes to the final Select.
Is it possible to return the partial Linq query from a method so I dont duplicate the code? The issue I have is finding the "Type" of the query to mark the method with.
If I use query.GetType(), it returns (cut down version) :
SubSonic.Linq.Structure.Query`1[<>f__AnonymousType18`6[advert,client]]
I tried to create a return type:
SubSonic.Linq.Structure.Query<advert, client> query = new SubSonic.Linq.Structure.Query<advert, client>();
However I receive the error:
Error 20 Using the generic type 'SubSonic.Linq.Structure.Query<T>' requires '1' type arguments
So I guess I am asking how to declare a return type that is a Subsonic Query containing an anonymous type containing a number of objects? (2 in my example)
Please excuse my simple example:
eg:
internal ????? GetQueryBody(string param1, string param2){
/* buld the linq query here */
}
internal List<Booking> GetSearchResultsOne(string param1, string param2){
var query = this.GetQueryBody(string param1, string param2);
var res = query.Select( db => new Booking { /*fields */).ToList();
return res;
}
internal List<BookingData> GetSearchResultsTwo(string param1, string param2){
var query = this.GetQueryBody(string param1, string param2);
var res = query.Select( db => new BookingData { /*fields*/).ToList();
return res;
}
Thank you for your time,
Yohimbo
Use IEnumerable<T> to return a query.
About the anonymous type: If a type is anonymous, how should another methode know about it? Read more here. To solve the problem, give your anonymous type a name by creating a class.
If you only want to return two types you could also return a tuple: Then T is Tuple<advert,client>. You can create a tuple by
var t = new Tuple<advert,client>(client, advert);
Answer 1: You can't do it because Booking and BookingData are different types so the expression trees are different.
Answer 2: Assuming you can find a common base class, there are two approaches to your question.
The 'type' of what a Linq Query acts on is actually an Expression<TDelegate>. You can construct Expression trees and store them and manipulate them, and then use them where needed.
The 'argument' for your final Select() is actually an Expression<Func<TSource, TResult>>. You can use any function in that location as long as it conforms to that delegate.
In other words, you can store the entire pre-formed expression trees from the top, or you can store one tree and substitute the bit at the bottom. There isn't enough in your sample for me to write the code, and you still have to resolve that fatal flaw.
I'm trying to implement encrypted columns in EF4 and using the CTP5 features to allow simple use of POCO's to query the database. Sorry that this is a lot of words, but I hope the below gives enough to explain the need and the problem!
So, bit of background, and my progress so far:
The intention is that if you query the tables without using our DAL then the data is rubbish, but I don't want the developers to worry about if/when/how the data is encrypted.
For simplicity, at this stage I'm working on the assumption any string column will be encrypted.
Now, I have successfully implemented this for returning the data using the Objectmaterialized event, and for data commits using the SavingChanges event.
So given the following class:
public class Thing
{
public int ID { get; set; }
[Required]
public string Name { get; set; }
public DateTime Date { get; set; }
public string OtherString { get; set; }
}
The below query returns all the required values and the POCO materialized has clear data in it.
var things = from t in myDbContext.Things
select t;
where myDbContext.Things is a DbSet<Thing>
Likewise, passing an instance of Thing to Things.Add()
(with clear string data in the Name and/or OtherString values)
and then calling myDbContext.SaveChanges() encrypts the strings before it gets to the data store.
Now, the problem I have is in this query:
var things = from t in myDbContext.Things
where t.Name == "Hairbrush"
select t;
This results in the unencrypted value being compared to the encrypted value in the DB. Obviously I don't want to get all the records from the database, materialize them, and then filter the results based on any supplied Where clause... so what I need to do is: intercept that query and rewrite it by encrypting any strings in the Where clause.
So I've looked at:
writing a query provider, but that doesn't seem like the right solution... (is it?)
writing my own IQueryable wrapper for the DbSet which will capture the expression, run over it using an expression tree visitor and then forward the new expression to the DbSet...
Attempts at both have left me somewhat lost! I prefer the second solution i think since it feels a bit neater, and is probably clearer to other developers in future. But I'm happy to go with either or another better option!!
The main thing I am struggling with is when/how the LINQ expression is applied to the object... I think i've got myself a bit confused as to where the expression executes in the IQueryable object thus I'm not sure which method I need to implement in my wrapper to then grab and manipulate the expression being passed in...
I'm sure I'm missing something fairly obvious here and I'm waiting for that light bulb moment... but its not coming!!
Any help will be very gratefully received!
Thought I'd let you know what my final solution was.
In the end I have gone a wrapper class which implements a Where method, but without going to the extent of implementing IQueryable entirely. LINQ will still execute against the class (at least to the extent that I want/need it to) and will call the Where method with the expression from the LINQ.
I then traverse this ExpressionTree and replace my strings with encrypted values before forwarding the new expressiontree to the internal DbSet. and then returning the result.
Its pretty crude, and has its limitation, but works for our particular circumstance without problem.
Thanks,
Ben
you should use the QueryInterceptor attribute, search here in SO or in google and you find examples on how to use it.
a snippet:
[QueryInterceptor("Orders")]
public Expression<Func<Order, bool>> FilterOrders()
{
return o => o.Customer.Name == /* Current principal name. */;
}
// Insures that the user accessing the customer(s) has the appropriate
// rights as defined in the QueryRules object to access the customer
// resource(s).
[QueryInterceptor ("Customers")]
public Expression<Func<Customer, bool>> FilterCustomers()
{
return c => c.Name == /* Current principal name. */ &&
this.CurrentDataSource.QueryRules.Contains(
rule => rule.Name == c.Name &&
rule.CustomerAllowedToQuery == true
);
}
You can use David Fowler's Query Interceptor:
https://github.com/davidfowl/QueryInterceptor
One example of its use:
IQueryable q = ...;
IQueryable modifed = q.InterceptWith(new MyInterceptor());
And on class MyInterceptor:
protected override Expression VisitBinary(BinaryExpression node) {
if (node.NodeType == ExpressionType.Equal) {
// Change == to !=
return Expression.NotEqual(node.Left, node.Right);
}
return base.VisitBinary(node);
}