Retrieve single element from LINQ query - linq

Working with LINQ for the first time in a while and trying to clean something up. I have the following statements:
var element = await _Entities.References
.Where(db => db.LoadId == request.LoadId && db.ReferenceCode == "123")
.OrderByDescending(rec => rec.Created).FirstOrDefaultAsync(cancellationToken);
if (element != null) {
dto.ElementValue = element.Value;
}
I'd like to condense this into a single statement if possible but I was having trouble getting just the value from the await method.

You could do something like this:
dto.ElementValue = (await _Entity.References
.Where(db => db.LoadId == request.LoadId && db.ReferenceCode == "123")
.OrderByDescending(rec => rec.Created)
.FirstOrDefaultAsync(cancellationToken))?.Value
?? dto.ElementValue;
Note that technically this changes the behaviour of the code. Previously, if the query doesn't return a result, the ElementValue property is not touched. With a one-liner, if the query doesn't return a result, the ElementValue getter and setter will both be called.
Also, if the query returns a result whose Value is null, the ElementValue property will be set to itself rather than null.

Related

Get Attribute Value by LINQ

The HTML Source is as follows
<img id="itemImage" src="https://www.xyz.com/item1.jpg">
I am using the following LINQ query to get the SRC value (Image Link)
string imageURL = document.DocumentNode.Descendants("img")
.Where(node => node.Attributes["id"] != null && node.Attributes["id"].Value == "itemImage")
.Select(node => node.Attributes["src"].Value).ToString();
But the imageURL gives output as
System.Linq.Enumerable+WhereSelectEnumerableIterator`2[HtmlAgilityPack.HtmlNode,System.String]
The problem is casting it to string. Select() returns IEnumerable<T> so you are basically converting an enumerator to a string (as the error message says). Call First() or Single() or Take(1) in order to get a single element before casting it to a string.
.Select(node => node.Attributes["src"].Value).First().ToString();
Also, if there is a chance that the desired element is not present, FirstOrDefault() and SingleOrDefault() returns null rather then throwing an exception. In that case, I would recommend
var imageUlr = ... .Select(node => node.Attributes["src"].Value).FirstOrDefault();
if (imageUrl != null)
{
// cast it to string and do something with it
}
Add .DefaultIfEmpty(string.Empty)
.FirstOrDefault
string imageURL = document.DocumentNode.Descendants("img")
.Where(node => node.Attributes["id"] != null && node.Attributes["id"].Value == "itemImage")
.Select(node => node.Attributes["src"].Value)
.DefaultIfEmpty(string.Empty)
.FirstOrDefault()
.ToString();
Try adding FirstOrDefault():
string imageURL = document.DocumentNode.Descendants("img")
.Where(node => node.Attributes["id"] != null && node.Attributes["id"].Value == "itemImage")
.Select(node => node.Attributes["src"].Value)
.FirstOrDefault();

Enumeration yielded no result?

I am trying to filter some objects using linq to enitites and I get an error telling me "Enumeration yielded no results".
on the client side I get a message like this:
The operation cannot be completed because the DbContext has been
disposed
I know that these filter values should return some results but it just doesnt work, so Im guessing my query is wrong, can you help please.
var mediaChannels =
NeptuneUnitOfWork.MediaChannels
.FindWhere(m => m.CountryID == CountryID &&
m.SonarMediaTypeID == MediaTypeID &&
m.SonarMediaTypes.SonarMediaGroupID == MediaGroupID &&
m.Name.Contains(search))
.Select(m => new MediaChannelModel() {
ID = m.ID,
Name = m.Name,
MediaType = m.MediaType.Name,
Country = m.Countries.Name,
SubRegion = m.Countries.Lookup_SubRegions.Name,
Region = m.Countries.Lookup_SubRegions.Lookup_Regions.Name
});
My guess is that this runs just fine, then you dispose you context, then you try to access mediaChannels. The problem is that Linq uses deferred execution. Therefore, you query doesn't really execute until you enumerate mediaChannels, which is after you context is disposed.
If you don't want to use deferred execution, then add a .ToList() to the end of your query to force it to load right there.
If you want to use deferred execution, then you can't dispose of your context until a later point.
The operation cannot be completed because the DbContext has been disposed is often seen if you send data to the client without saving the data to memory. This can be easily fixed by .ToList()-ing your query before sending it to the page
var mediaChannels = NeptuneUnitOfWork.MediaChannels
.Where(m => m.CountryID == CountryID
&& m.SonarMediaTypeID == MediaTypeID &&
&& m.SonarMediaTypes.SonarMediaGroupID == MediaGroupID
&& m.Name.Contains(search))
.Select(m => new MediaChannelModel() {
ID = m.ID,
Name = m.Name,
MediaType = m.MediaType.Name,
Country = m.Countries.Name,
SubRegion = m.Countries.Lookup_SubRegions.Name,
Region = m.Countries.Lookup_SubRegions.Lookup_Regions.Name
}).ToList(); // <<-- NOTE this additional method

Unable to cast object of type WhereSelectListIterator 2 System.Collections.Generic.List

I am working on these lists to get an item that matches the selected item from the combobox.
private void InitializaMessageElement()
{
if (_selectedTransactionWsName != null)
{
get a transaction webservice name matching the selected item from the drop down here the output=TestWS which is correct
var getTranTypeWsName = TransactionTypeVModel
.GetAllTransactionTypes()
.FirstOrDefault(transTypes =>
transTypes.WsMethodName == _selectedTransactionWsName);
Loop the list of wsnames from the treenode list. Here it gives me all the node I have which is correct.
var wsNameList = MessageElementVModel
.GetAllTreeNodes().Select(ame =>
ame.Children).ToList();//. == getTranTypeWsName.WsMethodName);
find the getTranTypeWsName.WsMethodName in the wsNameList. Here is where I have the problem:
var msgElementList = wsNameList.Select(x => x.Where(ame => getTranTypeWsName != null && ame.Name == getTranTypeWsName.WsMethodName)).ToList();
my MsgElement list:
MsgElementObsList = new ObservableCollection<MessageElementViewModel>(msgElementList);
this.messageElements = _msgElementList;
NotifyPropertyChanged("MessageElements");
}
Here it is throwing the cast error. why is it not working? I am new to LINQ. thanks
As the error is trying to tell you, LINQ methods return special iterator types the implement IEnumerable<T>; they do not return List<T>.
This enables deferred execution.
Since the object isn't actually a List<T>, you can't cast it to a type that it isn't.
If you need a List<T>, you can either call ToList(), or skip LINQ entirely and use List<T>.ConvertAll(), which is like Select(), but does return List<T>.
Modify
MsgElementObsList = new ObservableCollection<MessageElementViewModel>((List<MessageElementViewModel>) msgElementList);
to
MsgElementObsList = new ObservableCollection<MessageElementViewModel>(msgElementList);
This is because although all lists are enumerable, all enumerables are not lists, and this one happens not to be one.
Also, your bool error has to do with returning true in the select. Here's the fixed code for that:
var msgElementList = wsNameList.Select(x =>
x.Where(ame => ame.Name == getTranTypeWsName.WsMethodName));

Determine the root object of a MemberExpression in a LINQ Expression Tree

I'm currently working in a project that involves the encryption of a few columns in an existing database. There is quite a lot of code already written against the current schema, a lot of which is in the form of custom linq-to-sql queries. The number of queries is in the neighbourhood of a 5 figure number, so modifying and re-testing each and everyone of them would be way too expensive.
An alternative we found is to keep the DB schema the same --only altering the columns length slightly, which mean we don't need to change our current entity class definitions-- and instead, changing the expression trees on-the-fly, before they reach the l2sql IQueryProvider, and apply a decryption function on the columns I need. I do this by wrapping the pertinent Table<TEntity> properties of my DataContext with a custom IQueryable<TEntity> implementation, which allows me to preview every single query in the system.
In my current implementation, say I've got this query:
var mydate = new DateTime(2013, 1, 1);
var context = new DataContextFactory.GetClientsContext();
Expression<Func<string>> foo = context.MyClients.First(
c => c.BirthDay < mydate).EncryptedColumn;
but when I catch the query, I change it to read:
Expression<Func<string>> foo = context.Decrypt(
context.MyClients.First(c => c.BirthDay < mydate).EncryptedColumn);
I do this using the ExpressionVisitor class. In the VisitMember method, I check and see whether the current MemberExpression refers to an encrypted column. If it does, I substitute the expression for a method call:
private const string FuncName = "Decrypt";
protected override Expression VisitMember(MemberExpression ma)
{
if (datactx != null && IsEncryptedColumnReference(ma))
return MakeCallExpression(ma);
}
return base.VisitMember(ma);
}
private static bool IsEncryptedColumnReference(MemberExpression ma)
{
return ma.Member.Name == "EncryptedColumn"
&& ma.Member.DeclaringType == typeof(MyClient);
}
private Expression MakeCallExpression(MemberExpression ma)
{
const BindingFlags flags = BindingFlags.Instance | BindingFlags.Public;
var mi = typeof(MyDataContext).GetMethod(FuncName, flags);
return Expression.Call(datactx, mi, ma);
}
datactx is an instance variable with a reference to the expression pointing at the current datacontext (which I look up in a previous pass).
My problem is that if I have a query such as:
var qbeClient = new MyClient { EncryptedColumn = "FooBar" };
Expression<Func<MyClient>> dbquery = () => context.MyClients.First(
c => c.EncryptedColumn == qbeClient.EncryptedColumn);
I want it to be turned into:
Expression<Func<MyClient>> dbquery = () => context.MyClients.First(c =>
context.Decrypt(c.EncryptedColumn) == qbeClient.EncryptedColumn);
instead, what I'm getting is this:
Expression<Func<MyClient>> dbquery = () => context.MyClients.First(c =>
context.Decrypt(c.EncryptedColumn) == context.Decrypt(qbeClient.EncryptedColumn));
Which I don't want, because when I've got an in-memory object, the data is already unencrypted (besides, I don't want a nasty db function call against my objects!)
So, that's basically my question: Having a MemberExpression instance, how can I determine whether it refers to an in-memory object or a row in the database?
Thanks in advance
Edit:
#Shlomo's code actually solves the case I posted, but now one of my previous tests got broken:
var context = new DataContextFactory.GetClientsContext();
Expression<Func<string>> expr = context.MyClients.First().EncryptedColumn;
Expression<Func<string>> expected = context.Decrypt(
context.MyClients.First().EncryptedColumn);
var actual = MyVisitor.Visit(expr);
Assert.AreEqual(expected.ToString(), actual.ToString());
In this case, the reference to EncryptedColumn isn't a parameter, but it should definitely be taken into account by the visitor!
A MemberExpression representing a DB row will be a descendent of a ParameterExpression. In-Memory objects will not, they'll most likely come from some form of a FieldExpression.
In your case, something like this will work for most cases (adding one method to your code, and revising your VisitMember method:
private bool IsFromParameter(MemberExpression ma)
{
if(ma.Expression.NodeType == ExpressionType.Parameter)
return true;
if(ma.Expression is MemberExpression)
return IsFromParameter(ma.Expression as MemberExpression);
return false;
}
protected override Expression VisitMember(MemberExpression ma)
{
if (datactx != null && IsEncryptedColumnReference(ma) && IsFromParameter(ma))
return MakeCallExpression(ma);
}
return base.VisitMember(ma);
}

issue using Linq Any clause in Predicatebuilder

I am having an issue with the LinqKit predicatebuilder. I have used it in the past for simple queries and it has worked fine but I am now trying to use it with an Any clause in the statement and it seems to be giving me random results. Below is the code I am using to build the statement. Can anyone see what I am doing wrong? Is there a better and easier way to do what I want to do. I am using predicatebuilder right now because there is a very complex query I am building which could contain nested predicates and such and I have seen no other easy way to do this. I am using this with entity framework.
if (cqv.ComplexQuery.IncludeOnAll)
{
Includepredicate = PredicateBuilder.True<Customer>();
}
else
{
Includepredicate = PredicateBuilder.False<Customer>();
}
inner = PredicateBuilder.True<Customer>();
if (a.Include == true || a.Exclude == true)
{
productinner = PredicateBuilder.True<CustomerProduct>();
if (a.VersiondID != 0)
{
productinner = productinner.And(o => o.ProductTypeID == a.ProductType.ID && o.VersionID == a.VersiondID);
inner = inner.And(o => o.Products.Any(productinner.Compile()));
}
else
{
productinner = productinner.And(o => o.ProductTypeID == a.ProductType.ID);
inner = inner.And(o => o.Products.Any(productinner.Compile()));
}
if (cqv.ComplexQuery.IncludeOnAll)
{
Includepredicate = Includepredicate.And(inner.Expand());
}
else
{
Includepredicate = Includepredicate.Or(inner.Expand());
}
}
IncludedCustomers = UoW.Customers.AsExpandable().Where(Includepredicate).ToList();
//This second list does the exact query the first one should be doing so I could compare results. The reuslts are totally different. Not only are there more results using predicatebuilder but they seem random
List<Customer> test = UoW.Customers.Where(o => o.Products.Any(s => s.ProductTypeID == 1)).ToList();
I don't see any easy way to try to debug issue with predicate builder either. Does anyone know of a quick way to determine the SQL that gets created from this query?
EDIT ----------------------------------------------
So I have solved part of my issue but run into another one. the issue with the Any clause and random results was fixed by me setting in integer variable with the a.ProductType.ID and using that value in the clause. Once I did that I got the results I expected. Now my issue is that even though this works fine when 1 product is select if I select naymore than 1 instead of either looking for any customers that have both of these products or either of these products the results I gt is always just the customer with the last product I put a clause in for. I will put my updated code below
foreach (CustomerProductQueryProduct a in cqv.ComplexQuery.Products)
{
inner = PredicateBuilder.True<Customer>();
if (a.Include == true || a.Exclude == true)
{
value = a.ProductType.ID;
productinner = PredicateBuilder.True<CustomerProduct>();
if (a.VersiondID != 0)
{
productinner = productinner.And(s => s.ProductTypeID == value && s.VersionID == a.VersiondID);
inner = inner.And(o => o.Products.Any(productinner.Compile()));
}
else
{
productinner = productinner.And(s => s.ProductTypeID == value);
inner = inner.And(o => o.Products.Any(productinner.Compile()));
}
if (cqv.ComplexQuery.IncludeOnAll)
{
Includepredicate = Includepredicate.And(inner.Expand());
}
else
{
Includepredicate = Includepredicate.Or(inner.Expand());
}
}
}
IncludedCustomers = UoW.Customers.AsExpandable().Where(Includepredicate).ToList();
Is PredicateBuilder not able to handle multiple Any clauses?
I finally figured out that I need to make temporary variable inside of my for loop to hold the value. When you do that it somehow knows to resolve the value immediately and the predicates work.

Resources