TargetInvocationException on Nhibernate QueryOver with JoinAlias - nhibernate

Product productAlias = null;
Session sessionAlias = null;
Slot slotAlias = null;
Price priceAlias = null;
var queryOver = session.QueryOver<Slot>(() => slotAlias)
.JoinAlias(() => slotAlias.Session, () => sessionAlias)
.JoinAlias(() => sessionAlias.Product, () => productAlias);
if (productGuid.HasValue)
{
var productEntity = session.Query<Product>().FirstOrDefault(x => x.Guid == productGuid.Value);
queryOver = queryOver.Where(() => productAlias.Id == productEntity.Id);
}
if (onlyAvailable)
{
queryOver = queryOver.Where(() => slotAlias.StartDate >= DateTimeOffset.UtcNow.AddMinutes(productAlias.Duration));
}
queryOver.List();
When I run this query, I get TargetInvocationException. In the inner message it's a NullReferenceException at slotAlias.StartDate (Line 18, inside onlyAvailable if clause).
Is there something wrong with using aliases with if clauses and multiple Where clauses like this?
StackTrace:
System.Reflection.TargetInvocationException: Exception has been thrown by the target of an invocation. ---> System.NullReferenceException: Object reference not set to an instance of an object.
at lambda_method(Closure )
--- End of inner exception stack trace ---
at System.RuntimeMethodHandle.InvokeMethod(Object target, Object[] arguments, Signature sig, Boolean constructor)
at System.Reflection.RuntimeMethodInfo.UnsafeInvokeInternal(Object obj, Object[] parameters, Object[] arguments)
at System.Delegate.DynamicInvokeImpl(Object[] args)
at NHibernate.Impl.ExpressionProcessor.FindValue(Expression expression)
at NHibernate.Impl.ExpressionProcessor.ProcessSimpleExpression(Expression left, Expression right, ExpressionType nodeType)
at NHibernate.Impl.ExpressionProcessor.ProcessSimpleExpression(BinaryExpression be)
at NHibernate.Impl.ExpressionProcessor.ProcessBinaryExpression(BinaryExpression expression)
at NHibernate.Impl.ExpressionProcessor.ProcessExpression(Expression expression)
at NHibernate.Criterion.QueryOver`2.NHibernate.IQueryOver<TRoot,TSubType>.Where(Expression`1 expression)
at Fullseats.Database.Repositories.Repository.GetSlots(Int32 limit, Int32 offset, Nullable`1 operatorGuid, Nullable`1 productGuid, Nullable`1 from, Nullable`1 to, Boolean onlyAvailable) in C:\Users\erkin\Desktop\FullSeats\Fullseats.Api\Fullseats.Database\Repositories\Repository.cs:line 455
at Fullseats.Server.Core.Services.ProductService.GetSlots(Guid productGuid, PaginationQuery paginationQuery) in C:\Users\erkin\Desktop\FullSeats\Fullseats.Api\Fullseats.Server\Core\Services\ProductService.cs:line 63
at Fullseats.Server.Modules.ProductModule.GetSlotsForProduct(Object arg) in C:\Users\erkin\Desktop\FullSeats\Fullseats.Api\Fullseats.Server\Modules\ProductModule.cs:line 224
at CallSite.Target(Closure , CallSite , Func`2 , Object )
at System.Dynamic.UpdateDelegates.UpdateAndExecute2[T0,T1,TRet](CallSite site, T0 arg0, T1 arg1)
at Nancy.Routing.Route.<>c__DisplayClass4.<Wrap>b__3(Object parameters, CancellationToken context)

With this line:
queryOver.Where(
() => slotAlias.StartDate >= DateTimeOffset.UtcNow.AddMinutes(productAlias.Duration));
You're essentially mixing application-side logic with database-side logic. DateTimeOffset.UtcNow.AddMinutes is executed before the query is sent to the database. That's why passing constant values, but not productAlias.Duration, worked.
Since you're not sending a predefined value as a parameter, you'll need to perform the date manipulation in the database.
Doing this might require a few steps, depending on your dialect. I'm going to assume SQL Server 2012.
Create a custom dialect with a function that defines an addminutes function:
public class MyDialect : MsSql2012Dialect
{
public MyDialect()
{
this.RegisterFunction(
"addminutes",
new SQLFunctionTemplate(NHibernateUtil.DateTimeOffset, "dateadd(n, ?1, ?2)"));
}
}
Use the newly registered function in your query:
queryOver = queryOver.Where(
Restrictions.GeProperty(
Projections.Property(() => slotAlias.StartDate),
Projections.SqlFunction(
"addminutes",
NHibernateUtil.DateTimeOffset,
Projections.Property(() => productAlias.Duration),
Projections.Constant(DateTimeOffset.UtcNow))))
This generates a SQL snippet that looks like this:
WHERE
slotAlia_1.StartDate >= dateadd(minute, this_.Duration, '8/9/2016 2:22:48 AM +00:00');
The C# can be a bit hard to read, you could refactor out the SqlFunction part:
var addMinutesFunction = Projections.SqlFunction(
"addMinutes",
NHibernateUtil.DateTimeOffset,
Projections.Property(() => productAlias.Duration),
Projections.Constant(DateTimeOffset.UtcNow))
queryOver = queryOver.Where(
Restrictions.GeProperty(
Projections.Property(() => slotAlias.StartDate), addMinutesFunction))

Related

Avoid NVARCHAR when using ToList() in Linq

I have a property in a class with Column(TypeName) set to VARCHAR but when the linq uses ToList(), the SQL generated by linq converts to NVARCHAR instead. Is there a way to avoid nvarchar conversion which happens when ToList() method is called ?
var codes = xyz.Where(x => x.IsValid).Select(x => x.Code.ToLower()).ToList();
requests = requests.Where(p => codes.Contains(p.Type.ToLower()));
Property(c => c.Type).HasColumnType("varchar").HasMaxLength(3).IsFixedLength();
As shown above, though the Type property has column type set to VARCHAR but linq uses NVARCHAR when ToList() is used.
You can build your own predicate:
public static class PredicateBuilder
{
private static readonly MethodInfo asNonUnicodeMethodInfo =
typeof(EntityFunctions).GetMethod("AsNonUnicode");
private static readonly MethodInfo stringEqualityMethodInfo =
typeof(string).GetMethod("op_Equality");
public static Expression<Func<TEntity, bool>> ContainsNonUnicodeString<TEntity>(
IEnumerable<string> source,
Expression<Func<TEntity, string>> expression)
{
if (source == null) throw new ArgumentNullException("source");
if (expression == null) throw new ArgumentNullException("expression");
Expression predicate = null;
foreach (string value in source)
{
var fragment = Expression.Equal(
expression.Body,
Expression.Call(null,
asNonUnicodeMethodInfo,
Expression.Constant(value, typeof(string))),
false,
stringEqualityMethodInfo);
if (predicate == null)
{
predicate = fragment;
}
else
{
predicate = Expression.OrElse(predicate, fragment);
}
}
return Expression.Lambda<Func<TEntity, bool>>(predicate,
((LambdaExpression)expression).Parameters);
}
}
See more here: https://learn.microsoft.com/da-dk/archive/blogs/diego/workaround-for-performance-with-enumerable-contains-and-non-unicode-columns-against-ef-in-net-4-0

Ravendb Search Query Failed

Here is index class
public class Facility_SearchTerm : AbstractIndexCreationTask<FacilityApplication>
{
public Facility_SearchTerm()
{
Map = facilities => from facility in facilities
select new
{
facility.FacilityName
};
Indexes.Add(x => x.FacilityName, FieldIndexing.Analyzed);
}
}
And here is a simple query
var query = session.Advanced.DocumentQuery<FacilityApplication, Facility_SearchTerm>()
.Statistics(out statsRef)
.Search(s => s.FacilityName, "abc")
.Skip(size * (page - 1))
.Take(size);
What is wrong? I got the error
"Query failed. See inner exception for details. at Raven.Client.Connection.ServerClient.Query(String index, IndexQuery query, String[] includes, Boolean metadataOnly, Boolean indexEntriesOnly)

How to return IEnumerable <int> in WebAPI (applying filters manually)

I want to return all IDs of my process (Processo class) (apply filters and order before), something like this:
Url: api/processos/getIds?&$filter=(Id eq 1)
public IEnumerable<int> getIds(ODataQueryOptions opts)
{
var results = Repositorio.Query<Processo>();
results = opts.ApplyTo(results) as IQueryable<Processo>;
return results.Select(p => p.Id).ToArray();
}
Error
Full image in: http://i.stack.imgur.com/gzJ7n.jpg
Exception
System.ArgumentException was unhandled by user code
HResult=-2147024809
Message=Cannot apply ODataQueryOptions of 'System.Int32' to IQueryable of 'CreditoImobiliarioBB.Model.Processo'.
Parameter name: query
Source=System.Web.Http.OData
ParamName=query
StackTrace:
at System.Web.Http.OData.Query.ODataQueryOptions`1.ValidateQuery(IQueryable query)
at System.Web.Http.OData.Query.ODataQueryOptions`1.ApplyTo(IQueryable query)
at CreditoImobiliarioBB.Web.Api.processosController.getIds(ODataQueryOptions opts) in w:\Clients\creditoimobiliariobb.com.br\src\CreditoImobiliarioBB\CreditoImobiliarioBB.Web\Api\processosController.cs:line 39
at lambda_method(Closure , Object , Object[] )
at System.Web.Http.Controllers.ReflectedHttpActionDescriptor.ActionExecutor.<>c__DisplayClass13.<GetExecutor>b__c(Object instance, Object[] methodParameters)
at System.Web.Http.Controllers.ReflectedHttpActionDescriptor.ActionExecutor.Execute(Object instance, Object[] arguments)
at System.Web.Http.Controllers.ReflectedHttpActionDescriptor.<>c__DisplayClass5.<ExecuteAsync>b__4()
at System.Threading.Tasks.TaskHelpers.RunSynchronously[TResult](Func`1 func, CancellationToken cancellationToken)
InnerException:
You need to give ODataQueryOptions a type argument, otherwise it will use the action's return type to build the options. Here is the fixed code:
public IEnumerable<int> getIds(ODataQueryOptions<Processo> opts)
{
var results = Repositorio.Query<Processo>();
results = opts.ApplyTo(results) as IQueryable<Processo>;
return results.Select(p => p.Id).ToArray();
}

Can't get $filter to work with open types in WCF Data Service

I'm building a data service in WCF and I'm using a combination of reflection and open types as some of the data elements need to be created on-the-fly. Most everything is working well, but I can't get filters to work with the open type values.
The error I get is:
<message xml:lang="en-US">An error occurred while processing this request.</message>
<innererror>
<message>The method or operation is not implemented.</message>
<type>System.NotImplementedException</type>
<stacktrace> at lambda_method(Closure , GeographyProvider )
at System.Linq.Enumerable.WhereEnumerableIterator`1.MoveNext()
at System.Data.Services.DataService`1.SerializeResponseBody(RequestDescription description, IDataService dataService)
at System.Data.Services.DataService`1.HandleRequest()</stacktrace>
</innererror>
I'm using an expression visitor to rewrite the LINQ expressions and it is successfully pulling the value for the open type. At this point, I'm not sure what method or operation I need to implement is. The expression tree looks like this after the expression visitor has done it's work:
Alteryx.Web.API.DatasetProvider+<GetDatasets>d__0.Where(element =>
(element.Variant == "AGSSTD_701000")).SelectMany(element =>
ConvertChecked(element.Geographies)).Where(element =>
(element.Key == "County")).SelectMany(element =>
ConvertChecked(element.Geographies)).Where(element =>
(element.Key == "36")).SelectMany(element =>
ConvertChecked(element.Geographies)).Where(it =>
Convert(((Invoke((o, name) => GetOpenValue(o, name), it, "POPCY") >= Convert(100000)) == True)))}
I've put a break point in the GetOpenValue method and it is getting called and returning the correct value. Any thoughts on where I need to go from here?
Based on Vitek's suggestions, I added checks for Convert and the comparison methods to my expression visitor, but they aren't found. Here is what my visitor code looks like:
static readonly MethodInfo GetValueOpenPropertyMethodInfo =
typeof(OpenTypeMethods)
.GetMethod(
"GetValue",
BindingFlags.Static | BindingFlags.Public,
null,
new Type[] { typeof(object), typeof(string) },
null
);
static readonly MethodInfo OpenConvertMethodInfo =
typeof(OpenTypeMethods)
.GetMethod(
"Convert",
BindingFlags.Static | BindingFlags.Public,
null,
new Type[] { typeof(object), typeof(ResourceType) },
null
);
static readonly MethodInfo GreaterThanOrEqualMethodInfo =
typeof(OpenTypeMethods)
.GetMethod(
"GreaterThanOrEqual",
BindingFlags.Static | BindingFlags.Public,
null,
new Type[] { typeof(object), typeof(object) },
null
);
static readonly MethodInfo EqualMethodInfo =
typeof(OpenTypeMethods)
.GetMethod(
"Equal",
BindingFlags.Static | BindingFlags.Public,
null,
new Type[] { typeof(object), typeof(object) },
null
);
static readonly Expression<Func<object, string, object>> GetValueOpenReplacement =
(o, name) => GetOpenValue(o, name);
static object GetOpenValue(object o, string name)
{
return (o as OpenDataProvider).GetValue(name);
}
static readonly Expression<Func<object, object, object>> GetGreaterThanOrEqualReplacement =
(left, right) => GetOpenGreaterThanOrEqual(left, right);
static object GetOpenGreaterThanOrEqual(object left, object right)
{
string s = left.ToString();
return true;
}
static readonly Expression<Func<object, object, object>> GetEqualReplacement =
(left, right) => GetOpenEqual(left, right);
static object GetOpenEqual(object left, object right)
{
string s = left.ToString();
return true;
}
protected override Expression VisitMethodCall(
MethodCallExpression node
)
{
if (node.Method == GetValueOpenPropertyMethodInfo)
{
// Arguments[0] - the resource to get property from
// Arguments[1] - the ResourceProperty to get
// Invoke the replacement expression, passing the
// appropriate parameters.
if (node.Arguments[0].Type.BaseType == typeof(OpenDataProvider))
{
OpenDataProvider.RequestValue(((ConstantExpression)node.Arguments[1]).Value.ToString());
}
return Expression.Invoke(
Expression.Quote(GetValueOpenReplacement),
node.Arguments[0],
node.Arguments[1]
);
}
else if (node.Method == OpenConvertMethodInfo)
{
// Arguments[0] – the resource
// Arguments[1] – the ResourceType
// no need to do anything, so just
// return the argument
return this.Visit(node.Arguments[0]);
}
else if (node.Method == GreaterThanOrEqualMethodInfo)
{
// Invoke the replacement expression, passing the
// appropriate parameters.
return Expression.Invoke(
Expression.Quote(GetGreaterThanOrEqualReplacement),
node.Arguments[0],
node.Arguments[1]
);
}
else if (node.Method == EqualMethodInfo)
{
// Invoke the replacement expression, passing the
// appropriate parameters.
return Expression.Invoke(
Expression.Quote(GetEqualReplacement),
node.Arguments[0],
node.Arguments[1]
);
}
return base.VisitMethodCall(node);
}
I've put breakpoints in all of the if blocks in the VisitMethodCall method, but only the GetValueOpenProperty block is ever called.
Thanks!
Vitek kindly provided the answer to this here: http://social.msdn.microsoft.com/Forums/en-US/adodotnetdataservices/thread/bfb62cf5-48cc-4435-ae9a-76e4a13d762a
To summarize, the ExpressionVisitor needs to overide the OpenTypeMethods in the VisitBinary method.
Thanks for the help, Vitek!

To call SelectMany dynamically in the way of System.Linq.Dynamic

In System.Linq.Dynamic, there are a few methods to form Select, Where and other Linq statements dynamically. But there is no for SelectMany.
The method for Select is as the following:
public static IQueryable Select(this IQueryable source, string selector, params object[] values)
{
if (source == null) throw new ArgumentNullException("source");
if (selector == null) throw new ArgumentNullException("selector");
LambdaExpression lambda = DynamicExpression.ParseLambda(source.ElementType, null, selector, values);
IQueryable result = source.Provider.CreateQuery(
Expression.Call(
typeof(Queryable), "Select",
new Type[] { source.ElementType, lambda.Body.Type },
source.Expression, Expression.Quote(lambda)));
return result;
}
I tried to modify the above code, after hours working, I couldn't find a way out.
Any suggestions are welcome.
Ying
Already implemented this one for our project, let me know if it works for you!
public static IQueryable SelectMany(this IQueryable source, string selector, params object[] values)
{
if (source == null)
throw new ArgumentNullException("source");
if (selector == null)
throw new ArgumentNullException("selector");
// Parse the lambda
LambdaExpression lambda =
DynamicExpression.ParseLambda(source.ElementType, null, selector, values);
// Fix lambda by recreating to be of correct Func<> type in case
// the expression parsed to something other than IEnumerable<T>.
// For instance, a expression evaluating to List<T> would result
// in a lambda of type Func<T, List<T>> when we need one of type
// an Func<T, IEnumerable<T> in order to call SelectMany().
Type inputType = source.Expression.Type.GetGenericArguments()[0];
Type resultType = lambda.Body.Type.GetGenericArguments()[0];
Type enumerableType = typeof(IEnumerable<>).MakeGenericType(resultType);
Type delegateType = typeof(Func<,>).MakeGenericType(inputType, enumerableType);
lambda = Expression.Lambda(delegateType, lambda.Body, lambda.Parameters);
// Create the new query
return source.Provider.CreateQuery(
Expression.Call(
typeof(Queryable), "SelectMany",
new Type[] { source.ElementType, resultType },
source.Expression, Expression.Quote(lambda)));
}
I have added another SelectMany that retuns an AnonymousType.
public static IQueryable SelectMany(this IQueryable source, string selector, string resultsSelector, params object[] values)
{
if (source == null)
throw new ArgumentNullException("source");
if (selector == null)
throw new ArgumentNullException("selector");
// Parse the lambda
LambdaExpression lambda =
DynamicExpression.ParseLambda(source.ElementType, null, selector, values);
// Fix lambda by recreating to be of correct Func<> type in case
// the expression parsed to something other than IEnumerable<T>.
// For instance, a expression evaluating to List<T> would result
// in a lambda of type Func<T, List<T>> when we need one of type
// an Func<T, IEnumerable<T> in order to call SelectMany().
Type inputType = source.Expression.Type.GetGenericArguments()[0];
Type resultType = lambda.Body.Type.GetGenericArguments()[0];
Type enumerableType = typeof(IEnumerable<>).MakeGenericType(resultType);
Type delegateType = typeof(Func<,>).MakeGenericType(inputType, enumerableType);
lambda = Expression.Lambda(delegateType, lambda.Body, lambda.Parameters);
ParameterExpression[] parameters = new ParameterExpression[] {
Expression.Parameter(source.ElementType, "outer"), Expression.Parameter(resultType, "inner") };
LambdaExpression resultsSelectorLambda = DynamicExpression.ParseLambda(parameters, null, resultsSelector, values);
// Create the new query
return source.Provider.CreateQuery(
Expression.Call(
typeof(Queryable), "SelectMany",
new Type[] { source.ElementType /*TSource*/, /*,TCollection*/resultType /*TResult*/, resultsSelectorLambda.Body.Type},
source.Expression, Expression.Quote(lambda), Expression.Quote(resultsSelectorLambda)));
}
I still need to figure out how to do the following using Dynamic, the goal is to return a new result object.
var customerandorderflat = db.Customers
.SelectMany(c => c.Orders.SelectMany(o => o.Order_Details,
(ord, orddetail) => new
{
OrderID = ord.OrderID,
UnitPrice = orddetail.UnitPrice
}).DefaultIfEmpty(),
(cus, ord) => new
{
CustomerId = cus.CustomerID,
CompanyName = cus.CompanyName,
OrderId = ord.OrderID == null ? -1 : ord.OrderID,
UnitPrice = ord.UnitPrice
});
I am using the NWDB when I try:
var customerandorderquery = db.Customers .SelectMany(c => c.Orders.DefaultIfEmpty()).Select("new(CustomerId, CompanyName, OrderId)");
I get an error because CompanyName is in Customers not Orders. So it is not seeing the combination of the two objects.
When I do:
.SelectMany(c => c.Orders.DefaultIfEmpty(), (cus, ord) => new { CustomerId = cus.CustomerID, OrderId = ord.OrderID == null ? -1 : ord.OrderID });
It returns the desired result.