My goal is to extract a specific record that has a parameter value specified by me. The data is taken from an external API.
My query looks like this:
var productId = productsResponse.Where(x => x.Parameters.Any(y => y.Values.Any(z => z.Equals(auctionTitle)))).FirstOrDefault();
And it works fine until where filters out all the records. Then the method aborts and debugging cannot continue.
The problem is:
System.ArgumentNullException: Value cannot be null
because source transferred to FirstOrDefault is null.
I also tried:
var productId = productsResponse.Where(x => x.Parameters.Any(y => y.Values.Any(z => z.Equals(auctionTitle)))).DefaultIfEmpty().First();
Please let me know what topic I should read because I have run out of ideas. I really care to understand where I am wrong.
This can be not an answer but try this construction:
var productId = productsResponse
.Where(x => x.Parameters.SelectMany(y => y.Values)
.Any(z => z == auctionTitle))
.FirstOrDefault();
Also if data came from external API, it may be needed more null check.
var productId = productsResponse
.Where(x => x.Parameters?.SelectMany(y => y.Values ?? Enumerable.Empty<Value>())
?.Any(z => z == auctionTitle) == true)
.FirstOrDefault();
Related
I have an App made with Xamarin.Forms and ReactiveUI.
Picture a View in this App where you have a kind of dropdown (actually a button that push another view where the user can filter and select one option) and when this "dropdown" is changed, I need to reload a list based on its value.
This "dropdown" won't start with some value, I need to make a async request, grab a value and then update the view.
The problem is, when I'm creating the command to load the documents:
LoadAllDocuments = ReactiveCommand.CreateFromTask<string, IEnumerable<Document>>(_ => m_service.GetAllDocumentsByTodoListAsync(SelectedTodoList.Id), canLoadAll, m_scheduler);
I need the Id from the SelectedToDoList, but this will be null at this point.
There's any way to delay this first execution of a command? Or maybe there's a better workflow to solve this?
Here is a sniped on how I'm doing it right now. Let me know if more information is needed.
LoadAllDocuments = ReactiveCommand.CreateFromTask<string, IEnumerable<Document>>(_ => m_service.GetAllDocumentsByTodoListAsync(SelectedTodoList.Id), canLoadAll, m_scheduler);
ChangeToDoListCommand = ReactiveCommand.CreateFromTask<DocumentListViewModel, bool>(vm => this.PushPageFromCacheAsync<ToDoListViewModel>((model) => model.ParentViewModel = this));
this.WhenActivated((CompositeDisposable disposables) =>
{
SelectedItem = null;
var SelectedTodoListChanged =
this
.WhenAnyValue(x => x.SelectedTodoList)
.Throttle(TimeSpan.FromSeconds(1), RxApp.MainThreadScheduler)
.Publish();
SelectedTodoListChanged
.Where(x => x == null)
.Subscribe(async _ => SelectedTodoList = await viewService.GetMyToDoListByVaultAsync(RuntimeContext.Current.VaultId))
.DisposeWith(disposables);
SelectedTodoListChanged
.Where(x => x != null)
.InvokeCommand(LoadAllDocuments)
.DisposeWith(disposables);
SelectedTodoListChanged.Connect();
LoadAllDocuments
.ObserveOn(m_scheduler)
.SubscribeOn(m_scheduler)
.Subscribe(list => AddViewsToList(list.ToList()))
.DisposeWith(disposables);
If I understand your question correctly, you need to ensure Id is not null before calling InvokeCommand:
SelectedTodoListChanged
.Where(x => x?.Id != null)
.InvokeCommand(LoadAllDocuments)
.DisposeWith(disposables);
Perhaps a better option is to bake this knowledge into the command itself. Since InvokeCommand respects the execution window of the command (as of RxUI 7, that is), if your command's CanExecute is currently false then InvokeCommand will not actually invoke your command:
var canLoadAllDocuments = this
.WhenAnyValue(x => x.SelectedTodoList?.Id)
.Select(id => id != null);
LoadAllDocuments = ReactiveCommand.CreateFromTask<string, IEnumerable<Document>>(
_ => m_service.GetAllDocumentsByTodoListAsync(SelectedTodoList.Id), canLoadAll,
canLoadAllDocuments,
m_scheduler);
Now you can do this:
SelectedTodoListChanged
.InvokeCommand(LoadAllDocuments)
.DisposeWith(disposables);
I have some simple code that retrieves recorded ELMAH exceptions from a db:
HealthMonitoringEntities context = new HealthMonitoringEntities();
IQueryable<ELMAH_Error> exceptions = context.ELMAH_Error;
if (filter.ToDate != null)
exceptions = exceptions.Where(e => e.TimeUtc <= filter.ToDate.Value.AddHours(-4));
return exceptions.OrderByDescending(e => e.TimeUtc)
.Take(filter.Size)
.AsEnumerable()
.Select(e => new ElmahException()
{
ErrorId = e.ErrorId,
Application = e.Application,
Host = e.Host,
Type = e.Type,
Source = e.Source,
Error = e.Message,
User = e.User,
Code = e.StatusCode,
TimeStamp = e.TimeUtc.AddHours(-4).ToString()
}).ToList();
}
I get an exception on this line:
TimeStamp = e.TimeUtc.AddHours(-4).ToString()
The exception is:
LINQ to Entities does not recognize the method 'System.DateTime AddHours(Double)' method, and this method cannot be translated into a store expression.
When I call .AsEnumerable() before projecting with Select(), my sequence is enumerated and I project from a sequence that implements IEnumerable<ELMAH_Error>. Given that, why am I not working with the Linq-To-Objects API in my projection, which understands AddHours(), instead of still working with the Linq-To-Entities API?
UPDATE
There is a post on this topic by Jon Skeet here:
http://msmvps.com/blogs/jon_skeet/archive/2011/01/14/reimplementing-linq-to-objects-part-36-asenumerable.aspx
He has this query:
var query = db.Context
.Customers
.Where(c => some filter for SQL)
.OrderBy(c => some ordering for SQL)
.Select(c => some projection for SQL)
.AsEnumerable() // Switch to "in-process" for rest of query
.Where(c => some extra LINQ to Objects filtering)
.Select(c => some extra LINQ to Objects projection);
Note that after his call to AsEnumerable(), he indicated he is switching over to Linq-To-Objects. I am doing something similar in my function, but I am receiving a Linq-To-Entities exception where I had thought I would be executing against the Linq-To-Objects API.
Further Update
From Jim Wooley's blog: http://linqinaction.net/blogs/jwooley/archive/2009/01/21/linq-supported-data-types-and-functions.aspx
"As an example the following methods are shown as having translations for DateTime values: Add, Equals, CompareTo, Date, Day, Month, Year. In contrast methods like ToShortDateString, IsLeapYear, ToUniversalTime are not supported.
If you need to use one of the unsupported methods, you need to force the results to the client and evaulate them using LINQ to Objects at that point. You can do that using the .AsEnumerable extension method at any point in the query comprehension."
Is that not what I'm doing?
You have forgotten to protect your first call
if (filter.ToDate != null)
exceptions = exceptions.AsEnumerable()
.Where(e => e.TimeUtc <= filter.ToDate.Value.AddHours(-4));
Edit
Remember, IQueryables are not translated to sql until they are enumerated, so your debugger will execute that line but the error won't occur until you return. If filter.ToDate is null your code is equivalent to this:
if(filter.ToDate == null)
return exceptions
.Where(e => e.TimeUtc <= filter.ToDate
.Value
.AddHours(-4)) //uh-oh won't work in Linq-to-Entities
.OrderByDescending(e => e.TimeUtc)
.Take(filter.Size)
.AsEnumerable() //too late to protect you!
.Select(e => new ElmahException()
{
ErrorId = e.ErrorId,
Application = e.Application,
Host = e.Host,
Type = e.Type,
Source = e.Source,
Error = e.Message,
User = e.User,
Code = e.StatusCode,
TimeStamp = e.TimeUtc.AddHours(-4).ToString() //if we get this far we're OK
}).ToList();
I have a asp.net mvc application with NHibernate and I do not know how to resolve a problem to query some data. I have this query:
// create query
var query = session.QueryOVer<Laudo>().Fetch(x => x.Equipament).Eager;
// add some filters
if (idEquipament.HasValue)
query = query.And(x => x.Equipament.Id == idEquipament.Value);
//I got the error here...
if (idCompany.HasValue)
query = query.And(x => x.Equipament.Company.Id == idCompany.Value);
When I try to execute this query, I've got an exception with this message:
"could not resolve property: Equipament.Company.Id of: DomainModel.Laudo"
what can I do to fix this problem?
Thanks
You cannot use another entity property like that. NHibernate expects expression that can be evaluated to property of the current entity. You need to use JoinQueryOver or JoinAlias to join another entity, and perform where after that.
With JoinQueryOver:
// ...
query = query.JoinQueryOver(x => x.Equipment)
.JoinQueryOver(x => x.Company)
.Where(c => c.Id == idCompany.Value);
With JoinAlias:
Equipment equipment = null;
Company company = null;
// ...
query = query.JoinAlias(x => x.Equipment, () => equipment)
.JoinAlias(() => equipment.Company, () => company)
.Where(() => company.Id == idCompany.Value);
Some more info:
What is the difference between JoinQueryOver and JoinAlias?
What can be used as a NHibernate QueryOver alias?
Complex nHibernate QueryOver expression
The tags chosen for your question make me think you didn't want to use QueryOver, but LINQ.
This is achieved by using the extension method Query, in the NHibernate.Linq namespace:
var query = session.Query<Laudo>().Fetch(x => x.Equipament);
if (idEquipament.HasValue)
query = query.Where(x => x.Equipament.Id == idEquipament.Value);
if (idCompany.HasValue)
query = query.Where(x => x.Equipament.Company.Id == idCompany.Value);
I have the following LINQ query which uses NHibernate against a SQL Server backed repository...
var casesByCaseOwner = this.preGrantDetailRepository.All
.Where(x => x.CaseFileLocation.Id == cflId)
.GroupBy(x => x.CaseOwner)
.Select(x => new StagSummaryForCfItem
{
Id = x.Key.Id,
Description = x.Key.Name,
NumberOfCases = x.Count(),
UninvoicedNetFee = x.Sum(y => y.UninvoicedNetFee),
UninvoicedDisbursement = x.Sum(y => y.UninvoicedDisbursement)
}).AsEnumerable();
However, it complains that SQL Server is unable to group by the CaseOwner.Name column because it is not contained in the select list or group clause. Coming from a database world I understand that error, however, I'm not sure how to force NHibernate to group by both Id and Name but still have the CaseOwner entity available to me in my Select.
I found the answer finally...
var casesByCaseOwner = this.preGrantDetailRepository.All
.Where(x => x.CaseFileLocation.Id == cflId)
.GroupBy(x => new { x.CaseOwner.Id, x.CaseOwner.Name })
.Select(x => new StagSummaryForCfItem
{
Id = x.Key.Id,
Description = x.Key.Name,
NumberOfCases = x.Count(),
UninvoicedNetFee = x.Sum(y => y.UninvoicedNetFee),
UninvoicedDisbursement = x.Sum(y => y.UninvoicedDisbursement)
}).AsEnumerable();
return casesByCaseOwner;
This works nicley, it turns out I need to project a new entity with the properties I want to group on.
I have some Setting entities that are related to a SettingDescription which is related to a SettingGroup.
Setting history is preserved by making a "Modified" field part of the key.
To get the settings matching a specific category I use this query (after help from here):
var latestSettings =
context.Settings.Include("Description.SettingGroup")
.OrderByDescending(x => x.Modified)
.GroupBy(x =>
new {
x.Category,
x.Group,
x.Name,
x.Target }, x => x)
.Where(x => x.Key.Category == category)
.Select(result => result.FirstOrDefault())
.ToArray();
This returns a set of the latest settings, but the "Include" part is completely ignored. However, I can force load the descriptions by running a second dummy query that loads the descriptions into the context.
var latestSettings =
context.Settings.Include("Description.SettingGroup")
.OrderByDescending(x => x.Modified)
.GroupBy(x =>
new {
x.Category,
x.Group,
x.Name,
x.Target }, x => x)
.Where(x => x.Key.Category == category)
.Select(result => result.FirstOrDefault())
.ToArray();
var settingDescriptions =
context.SettingDescriptions.Include("SettingGroup")
.Where(x => x.Category == category)
.ToArray();
Why is the include ignored in the "stand alone" group query?
Can I combine the setting and description loading into a single query?
AlexJ from the EF team posted an excellent series of tips, including:
"Tip 22 - How to make Include really Include"
http://blogs.msdn.com/b/alexj/archive/2009/06/02/tip-22-how-to-make-include-really-include.aspx
It looks to me like your query is returning "Settings" entities (without a "change of shape") so this tip should apply.