LINQ Dynamic Expression API, predicate with DBNull.Value comparison - linq

I have an issue using the Dynamic Expression API. I cannot seem to compare a DataTable field against DBNull.Value. The API is supposed to be able to "support static field or static property access. Any public field or property can be accessed.". However given the following query:
var whatever = table1.AsEnumerable()
.Join(table2.AsEnumerable(),
(x) => x.Field<int>("Table1_ID"),
(y) => y.Field<int>("Table2_ID"),
(x, y) => new { x, y})
.AsQueryable()
.Where("x[\"NullableIntColumnName\"] == DBNull.Value");
I end up getting the error: "No property or field 'DBNull' exists in type '<>f__AnonymousType0`2'"
Anyone have ideas on how to get around this? I can't use Submission.Field("NullableIntColumnName") in the string passed to the Where method either, btw, or else I would be able to compare against null instead of DBNull.Value.

Well, I finally got it. cptScarlet almost had it.
var values = new object[] { DBNull.Value };
...
.Where("x[\"NullableIntColumnName\"] == #0", values);
or
.Where("x[\"NullableIntColumnName\"] == #0", DBNull.Value);

What happens when you replace your current .Where with something like
.Where(string.format("x[\"NullableIntColumnName\"] == {0}",DBNull.Value));

If you change x.Field<int>("Table1_ID") to x.Field<int?>("Table1_ID") then you'll get nullable integers instead of regular integers, and any DBNull values will be converted to simple C# null values. Based simply on your code snippet, I'm not even sure you'd need dynamic expressions - a simple .Where(foo => foo.x == null) ought to work.

In general, you can also try:
.Where("NullableColumnName.HasValue");

Sorry to non-answer with a USL but...
Have you looked in the source? There's not a lot of it. My guess is that DBNull is not in the list of registered root objects.
I dont have the source to hand right now, but it is also likely to tell you what any other constants one might compare against might be.

.Where(a => a.IntColName == null);
Edit:
Sorry, I did't see this dynamic requirement... Dynamic would be: (at least in Framework 4)
var intColName = "...";
.Where(string.Format("it.{0} is null", intColName));

Related

Linq where clause invalid

var advocacy = (from c in svcContext.CreateQuery("lead")
join a in svcContext.CreateQuery("product")
on c["transactioncurrencyid"] equals a["transactioncurrencyid"]
where a["capg_billingtimeframe"].Equals(126350000)
select new
{
dues = c["capg_calculatedduesbilling"],
product = c["capg_primaryproduct"],
listprice = a["price"],
eligibility = c.FormattedValues["capg_eligibility"]
});
That is my linq query and it is giving me the error: Invalid 'where' condition. An entity member is invoking an invalid property or method.
I have looked online everywhere and done their suggestions. I am not using Xrm.cs because late binding can be faster. I have tried using the == operand and I have tried doing (int) and Convert.ToInt32(a["capg_billingtimeframe"]) and even converting everything to a string. I will say that a["capg_billingtimeframe"] I know is an object (that's why I did those conversions.
I'm guessing by that integer that capg_billingtimeframe is an optionset. If it is, you need to cast it like this:
where ((OptionSetValue)a["capg_billingtimeframe"]).Value == 126350000
I used early bound and for getting the local I wrote:
OptionSetValue branch = this.InputTargetEntity.Attributes.Contains("capg_calculatorutilized") ? (OptionSetValue)this.InputTargetEntity["capg_calculatorutilized"] : (OptionSetValue)this.TargetPreImage["capg_calculatorutilized"];
I then had to get the Optionsets.cs using crmsvcutil and writing:
if (branch.Value == (int)capg_calculatorrequired.SectionA)
Works like a charm.

Access a collection via LINQ and set a single member to a new object

I am trying to access a user object in a collection with the id = to users101 and set this to another users.
Controller.MyObject.SingleOrDefault(x => x.Id == "user101") = OtherUser();
Thanks in advance.
You can't do it with one LINQ expression.
Usually LINQ extensions works on enumerables, if MyObject is a collection you first have to find the required item and then overwrite it with the new object (moreover SingleOrDefault() will simply return null if condition is not satisfied).
You should write something like this (exact code depends on what MyObject is):
var item = Controller.MyObject.SingleOrDefault(x => x.Id == "user101");
if (item != null)
Controller.MyObject[Controller.MyObject.IndexOf(item)] = new OtherUser();
Please note that if you do not really need the check performed by SingleOrDefault() you can simplify the code (and avoid the double search performed in SingleOrDefault() and IndexOf()).
If this is "performance critical" maybe it is better to write an ad-hoc implementation that does this task in one single pass.
Try it in two lines:
var objectWithId = Controller.MyObject.SingleOrDefault(x => x.Id == "user101");
(objectWithId as WhateverTypeOfObjectOtherUserIs) = OtherUser();

TargetInvocationException thrown when attempting FirstOrDefault on IEnumerable

I suspect I'm missing something rather basic, yet I can't figure this one out.
I'm running a simple linq query -
var result = from UserLine u in context.Users
where u.PartitionKey == provider.Value && u.RowKey == id.Value
select u;
UserLine user = null;
try
{
user = result.FirstOrDefault();
}
For some reason this produces a TargetInvocationException with an inner exception of NullReferenceException.
This happens when the linq query produces no results, but I was under the impression that FirstOrDefault would return Default<T> rather than throw an exception?
I don't know if it matters, but the UserLine class inherits from Microsoft.WindowsAzure.StorageClient.TableServiceEntity
there are two possible reasons:
provider.Value
id.Value
Are you sure that theese nullables have value. You might want to check HasValue before
var result = from UserLine u in context.Users
where (provider.HasValue && u.PartitionKey == provider.Value)
&& (id.HasValue && u.RowKey == id.Value)
select u;
UserLine user = null;
try
{
user = result.FirstOrDefault();
}
I thought it produced a different error, but based on the situation in which the problem is occurring you might want to look to check if context.IgnoreResourceNotFoundException is set to false? If it is try setting it to true.
This property is a flag to indicate whether you want the storage library to throw and error when you use both PartitionKey and RowKey in a query and no result is found (it makes sense when you think about what the underlying REST API is doing, but it's a little confusing when you're using LINQ)
I figured it out - the problem occured when either id or provider had '/' in the value, which the id did. when I removed it the code ran fine
Understanding the Table Service Data Model has a section on 'Characters Disallowed in Key Fields' -
The following characters are not allowed in values for the
PartitionKey and RowKey properties:
The forward slash (/) character
The backslash () character
The number sign (#) character
The question mark (?) character
Here's some fun try putting the where query the other way around like this to see if it works (I heard a while ago it does!):
where (id.HasValue && u.RowKey == id.Value) && (provider.HasValue && u.PartitionKey == provider.Value)
Other than this you can now set IgnoreResourceNotFoundException = true in the TableServiceContext to receive null when an entity is not found instead of the error.
It's a crazy Azure storage thing.

improving readiblity and be slightly less explicit using Linq

All,
I've got the following code, and looking into ways to improve its readibility (and remove the null check) by using Linq.
var activePlan = CurrentPlans.First();
var activeObjectives = activePlan != null ? activePlan.Objectives : null;
The closest I get is the following:
var activeObjectives = CurrentPlans.Take(1).Select(x => x.Objectives);
which gives me a collection of x.Objectives instead of Objectives.
Any ideas?
I'd write if like this:
var activeObjectives = CurrentPlans.Select(x => x.Objectives).FirstOrDefault();
This way, it's easier to work out the intention by the use of methods. Take the first set of objectives, otherwise the default (null assuming Objectives refers to a reference type). Using SelectMany() for this case isn't the best choice IMO.
oh got it:
var activeObjectives = CurrentPlans.Take(1).SelectMany(x => x.Objectives)
http://msdn.microsoft.com/en-us/library/system.linq.enumerable.selectmany.aspx

Invoke an Expression in a Select statement - LINQ to Entity Framework

I'm trying to use an already existing Expression building class that I made when trying to do a select clause, but I'm not sure how to attach the expression to the expression tree for the Select, I tried doing the following:
var catalogs = matchingCatalogs.Select(c => new
{
c.CatalogID,
Name = EntitiesExpressionHelper.MakeTranslationExpression<Catalog>("Name", ApplicationContext.Instance.CurrentLanguageID).Compile().Invoke(c),
CategoryName = EntitiesExpressionHelper.MakeTranslationExpression<Category>("Name", ApplicationContext.Instance.CurrentLanguageID).Compile().Invoke(c.Category),
c.CategoryID,
c.StartDateUTC,
c.EndDateUTC
});
But I obviously get the error stating that the Entity Framework can't map Invoke to a SQL method. Is there a way to work around this?
FYI, EntitiesExpressionHelper.MakeTranslationExpression<T>(string name, int languageID) is equivalent to:
x => x.Translations.Count(t => t.LanguageID == languageID) == 0 ? x.Translations.Count() > 0 ? x.Translations.FirstOrDefault().Name : "" : x.Translations.FirstOrDefault(t => t.LanguageID == languageID).Name
EDIT: I realize that I need to use an ExpressionVisitor to accomplish this, but I'm not sure how to use an ExpressionVisitor to alter the MemberInitExpression, so if anyone knows how to accomplish this, let me know.
You need to capture the expressions in vars. You won't be able to use anonymous types. The general idea is that this works:
Expression<Func<Foo, Bar>> exp = GenExpression();
var q = matchingCatalogs.Select(exp);
But this will not:
var q = matchingCatalogs.Select(GenExpression());
The first happily passes the result of GenExpression to L2E. The second tries to pass GenExpression itself to L2E, rather than the result.
So you need a reference to a var of the same type as the expression. Those can't be implicitly typed, so you'll need a real type for your result type.

Resources