Fluent NHibernate does not create IN part of WHERE clause - c#

I have Fluent NHibernate Linq queries where I check values based on run time arrays. A basic example would be something like:
var array = [1,2,3,4,5,6];
using (var session = SessionProvider.SessionFactory.OpenSession())
{
return session.Query<MyObject>().Where(x => array.Contains(x.CompareVal)).ToList();
}
I would expect the generated SQL statement to look something like this:
SELECT CompareVal, Column1, Column2
FROM MyObject
WHERE CompareVal IN (1,2,3,4,5,6)
However, what I'm finding instead is that the generated SQL statement simply emits the WHERE clause (proven by watching in Profiler) and selects the entire table, and then seems to run the filter in memory once it gets all the data back.
Something to note - I have a Generic Repository class that all of these calls are funneled through. The Query method is as follows:
public IList<T> Query(Func<T, bool> criteria)
{
using (var session = SessionProvider.SessionFactory.OpenSession())
{
return session.Query<T>().Where(criteria).ToList();
}
}
Obviously this (lack of a where clause) is not acceptable in a table with a large amount of data. What can I do to force NHibernate to generate the query correctly with the WHERE clause and still keep a generic pattern for repositories?

Does it make a difference if you change your Query method to the following ?
public IList<T> Query(Expression<Func<T, bool>> criteria)
{
using (var session = SessionProvider.SessionFactory.OpenSession())
{
return session.Query<T>().Where(criteria).ToList();
}
}
This is how I usually proceed with a generic Query :
public List<TOut> GetEntitiesLinq<TIn,TOut>(Expression<Func<IQueryable<TIn>,IQueryable<TOut>>> myFunc)
{
var t = (myFunc.Compile())(_session.Query<TIn>()) ;
return t.ToList();
}
Then how I would use it in your case :
var myObjList = myQueryManager.GetEntitiesLinq<MyObject,MyObject>(x=>x.Where(myObj => array.Contains(myObj.CompareVal)));
Hope this will help

Use Any:
return session.Query<MyObject>().Where(x => array.Any(y => y == x.CompareVal)).ToList();
Your repository pattern (using plain Func) automatically materializes your query to list, if you want something to be deferredly executed, use IQueryable, don't use Func only
Something to note - I have a Generic Repository class that all of
these calls are funneled through. The Query method is as follows:
public IList<T> Query(Func<T, bool> criteria)
{
using (var session = SessionProvider.SessionFactory.OpenSession())
{
return session.Query<T>().Where(criteria).ToList();
}
}
Your repository just mimic what is already provided out of the box by NHibernate

Can you use QueryOver and WhereRestrictionOn instead?
session.QueryOver<MyObject>().WhereRestrictionOn(o => o.CompareVal).IsIn(array).List();

Related

How can I do it ? I don't want to add same record in database with Entity Framework

What methods can I use Find, FirstOrDefault, Any ....
public virtual void Add(T entity)
{
using (MovieAppContext _context = new MovieAppContext())
{
var record = _context.Set<T>().Find(entity); // does not work I got e
if (record == null)
{
_context.Set<T>().Add(entity);
_context.SaveChanges();
}
}
}
Given your scope here, DbSet<T>.Find<T>(params object[]) is probably not the method you should be using.
The following solution uses IQueryable<T>.Any<T>(Expression<Func<T, bool>>), a Linq Extension Method that accepts a Predicate\Function.
Code Sample using it
using (MovieAppContext _context = new MovieAppContext())
{
bool queryResult = _context.Set<T>().Any((e) => e.Name.Equals(entity.Name, StringComparison.OrdinalIgnoreCase));
if (queryResult)
{
_context.Set<T>().Add(entity);
_context.SaveChanges();
}
}
Here, we are defining an anonymous function delegate, Func<T, bool>, that is in turn being used to define a Linq Expression, Expression<Func<T, bool>>.
Note
I defined this function predicate expression to evaluate on the entity names (a made-up property of this type; that in this example portrays a unique constraint.)
Also, an alternative to using anonymous functions declaration for this expression is to declare it, then pass it.
That looks like the following:
...
Expression<Func<T, bool>> WhereEntityNameMatches = (e) => e.Name.Equals(entity.Name, StringComparison.OrdinalIgnoreCase));
bool queryResult = _context.Set<T>().Any(WhereEntityNameMatches);
...
Which may help out with reusability and scope considerations.
For consideration, An alternative here would be to not attempt the query lookup and check at all.
Instead, you would do a try-catch for DbUpdateException in the attempt to save changes after\following this Add operation.
try
{
_context.Set<T>().Add(entity);
_context.SaveChanges();
}
catch (DbUpdateException)
{
var isRaisedByUniqueIndexOrConstraint = new[] { "2601", "2627" }.Contains(ex.GetBaseException().Data["HelpLink.EvtID"]) ?? false;
if (!isRaisedByUniqueIndexOrConstraint)
throw;
// Could suppress and\or handle attempted duplicate record adding
}
This relies on those index\constraints existing, so you would first define a Unique Index on the column(s) (as well as your entity type using Data Annotations: IndexAttribute).
Note
Part of this answer\solution purposes getting the Error Number from ADO.NET SqlClient Base Exception to further scope and qualify the cause of exception. The Error Numbers, "2601", "2627", are hard-coded in this example.

Can I clone an IQueryable to run on a DbSet for another DbContext?

Suppose I have built up, through some conditional logic over many steps, an IQueryable<T> instance we'll call query.
I want to get a count of total records and a page of data, so I want to call query.CountAsync() and query.Skip(0).Take(10).ToListAsync(). I cannot call these in succession, because a race condition occurs where they both try to run a query on the same DbContext at the same time. This is not allowed:
"A second operation started on this context before a previous asynchronous operation completed. Use 'await' to ensure that any asynchronous operations have completed before calling another method on this context. Any instance members are not guaranteed to be thread safe."
I do not want to 'await' the first before even starting the second. I want to fire off both queries as soon as possible. The only way to do this is to run them from separate DbContexts. It seems ridiculous that I might have to build the entire query (or 2, or 3) side-by-side starting with different instances of DbSet. Is there any way to clone or alter an IQueryable<T> (not necessarily that interface, but it's underlying implementation) such that I can have one copy that runs on DbContext "A", and another that will run on DbContext "B", so that both queries can be executing simultaneously? I'm just trying to avoid recomposing the query X times from scratch just to run it on X contexts.
There is no standard way of doing that. The problem is that EF6 query expression trees contain constant nodes holding ObjectQuery instances which are bound to the DbContext (actually the underlying ObjectContext) used when creating the query. Also there is a runtime check before executing the query if there are such expressions bound to a different context than the one executing the query.
The only idea that comes in my mind is to process the query expression tree with ExpressionVisitor and replace these ObjectQuery instances with new ones bound to the new context.
Here is a possible implementation of the aforementioned idea:
using System.Data.Entity.Core.Objects;
using System.Data.Entity.Infrastructure;
using System.Linq;
using System.Linq.Expressions;
namespace System.Data.Entity
{
public static class DbQueryExtensions
{
public static IQueryable<T> BindTo<T>(this IQueryable<T> source, DbContext target)
{
var binder = new DbContextBinder(target);
var expression = binder.Visit(source.Expression);
var provider = binder.TargetProvider;
return provider != null ? provider.CreateQuery<T>(expression) : source;
}
class DbContextBinder : ExpressionVisitor
{
ObjectContext targetObjectContext;
public IQueryProvider TargetProvider { get; private set; }
public DbContextBinder(DbContext target)
{
targetObjectContext = ((IObjectContextAdapter)target).ObjectContext;
}
protected override Expression VisitConstant(ConstantExpression node)
{
if (node.Value is ObjectQuery objectQuery && objectQuery.Context != targetObjectContext)
return Expression.Constant(CreateObjectQuery((dynamic)objectQuery));
return base.VisitConstant(node);
}
ObjectQuery<T> CreateObjectQuery<T>(ObjectQuery<T> source)
{
var parameters = source.Parameters
.Select(p => new ObjectParameter(p.Name, p.ParameterType) { Value = p.Value })
.ToArray();
var query = targetObjectContext.CreateQuery<T>(source.CommandText, parameters);
query.MergeOption = source.MergeOption;
query.Streaming = source.Streaming;
query.EnablePlanCaching = source.EnablePlanCaching;
if (TargetProvider == null)
TargetProvider = ((IQueryable)query).Provider;
return query;
}
}
}
}
One difference with the standard EF6 LINQ queries is that this produces ObjectQuery<T> rather than DbQuery<T>, although except that ToString() does not return the generated SQL, I haven't noticed any difference in the further query building / execution. It seems to work, but use it with care and on your own risk.
You could write a function to build up your query, taking DbContext as a parameter.
public IQueryable<T> MyQuery(DbContext<T> db)
{
return db.Table
.Where(p => p.reallycomplex)
....
...
.OrderBy(p => p.manythings);
}
I've done this many times and it works well.
Now it's easy to make queries with two different contexts:
IQueryable<T> q1 = MyQuery(dbContext1);
IQueryable<T> q2 = MyQuery(dbContext2);
If your concern was the execution time taken to build the IQueryable objects, then my only suggestion is don't worry about it.
So you have an IQueryable<T> that will be performed on DbContext A as soon as the query is executed and you want the same query to run on DbContext B when the query is executed.
For this you'll have to understand the difference between an IEnumerable<T> and an IQueryable<T>.
An IEnumerable<T> holds all code to enumerate over the elements that the enumerable represents. The enumeration starts when GetEnumerator and MoveNext are called. This can be done explicitly. However it is usually done implicitly by functions like foreach, ToList, FirstOrDefault, etc.
An IQueryable does not hold the code to enumerate, it holds an Expression and a Provider. The Provider knows who will execute the query, and it knows how to translate the Expression into the language that is understood by the query executioner.
Due to this separation, it is possible to let the same Expression be executed by different data sources. They don't even have to be of the same type: one data source can be a database management system that understands SQL, the other one could be a comma separated file.
As long as you concatenate Linq statements that return an IQueryable, the query is not executed, only the Expression is changed.
As soon as enumeration starts, either by calling GetEnumerator / MoveNext, or by using foreach or one of the LINQ functions that do not return an IQueryable, the Provider will translate the Expression into the language the the data source understands and communicates with the data source to execute the query. The result of the query is an IEnumerable, which can be enumerated as if all data was in local code.
Some Providers are smart and use some buffering, so that not all data is transferred to local memory, but only part of the data. New data is queried when needed. So if you do a foreach in a database with a zillion elements, only the first few (thousands) elements are queried. More data is queried if your foreach runs out of fetched data.
So you already have one IQueryable<T>, therefore you have an Expression a Provider and an ElementType. You want the same Expression / ElementType to be executed by a differentProvider. You even want to change theExpression` slightly before you execute it.
Therefore you need to be able to create an object that implements IQueryable<T> and you want to be able to set the Expression, ElementType and a Provider
class MyQueryable<T> : IQueryable<T>
{
public type ElementType {get; set;}
public Expression Expression {get; set;}
public Provider Provider {get; set;}
}
IQueryable<T> queryOnDbContextA= dbCotextA ...
IQueryable<T> setInDbContextB = dbContextB.Set<T>();
IQueryable<T> queryOnDbContextB = new MyQueryable<T>()
{
ElementType = queryOnDbContextA.ElementType,
Expression = queryOnDbContextB.Expression,
Provider = setInDbContextB.Provider,
}
If desired you can adjust the query on the other context before executing it:
var getPageOnContextB = queryOnDbContextB
.Skip(...)
.Take(...);
Both queries are still not executed yet. Execute them:
var countA = await queryOnContextA.CountAsync();
var fetchedPageContextB = await getPageOnContextB.ToListAsync();

Automated tests for Expression translation

I am using PredicateBuilder to build reusable expressions as return values of objects. For example:
public interface ISurveyEligibilityCriteria
{
Expression<Func<Client, bool>> GetEligibilityExpression();
}
I want to have automated tests that determine whether a particular expression is translateable into T-SQL by Entity Framework (ie that it doesn't throw a NotSupportedException while "executing"). I can't find anything on the internet - is this possible (seems like it should be)?
You can create a LINQ statement containing the expression and then check whether it can be translated without actually executing it:
var connString = #"server=x;database=x";
using(var db = new MyContext(connString))
{
// ToString() shows the generated SQL string.
var sql = db.Entities.Where(generatedExpression).ToString();
Assert.IsTrue(sql.StartsWith("SELECT");
}
In the Assert you can test anything you'd expect to be part of the generated SQL string, but of course if the expression can't be translated, the test will fail because e.g. a NotSupportedException is thrown.
You can wrap this up into a handy extension method:
public static class EntityFrameworkExtensions
{
public static void CompilePredicate<T>(this DbContext context, Expression<Func<T, bool>> predicate)
where T : class
{
context.Set<T>().Where(predicate).ToString();
}
}
Then in your test:
// act
Action act = () => context.CompilePredicate(predicate);
// assert
act.ShouldNotThrow();
A very simple solution is executing it:
using (var context = ...)
{
// The query will return null, but will be executed.
context.Clients.Where(GetEligibilityExpression())
.Where(() => false)
.SingleOrDefault();
}
In older versions of EF (or using ObjectContext) you could have tried "manually" compiling the query with CompiledQuery.Compile, but this isn't supported with DbContext.

Running inline SQL with Entity Framework 5 and returning tracked Entities

How can I execute a command such as:
SELECT * FROM CATS
That behaves exactly as if I'd done myContext.Cats.Where(c => true); ?
Google suggested context.Database.ExecuteSqlCommand() but that returns an int. There is also context.Database.SqlQuery which looks promising, but it says that entities returned are not tracked, which I suspect is important (I'm not really familiar with how EF works in terms of tracking things).
It suggests using System.Data.Entity.DbSet<TEntity>.SqlQuery(Object[]) to track it, but I'm not entirely sure what this means or how to implement it.
In addition to make it even more confusing I want to write a generic method to allow me to execute the specific query against any table.
Here's a rough example of what I'd like in pseudocode
public DbSet<T> ExecuteSelect<T>(DbContext context, string table)
{
DbSet<T> entities = context.RunSql("SELECT * FROM " + table);
return entities;
}
Any ideas?
Accoring to this: http://msdn.microsoft.com/en-us/data/jj592907.aspx you want the following:
public IEnumerable<T> ExecuteSelect<T>(DbContext context, string table)
{
IEnumerable<T> entities = context.Set<T>.SqlQuery("SELECT * FROM " + table).ToList();
return entities;
}
myContext.Cats.Where(c => true) returns IQueriable<Cat> (not DbSet)
BUT
Your returned set will actually be finalized already (eg you cant add extra bits to your query later) so having it Queriable is misdirecting.
You can execute sql queries directly as follows :
private int DeleteData()
{
using (var ctx = new MyEntities(this.ConnectionString))
{
if (ctx != null)
{
//Delete command
return ctx.ExecuteStoreCommand("DELETE FROM ALARM WHERE AlarmID > 100");
}
}
return 0;
}
For select we may use
using (var context = new MyContext())
{
var blogs = context.MyTable.SqlQuery("SELECT * FROM dbo.MyTable").ToList();
}

How to share one LINQ query between two methods?

is there any way to share one LINQ query between two methods? I have quite long LINQ query that gets search results from database and I need to use this query to get results (some kind of list<>) -first method - and to get its count (int) - second method -. I don't want to copy this query in two separate methods and I can't return custom class object containing search results and records count (returned by this query). So what I want to do is to get LINQ query definition(or something like this?) but no the results set that I can use in other methods. Maybe there is another good way to do that. thanks for yout help ;)
the code look like this:
public ??? GetSearchResultsQuery(SearchRequest search_request)
{
var queryGetSearchResults = ....
return queryGetSearchResults;
}
public int GetSearchResultsCount(SearchRequest search_request)
{
return GetSearchResultsQuery(search_request).Count();
}
public List<SearchResults> GetSearchResults(SearchRequest search_request)
{
return GetSearchResultsQuery(search_request).Skip(search_request.startRowIndex).Take(search_request.maximumRows).ToList();
}
public IQueryable<SearchedForType> GetSearchResultsQuery(SearchRequest search_request)
{
var queryGetSearchResults = context.SearchedForTypes.Where(x => x == search_request.X);
... build up your search query ...
return queryGetSearchResults;
}

Categories