I want to achieve that NHibernate queries different types based on a condition. Let's say the condition is false then the query should look like this:
var myVar = session.Query<TypeWhenFalse>()
.Where(a => condition)
And when the condition is true it should look like this:
var myVar = session.Query<TypeWhenTrue>()
.Where(a => condition)
Notice the different query types TypeWhenFalse and TypeWhenTrue. Because of duplicate code, I don't wanna use a if clause to set the different types.
Is there a way to do this dynamically in order to avoid an if clause? Couldn't find anything helpful in the internet.
Thanks in advance :-)
You have to use generic type method and you can use an extension to implement what you want.
But.. in order to have this to make sense, you need to have both returned types to have common structures implemented by an interface.
See this example:
public static void Main()
{
var abc = new List<long>(){ 1,2,3,4};
var result = abc.AsQueryable().ConvertExtension();
}
public static IQueryable ConvertExtension<T>(this IQueryable<T> source, bool condition=false)
{
return condition
? (IQueryable) source.Select(x => x.ToString()).AsQueryable<string>()
: source.Select(x => int.Parse(x.ToString())).AsQueryable<int>();
}
More about Generic Types: C# generic types
More about Interfaces: Interfaces
If you implement an interface for both types, you can type your resulting Iqueryable as the interface. The method would be IQueryable ConvertExtension
And you would be able to manipulate both Types as it.
The code you provided is very less to suggest anything confirmed.
You can use generics to achieve this easily. I mean, make this function generic and pass the generic type from outside. This should resolve your concerns.
public yourtype GetData<T>()
{
var myVar = session.Query<T>()
.Where(a => condition);
return myVar;//or whatever
}
and your calling code:
if(false)
myVar = GetData<TypeWhenFalse>();
else
myVar = GetData<TypeWhenTrue>();
As you said, you do not want to use if...else to avoid redundant code. If you are agreed for little redundancy, consider following solution:
var query;
if(false)
query = session.Query<TypeWhenFalse>();
else
query = session.Query<TypeWhenTrue>();
var myVar = query.Where(a => condition);
Related
i have a generic repository
public TEntity GetByCondition(Func<TEntity, Boolean> where)
{
return DbSet.Where(where).FirstOrDefault<TEntity>();
}
i need to get the selected records based on condition;
public IEnumerable<ResultEntity> GetResultByParams(string _RollNo, string _Class)
{
var result = _unitOfWork.ResultRepository.GetByCondition(); // i need to check ondition of _RollNo here. Please guide me the usage.
}
// how to use the var result = _unitOfWork.ResultRepository.GetByCondition(); with condition
In general:
var result = _unitOfWork.ResultRepository.GetByCondition(res => /*your condition for "result" here*/);
By condition I mean expression that returns bool.
For example:
var result = _unitOfWork.ResultRepository.GetByCondition(res => res.RollNo == _RollNo);
It basically means: "give me all Results which have RollNo equals to _RollNo"
I recommend you to read explanations of Func from following question, it may be useful: Explanation of Func.
And, of course, msdn reference for the type of Func-s used in your case.
I'm new in Linq and so I have these situation below.
Now below error during compilation, says Cannot implicitly convert type 'System.Linq.IQueryable' to 'System.Data.Entity.DbSet'.
var query = _db.Products;
if (bool) {
query = query.Where(p => p.Id == id);
}
And so I tried to change var to IQueryable and it works.
IQueryable<Product> query = _db.Products;
if (bool) {
query = query.Where(p => p.Id == id);
}
But then, I tried to change it again (see below) and it works.
var query = from product in products
select product;
if (bool) {
query = query.Where(p => p.Id == id);
}
And I just want to know why the other one works, but the other's not.
A good explanation with example might help. Thanks
The reason why the first scenario does not work is that the System.Linq.IQueryable is an interface which is implemented, among others, by the System.Data.Entity.DbSet class. In C#, if class C implements interface I, when it comes to transitions between types, you may as well treat I as C's base class (even the semantics class C : I suggest such an approach). And since you cannot implicitly (i.e. not verbosely) cast a class (or interface) to one of it's descendant classes, you get a compile-time error when trying to do it. You can do the opposite, that is implicitly cast a descendant class to it's base class (or interface). That's exactly what happens in the second scenario.
In your case, you could trick the compiler by casting explicitly:
query = (DbSet<Customer>) query.Where(p => p.Id == id);
but I would strongly advise you not to since you'll end up with a messy exception, because the result of query.Where(p => p.Id == id) is not in fact an instance of DbSet<Customer>, but rather some class representing the result of a query performed on a DbSet<Customer>, which implements the IQueryable interface.
So, to sum up, let's go through all the scenarios:
Scenario 1:
//query is of type DbSet<Customer>
var query = _db.Products;
if (bool) {
//here you're trying to assign a value of type IQueryable<Customer>
//to a variable of it's descendant type DbSet<Customer>
//hence the compile-time error
query = query.Where(p => p.Id == id);
}
Scenario 2:
//here you implicitly cast value of type DbSet<Customer>
//to IQueryable<Customer>, which is OK
IQueryable<Customer> query = _db.Products;
if (bool) {
//here you're assigning a value of type IQueryable<Customer>
//to a variable of the same type, which is also OK
query = query.Where(p => p.Id == id);
}
Scenario 3:
//I assume you have the following line in your code
var products = _db.Products;
//query is of type IQueryable<Customer>, because you perform
//a query on the DbSet<Product>
var query = from product in products
select product;
if (bool) {
//here you're assigning a value of type IQueryable<Customer>
//to a variable of the same type, which is OK
query = query.Where(p => p.Id == id);
}
EDIT
It's been a while since I answered this question, and even though the merit of it still stands, I tend to use a slightly different approach (which might have not been available at the time of original answer, I'm not sure).
The simplest (and I believe safest) way of casting an object implementing IQueryable<T> to IQueryable<T> is this:
var query = _db.Products.AsQueryable();
This simply returns the subject of the call to its IQueryable<T> interface implementation. It should not produce any overhead when executing the query. Now, there are comments suggesting to use some tricks, using which I believe might be a bad idea.
One example of such trick is to use this:
var queryable = query.Select(x => x);
While being (almost) completely benign when querying objects, it can do some harm when dealing with some implementations of IQueryable<T>. Namely, when the query is translated to, for example, a SQL query, it most likely will add a redundant "SELECT * FROM ..." to the executed query. That's the best case scenario - in the most probable scenario it will add something greatly more tedious - something like "SELECT x.P1, x.P2, ... FROM ... AS x". Of course, you might be OK with it, but you should be aware of it. Aware of the fact that, depending on implementation, calls like that might not be "free", even though appearing to do nothing.
Another example:
query.Where(x => true)
will potentially add a WHERE 1=1 to your SQL query.
When using var the compiler infers the type of the expression to the right of the assignment. When you write
var query = _db.Products;
query is of type DbSet<Product>, and it cannot be assigned any IQueryable<Product>, which the Where extension method returns.
When you used the query syntax, query was again IQueryable<Product>, which made it work. It's equivalent to writing
var query = products.Select(t => t);
The Select extension method, like Where, returns IQueryable<Product>.
It's because _db.Products isn't a query, it is a DbSet.
The second block works because you are casting it to IQueryable, and the last one works because it is an actual query.
var query = _db.Products.Where(x => true);
if (bool) {
query = query.Where(p => p.Id == id);
}
I am writing a query which contains several join operations. I want to write this query in a function so that I can call this function everytime I need this query. My function and the query are as follows. Since I have several join operations and I don't want to define a complex return type, I keep the return type just IQueryable.
private IQueryable getMySubQuery(MyContext db)
{
var query = db.Orders
.Join( ... )
.Join( ... )
.Join( ... );
return query;
}
public IQueryable <MyType> getData()
{
var db = ...
...
...
var query = getMySubQuery(db)
.Select( /// ERROR ???
return query;
}
I am getting an error: System.Linq.IQueryable doesn't contain a definition for Select.
I understand that if I define the return type of getMySubQuery() method as IQueryable <SomeType>, it will solve the problem. But the problem is I have to define a very complex type containing 50 fields. I don't want to do the projection in getMySubQuery() function. Is there any way I can solve it without creating the complex type? I want to use IQueryable rather than IEnumerable?
If this sample code is truly representative of your actual code then the problem is you're returning an IQueryable, not an IQueryable<T>. The reason you're doing that is because you're trying to use an anonymous type as the result of getMySubQuery, but it's not possible to share anonymous types across method boundaries. If you truly want to do this, you need to create a type that represents the data that is currently being returned as an anonymous type and then make sure you change getMySubQuery to return IQueryable<T> of that type.
Try this: var query = getMySubQuery(db).Tolis()
.Select( /// No ERROR!
In EF, when you want to include a navigation property in the result of a query, you use Include(). Since some queries require calling this more than once, I tried to create a generic wrapper around this concept:
public IQueryable<T> FindAll<P>(params Expression<Func<T, P>>[] predicates) where P : class {
var entities = AllEntities();
foreach (var p in predicates) entities = entities.Include(p);
return entities;
}
And call it as such:
var customers = FindAll(q => q.Orders, q => q.Invoices, q => q.Contacts);
Questions:
The function compiles, but when I call it: "The type arguments for method cannot be inferred from the usage. Try specifying the type arguments explicitly." What am I doing wrong?
If I get it to work, can I call Include() the first time: var customers = FindAll(q => q.Orders, q => q.Invoices); and then again later in a separate step: customers = customers.Include(p => p.Invoices); or will that result in poor performance such that I should do the includes in one go?
EDIT:
JonSkeet's answer is correct, of course, and yet there is this solution which seems to do what I want. Not sure why it works however. Is it because of the Aggregate() function?
Fundamentally, I think you've got a problem - assuming that q.Orders, q.Invoices and q.Contacts return different types, you can't express what you want to happen. You probably want something like:
entities.Include<Parent, Order>(p => p.Order);
entities.Include<Parent, Invoice>(p => p.Invoices);
entities.Include<Parent, Contact>(p => p.Contacts);
... so you want a different value for P for each predicate. But you're calling a single method, providing a single type argument for P.
It's not clear that this is really giving you much benefit anyway... couldn't you just write:
var customers = AllEntities().Include(q => q.Orders)
.Include(q => q.Invoices)
.Include(q => q.Contacts);
I'd say that's clearer and it gets round the "multiple type parameters" problem.
What is the exact use of AsEnumerable? Will it change non-enumerable collection to enumerable
collection?.Please give me a simple example.
From the "Remarks" section of the MSDN documentation:
The AsEnumerable<TSource> method has no effect
other than to change the compile-time
type of source from a type that
implements IEnumerable<T> to
IEnumerable<T> itself.
AsEnumerable<TSource> can be used to choose
between query implementations when a
sequence implements IEnumerable<T> but also has a different set
of public query methods available. For
example, given a generic class Table
that implements IEnumerable<T> and has its own methods such
as Where, Select, and SelectMany, a
call to Where would invoke the public
Where method of Table. A Table type
that represents a database table could
have a Where method that takes the
predicate argument as an expression
tree and converts the tree to SQL for
remote execution. If remote execution
is not desired, for example because
the predicate invokes a local method,
the AsEnumerable<TSource>
method can be used to hide the custom
methods and instead make the standard
query operators available.
If you take a look in reflector:
public static IEnumerable<TSource> AsEnumerable<TSource>(this IEnumerable<TSource> source)
{
return source;
}
It basically does nothing more than down casting something that implements IEnumerable.
Nobody has mentioned this for some reason, but observe that something.AsEnumerable() is equivalent to (IEnumerable<TSomething>) something. The difference is that the cast requires the type of the elements to be specified explicitly, which is, of course, inconvenient. For me, that's the main reason to use AsEnumerable() instead of the cast.
AsEnumerable() converts an array (or list, or collection) into an IEnumerable<T> of the collection.
See http://msdn.microsoft.com/en-us/library/bb335435.aspx for more information.
From the above article:
The AsEnumerable<TSource>(IEnumerable<TSource>) method has no
effect other than to change the compile-time type of source from a type
that implements IEnumerable<T> to IEnumerable<T> itself.
After reading the answers, i guess you are still missing a practical example.
I use this to enable me to use linq on a datatable
var mySelect = from table in myDataSet.Tables[0].AsEnumerable()
where table["myColumn"].ToString() == "Some text"
select table;
AsEnumerable can only be used on enumerable collections. It just changes the type of the collection to IEnumerable<T> to access more easily the IEnumerable extensions.
No it doesn't change a non-enumerable collection to an enumerable one. What is does it return the collection back to you as an IEnumerable so that you can use it as an enumerable. That way you can use the object in conjunction with IEnumerable extensions and be treated as such.
Here's example code which may illustrate LukeH's correct explanation.
IEnumerable<Order> orderQuery = dataContext.Orders
.Where(o => o.Customer.Name == "Bob")
.AsEnumerable()
.Where(o => MyFancyFilterMethod(o, MyFancyObject));
The first Where is Queryable.Where, which is translated into sql and run in the database (o.Customer is not loaded into memory).
The second Where is Enumerable.Where, which calls an in-memory method with an instance of something I don't want to send into the database.
Without the AsEnumerable method, I'd have to write it like this:
IEnumerable<Order> orderQuery =
((IEnumerable<Order>)
(dataContext.Orders.Where(o => o.Customer.Name == "Bob")))
.Where(o => MyFancyFilterMethod(o, MyFancyObject));
Or
IEnumerable<Order> orderQuery =
Enumerable.Where(
dataContext.Orders.Where(o => o.Customer.Name == "Bob"),
(o => MyFancyFilterMethod(o, MyFancyObject));
Neither of which flow well at all.
static void Main()
{
/*
"AsEnumerable" purpose is to cast an IQueryable<T> sequence to IEnumerable<T>,
forcing the remainder of the query to execute locally instead of on database as below example so it can hurt performance. (bind Enumerable operators instead of Queryable).
In below example we have cars table in SQL Server and are going to filter red cars and filter equipment with some regex:
*/
Regex wordCounter = new Regex(#"\w");
var query = dataContext.Cars.Where(car=> article.Color == "red" && wordCounter.Matches(car.Equipment).Count < 10);
/*
SQL Server doesn’t support regular expressions therefore the LINQ-to-db providers will throw an exception: query cannot be translated to SQL.
TO solve this firstly we can get all cars with red color using a LINQ to SQL query,
and secondly filtering locally for Equipment of less than 10 words:
*/
Regex wordCounter = new Regex(#"\w");
IEnumerable<Car> sqlQuery = dataContext.Cars
.Where(car => car.Color == "red");
IEnumerable<Car> localQuery = sqlQuery
.Where(car => wordCounter.Matches(car.Equipment).Count < 10);
/*
Because sqlQuery is of type IEnumerable<Car>, the second query binds to the local query operators,
therefore that part of the filtering is run on the client.
With AsEnumerable, we can do the same in a single query:
*/
Regex wordCounter = new Regex(#"\w");
var query = dataContext.Cars
.Where(car => car.Color == "red")
.AsEnumerable()
.Where(car => wordCounter.Matches(car.Equipment).Count < 10);
/*
An alternative to calling AsEnumerable is ToArray or ToList.
*/
}
The Enumerable.AsEnumerable method can be used to hide a type's custom implementation of a standard query operator
Consider the following example. we have a custom List called MyList
public class MyList<T> : List<T>
{
public string Where()
{
return $"This is the first element {this[0]}";
}
}
MyList has a method called Where which is Enumerable.Where() exact same name. when I use it, actually I am calling my version of Where, not Enumerable's version
MyList<int> list = new MyList<int>();
list.Add(4);
list.Add(2);
list.Add(7);
string result = list.Where();
// the result is "This is the first element 4"
Now how can I find the elements which are less than 5 with the Enumerable's version of Where?
The answer is: Use AsEnumerable() method and then call Where
IEnumerable<int> result = list.AsEnumerable().Where(e => e < 5);
This time the result contains the list of elements that are less than 5