Dynamics CRM 2011 filtering data returned from web service - where-clause

I am trying to filter data by the EntityReference with no luck. Without the where clause it runs fine with the where clause I get the following error:
The server did not provide a meaningful reply; this might be caused by
a contract mismatch, a premature session shutdown or an internal
server error.
Here is my method that calls the CRMService:
/// <summary>
/// Gets the categories.
/// </summary>
/// <returns></returns>
public IEnumerable<category> GetCategoriesExcludingSomething()
{
IEnumerable<category> data = CrmClient.categorySet.OrderBy(x => x.SubCategory).ThenBy(x => x.itf_name);
return data.Where(x => x.SubCategory.ToString() == "SomethingToExclude");
}
I have tried using SubCategory.Name also but it gives the same error. I think it's related to the fact it uses early binding or something along those lines but I couldn't get any useful information when debugging.
Any advice or help would be great, this should be easy :/

According to this documentation: http://technet.microsoft.com/en-us/library/gg328328.aspx
The LINQ query provider supports a subset of the LINQ operators. Not
all conditions that can be expressed in LINQ are supported.
orderBy
supports ordering by entity attributes, such as Contact.FullName.
What you could do is to use the where clause first, and then use the ToList() method. After this, you'll have a collection of data on which you can use all the common Linq queries.
Also, trying to OrderBy an EntityReference is not the good way to do it. You should try ordering like this:
OrderBy(x => x.SubCategory.Id)
where: The left side of the clause must be an attribute name and the right side of the clause must be a value. You cannot set the left side to a constant. Both the sides of the clause cannot be constants.
Supports the String functions Contains, StartsWith, EndsWith, and Equals
What you could do here is either filtering values by the Id or by the Name (that's the only relevant informations you'll have from the EntityReference in this case).
Where(x => x.SubCategory.Name == "CategoryNameToExclude");

Related

EF Core generating bad SQL [duplicate]

I'd like to be able to implement a search method that can take any arbitrary properties of my POCO class as arguments. This works well:
public static IEnumerable<iUser> Search(DataContext context, Func<iUser, bool> predicate)
{
return from i in context.GetTable<iUser>().Where(predicate) select i;
}
but in this case the filtering appears to take place after collecting all the rows in the table.
Is it possible to use Linq to generate an arbitrary query like this without filtering after the sql call? What approaches would you recommend?
Thanks!
LINQ to DB is an Object-Relational Mapper (ORM) that is capable of translating LINQ expressions into SQL. The word "expression" is important here. A Func is not an expression but a delegate, you have to use Expression<Func<>> in LINQ methods for LINQ to DB to be able to translate them. Otherwise the data will be pulled from the database first after which the Func filters them in memory.
So your function should look like:
public static IEnumerable<iUser> Search(DataContext context,
Expression<Func<iUser, bool>> predicate)
{
return context.GetTable<iUser>().Where(predicate);
}
The return type depends on the what you want the caller of this function to be capable of. If you return IQueryable<iUser> the caller will be able to extend the expression by their own expressions. That is, Search(context, somePredicate).Where(...) will be translated into SQL as a whole. Returning IEnumerable will apply any subsequent predicates (either as Func or as Expression) in memory.
Side note, in order to line up with common naming conventions, if iUser is an interface (I have no idea if LINQ to DB supports interfaces) then you should rename it into IUser, otherwise name it User.

Using another list in an Entity Framework query

Looking to achieve the below but it is failing as the locations.Any() is being treated as an IEnumerable instead of an IQueryable and scalar functions invoked via EF require IQueryable. I need this filter to happen at the database level (not materialize the list first).
How can I get the locations.Any() to be treated as an IQueryable here? I understand the list doesn't exist in the database but is there a way for Entity Framework to understand this any and build and AND statement with nested OR in SQL?
public Address GetAddresses(List<Loctions> locations)
{
_context.Addresses.
Where(a => locations.Any(l => MyContext.CustomFunction(l.PropA,l.PropB, a.PropA, a.ProbB) > 1 ))
}
[DbFunction("fn_DistanceBetweenCoordinates", "dbo")]
public static decimal CustomFunction(decimal SourceLatitude, decimal SourceLongitude, decimal TargetLatitude, decimal TargetLongitude) {
throw new NotImplementedException();
}
You could achieve this by moving CustomFunction into the database and use that server side function when querying from EF.
Please read User defined function mapping and try to adapt the sample according to your use case.
We haven't seen the body of the CustomFunction, so it's impossible to tell if it's viable to do the transfer from client based UDF to server based.
We also don't know how the Locations list is populated. Depending on how that is done, the adaptation of the example code might become more cumbersome.

NHibernate - truly dynamic sorting

Using NHibernate, I need to be able to configure my application to sort a specific collection of entities exactly as needed.
The configurable sort:
can involve multiple properties
can specify the sorted properties in any order
can specify asc/desc on the sorted properties
can sort by custom properties (i.e. there is no corresponding SQL/C# property - it is calculated)
This functionality is inherited from an existing app where parts of the SQL are specified by an administrator and the SQL statement is built/executed dynamically.
Every time I try thinking through a solution I start getting in muddy waters with all kinds of alarms going off in my head regarding maintainability, performance, scalability, security, etc..
For example, I figure the admin can specify a comma delimited string like so:
"Date asc, FirstName asc, LastName desc"
I can split the string and go through a loop matching the property/sort pairings in a case statement and calling .AddOrder(Order.Asc("FirstName")) as necessary. But then, how do I handle custom properties? I could allow the user to specify SQL for calculating custom properties and then allow the user to sort on those like they would FirstName, but I'm seemingly back at dirty/kludge again.
Is there a clean/appropriate way to handle this requirement?
After much thought and a stroke of luck, I may have a solution.
public class CustomOrder : Order
{
private string customOrderSql;
public CustomOrder(string customOrderSql) : base("", true)
{
this.customOrderSql = customOrderSql;
}
public override NHibernate.SqlCommand.SqlString ToSqlString(
ICriteria criteria, ICriteriaQuery criteriaQuery)
{
return new NHibernate.SqlCommand.SqlString(this.customOrderSql);
}
}
I can pass a custom sort string to my repository where I add my CustomOrder as follows:
.AddOrder(new CustomOrder(customSort))
I still can't sort by custom properties but maybe I can get away with applying case statements in the order by clause. I'm still open for better suggestions if they exist.

What to do when your passing in a collection of zero into a hql request?

I have this
public List<TableA> Get(Guid Id, List<int> listId)
{
const string query = "FROM TableA WHERE MyListColumn in (:listId) AND Id in (:Id)";
return session.CreateQuery(query).SetParameterList("listId", listId).SetParameter("Id",Id).List<TableA>().ToList();
}
I am wondering what to do if keys count equals zero? When using a linq way it would jsut return an empty List of TableAs. The only way I can think of is a "if" statement but I am wondering if there is any other way before I do that since I don't want really logic in my method here as it is a repo method.
if(listId.Count == 0){..}
As I get this error
Server Error in '/' Application. An
empty parameter-list generate wrong
SQL; parameter name 'listId'
I consider an if block proper as it's not "program logic": it's query generation.
That said, i personally change the query signature to a MyListColumn =:id for listId.Count==1 and for listId.Count==0 i don't run the query at all: Why have a round-trip when you know that no results will return?
Based on your other questions regarding this topic, I suggest using QueryOver:
The following query will work with listId == null and listId.Count == 0.
public List<TableA> Get(List<int> listId)
{
return session.QueryOver<TableA>()
.Where(Restrictions.In(
Projections.Property<TableA>(e => e.Id),
listId ?? (new List<int>() { }))).List().ToList();
}
I dropped the Guid Id parameter, since that doesn't make much sense here.
Edit:
Yes, there are several ways in NHibernate to query the database (plain SQL, HQL, ICriteria, QueryOver and NHibernate.Linq). All of these have their strengths and weaknesses and I usually try to use the method best suited for the job.
In this I suggestes QueryOver instead of HQL, because you get full Intellisense and compiler support. Also, QueryOver does not throw an error if the list is empty (it will translate to WHERE 1=0). As Jaguar wrote, in that case you could also return an empty list without going to the DB.
I usually only use HQL when QueryOver or ICriteria can't get me the result I want. The Linq provider still lacks some features, so that will currently only work for rather simple queries.
You can also do the above query with Linq. Here is the NHibernate.Linq version of the code above: (this works in NHibernate 3.1, not tested in previous versions)
List<int> idList = new List<int>(){ 1, 2 };
// if idList is null this will still throw an error
var result = session.Query<TableA>()
.Where(e => idList.Contains(e.Id)).ToList();

How can one delete NHibernate objects using a criteria?

This must be a simple question. Given a criteria, how one deletes the entities satisfying the criteria?
The rationale:
HQL and NH criteria are NHibernate specific constructs and as such they are server side DAL implementation details. I do not want them to "leak" to the client side. So, our client side provides LINQ expressions for the server to process. Up until now the requests where select requests and LINQ to NHibernate dealed with them just fine.
However, there is a need to implement batch delete operation now. As usual, the client side provides a LINQ expression and the server is to delete entities satisfying the expression.
Unfortunately, LINQ to NHibernate is of no help here. The most it can do is translate the given LINQ expression to NHibernate criteria.
Anyway, this is the story. I wish to stress that the client side is unaware of NHibernate at all and I like it to stay this way.
P.S.
I am using NH 2.1
You may use the criteria to select the IDs of your elements, join them in a string and use HQL to delete them?
Something like:
public void Delete(ICriteria criteria, string keyName, string tableName)
{
criteria.setProjection(Projections.Attribute(keyName));
IList<int> itemIds = criteria.List<int>();
string collection = string.Join(",", Array.ConvertAll<int, string>(itemIds, Convert.ToString));
Session.HQL(string.Format("delete from {0} where {1} in ({2})", tableName, keyName, collection);
}
This code was not tested or compiled (in particular I'm not sure of the HQL section), but I think that you got the idea: we don't fetch the whole objects thanks to the projection, but only the indices.
Simply put, up until 2.1.2 you cannot.
However, if you can translate the LINQ expression to HQL (or the ICriteria to HQL) then you can use the overloaded ISession.Delete() method which uses a passed HQL string.
In your repository/dao/persistencemanager/whatever class:
public IEnumerable<T> FindAll(DetachedCriteria criteria)
{
return criteria.GetExecutableCriteria(Session).List<T>();
}
and then
public void Delete(DetachedCriteria criteria)
{
foreach (T entity in FindAll(criteria))
{
Delete(entity);
}
}
See Davy Brion's post Data Access with NHibernate.
Edit:
As far as I know, if you want to use Criteria you need to load the objects and iterate over them to delete them. Alternatively use HQL or pass in the SQL to the session.
I know this is an old question but for argument sake; if one uses repository pattern you can declare a delete method which does the following:
public void Delete(System.Linq.Expressions.Expression<System.Func<TEntity, bool>> predicate)
{
var entities = _session.Query<TEntity>().Where(predicate);
foreach (var entity in entities)
_session.Delete(entity);
}
Note the code is using expressions in order for repository interface to be generic enough so you can also implement a for example Entity Framework repository.