linq not returning any data - c#

I have two variables. first variable docList of type www.abc.com.docListResponse. It consists of an array of values with docID[0] = "12345", docID[1] = "34567" docID[2] = "32498"
The second variable is of custom type Documents[] docsColl with values docRefID[0] = "12345", docRefID[1] = "34567", DocRefID[2] = "67890"
The ling operator
var docs = docsColl.ToList().Where(i => i.docRefID== docList .FirstOrDefault().docID.ToString()).ToArray();
I'm expecting it to return two records but its returning 0.

If you need to compose a query using another existing collection, you can generally use the Any() method to accomplish this, which will return any records that match your specific constraint.
var docs = docsColl.Where(d => docList.Any(x => d.docID == x.docRefID));
One caveat with this is that if you are using complex types and your initial collection is not in memory (and is in something like a database), LINQ won't know how to properly handle this, so you typically need to either throw a ToList() call prior to your Where() statement :
var docs = docsColl.ToList()
.Where(d => docList.Any(x => d.docID == x.docRefID));
Or you'll need to ensure that the collection you are using to query through (in this case docList) only consists of primitive types like strings :
// Get your IDs to check against
var docRefIds = docList.Select(d => d.docRefID).ToArray();
var docs = docsColl.Where(d => docRefIds.Any(r => d.docID == r));

Related

How to get data from linq query using select

I'm testing a simple LINQ select query and want to get two DateTime values from the table, but I'm doing something wrong here and need to know what I'm doing/thinking wrong?
My query:
var test = from x in db.Projects where x.ID == 1 select x;
Then I try to get on of the values like this:
DateTime Date = test. ?????
Here I thought I should get a suggestion from the Intellisense after the dot to pick the value from the column StartDate the table, but this isn't working.
If you need multiple matches...
Are you sure that you have multiple Project objects that have the same ID of 1 which your query currently suggests? If that is the case, then your query should return all of the records that meet that constraint via the Where() method :
// Get your Projects that meet your criteria
var test = db.Projects.Where(p => p.ID == 1);
If you need to access properties from these elements, you could either loop through them explicitly :
// Iterate through each match that was found
foreach(var t in test)
{
// Access your properties here
DateTime dt = t.YourDateProperty;
}
Or you could accomplish this using a Select() statement to only pull the properties that you need :
// This will return a collection of Dates mapped from each element in your collection
var testDates = db.Projects.Where(p => p.ID == 1)
.Select(x => x.YourDateProperty);
If you only need a single match...
If you only need to match a single element within your collection, you might consider using the First(), Single() or their equivalent FirstOrDefault() and SingleOrDefault() methods, which will return a single entity that you can use as expected :
// This will return the first Project with an ID of 1
var test = db.Project.FirstOrDefault(p => p.ID == 1);
// If it was successful
if(test != null)
{
// Then you can safely access it here
DateTime dt = test.YourDateProperty;
}
The only difference between the methods mentioned (normal vs OrDefault()) is that the OrDefault() methods will return null if no matching elements are found, so they generally require a null check as seen above.
test is going to be an enumeration (IEnumerable<>, IQueryable<>, etc... many are applicable) of your Project type. So if, for example, you want the first record, you might do this:
DateTime Date = test.First().SomeDateProperty;
All of the data returned from your query is in test. It could be zero records, one record, many records, etc.
In test you will have a collection which matches the condition x.ID == 1. You should iterate through that collection and take your needed properties.
Edit
I suggest you to use the syntax:
var result = db.Projects.FirstOrDefault(x => x.ID ==1);
this is such as:
var date = test.FirstOrDefault();
DateTime? Date = date != null ? date.StartDate : null;

LINQ Query comparing Id with an Id in a list inside SingleOrDefault query

I am trying to fetch an option using the SingleOrDefault Linq to SQL method.
var po = repository.Context.AsQueryable<Option>().SingleOrDefault(o => o.Option.Id == sp.Options // sp.Options is a collection);
The problem is that inside the SingleOrDefault method I am comparing p.Option.Id == a collection. What I want is to select the option from sp.Options that matches the o.Option.Id. How can I do that?
UPDATE:
One thing I should have mentioned that the sp.Options is a different class than the Option class. sp.Options is SPOptions class so I cannot pass it inside the contains method.
Take a look at Contains.
repository.Context.AsQueryable<Option>().SingleOrDefault(o => sp.Options.Contains(o.Option.Id));
If Options is not a collection of the class of Option.Id, you can use the Any method with your comparison logic in it as follow :
repository.Context.AsQueryable<Option>().SingleOrDefault(o => sp.Options.Any(opts => opts.Something == o.Option.Id));
Search using Contains (sp.Options.Contains(o.Option.Id)) like:
var po = repository.Context.AsQueryable<Option>()
.SingleOrDefault(o => sp.Options.Contains(o.Option.Id));
If members of sp.Options are different from Id then you can do:
var po = repository.Context.AsQueryable<Option>()
.SingleOrDefault(o => sp.Options.Any(r=> r.Id == o.Option.Id));
or
var po = repository.Context.AsQueryable<Option>()
.SingleOrDefault(o => sp.Options.Select(r=> r.Id).Contains(o.Option.Id));
Assuming Id is the field in sp.Options elements that you want to compare with.
Based on your question it seems you're expecting to have a single match between those two option sets, correct ?
If so, I'd suggest you to write it as:
var po = repository.Context.AsQueryable().Where(o => sp.Options.Any(item=>item.id == o.Option.Id)).SingleOrDefault();

Select from IEnumerable using LINQ

I have a method that returns an object from cache which I use to populate selectlists within the system.
var r = CacheHelper.GetCacheItem(...);
return new SelectList((IEnumerable)r, "Id", "Name");
r is of type System.Collections.Generic.List<<>f__AnonymousType39<string,int,string>>
By using IEnumerable enumerable = (IEnumerable)r; I see that
enumerable looks like this:
[0]: { Name = "Lost", Id = 1, Area = null }
[1]: { Name = "Found", Id = 2, Area = null }
[2]: { Name = "Stock Adjustment", Id = 3, Area = null }
...
I would like to be able to use LINQ toquery the result set to return a subset, while keeping the full list in memory. Something like:
var s = enumerable.Where(x => x.Area == "myarea");
Answers to similar SO questions suggest using ToList(), but I can't call any LINQ methods on enumerable without getting a System.Collections.IEnumerable' does not contain a definition for 'ToList'.. error message. (I'm using ToList() elsewhere in the code so I have a valid reference to System.Linq.
Ideally, you should keep the type information all the way through the system - and potentially create a named type with the relevant properties. You could then cast back to IEnumerable<SomeConcreteType> later.
If you can't do that though, and assuming you're using C# 4 and .NET 4 or later, you could use dynamic typing:
IEnumerable<dynamic> enumerable = (IEnumerable<dynamic>) r;
...
var s = enumerable.Where(x => x.Area == "myarea");

Merging two iqueryables that are projected to one entity from different tables

I have tried This answer, This one and this one to merge two iqueryables. But I always receive the following error:
The type 'Estudio' appears in two structurally incompatible initializations within a single LINQ to Entities query. A type can be initialized in two places in the same query, but only if the same properties are set in both places and those properties are set in the same order.
I'm mapping from two different but similar Entity Framework Entities (EXAMEN and EXPLORACION) to my domain entity Estudio, with the following code.
IQueryable<Estudio> listExamen = context.Set<EXAMEN>().Project().To<Estudio>();
IQueryable<Estudio> listExploracion = context.Set<EXPLORACION>().Project().To<Estudio>();
var listCombined = listExamen.Concat(listExploracion);
Is there anyway of generate a IQueryable (not enumerable) with the merging of both list? If AsEnumerable() is used, then the following filters (Order, Take, etc) are executed on memory. So I need to merge the list but still be able to apply filter to the merged list wihtout execute the queries.
//This will force the next condition is executed on memory
var listCombined = listExamen.AsEnumerable().Concat(listExploracion);
Is that possible?
I would try to select your data into an anonymous type in your linq query, perform the union, and add your criteria.
var listExamen = context.Examen
.Select(x => new { x.Prop1, x.Prop2, ... }); // Add properties
var listExploracion = context.Exploraction
.Select(x => new { x.Prop1, x.Prop2, ... }); // Add identical properties
var listCombined = listExamen.Concat(listExploracion);
var whereAdded = listCombines
.Where(x => x.Prop1 == someValue);
var result = whereAdded
.Skip(skipCount)
.Take(takeCount)
.ToList();
Note: I have no idea if you can use Common Table Expressions (the SQL necessity for skip/take) in combination with a Union-query
Note: I've changed the methods used to create the expressions, since I do not know your methods (Project, To)
So I think the solution is not to cast to a specific type, but to an anonymous type, since that probably can be translated to SQL.
Warning: didn't test it
My solution was to revise my mapping code. Instead of using individual property-based mappers, I had to project the entire entity at once, making sure that all of the properties were given in the same order.
So, instead of the ForMember syntax:
Mapper.CreateMap<Client, PersonResult>()
.ForMember(p => p.Name, cfg => cfg.MapFrom(c => c.Person.FirstName + " " + c.Person.LastName))
...
I used the ProjectUsing syntax:
Mapper.CreateMap<Client, PersonResult>()
.ProjectUsing(c => new PersonResult()
{
Name = c.Person.FirstName + " " + c.Person.LastName
...
});
This must be because of the way AutoMapper constructs its projections.
One way to work around this is to add dummy types:
class Estudio<T> : Estudio { }
And new mapping:
Mapper.CreateMap<Estudio , Estudio>();
Mapper.CreateMap<EXAMEN , Estudio<EXAMEN>>();
Mapper.CreateMap<EXPLORACION, Estudio<EXPLORACION>>();
One caveat is that all fields in Estudio need some value in mapping.
You can't use ignore. Returning 0 or "" is fine.
Now we can do:
var a = context.Set<EXAMEN>().ProjectTo<Estudio<EXAMEN>>();
var b = context.Set<EXPLORACION>().ProjectTo<Estudio<EXPLORACION>>();
return a.ProjectTo<Estudio>().Concat(b.ProjectTo<Estudio>());

Linq To Entities Query

Consider the following Query :
var profilelst =
(
from i in dbContext.ProspectProfiles
where i.CreateId == currentUser
select new ProspectProfile
{
ProspectId = i.ProspectId,
Live = i.Live,
Name = i.Name,
ServiceETA = i.Opportunities.OrderByDescending(t => t.FollowUpDate)
.FirstOrDefault()
.ServiceETA.ToString(),
FollowUpDate = i.Opportunities.OrderByDescending(t => t.FollowUpDate)
.FirstOrDefault()
.FollowUpDate
}
)
.ToList();
return profilelst.OrderByDescending(c=>c.FollowUpDate)
.Skip(0).Take(endIndex)
.ToList();
Here in this query please take a look at FollowUpDate and ServiceType, these both i have fetched from Opportunity table, is there any other work around to get these both..
One to Many Relationship in tables is like: ProspectProfile -> Opportunities
Whether the query i have written is ok or is there any another work around that can be done in easier way.
The only thing you can improve is to avoid ordering twice by changing your code to this:
var profilelst
= dbContext.ProspectProfiles
.Where(i => i.CreateId == currentUser)
.Select(i =>
{
var opportunity
= i.Opportunities
.OrderByDescending(t => t.FollowUpDate)
.First();
return new ProspectProfile
{
ProspectId = i.ProspectId,
Live = i.Live,
Name = i.Name,
ServiceETA = opportunity.ServiceETA.ToString(),
FollowUpDate = opportunity.FollowUpDate
}
}).ToList();
return profilelst.OrderByDescending(c => c.FollowUpDate).Take(endIndex).ToList();
I made several changes to your original query:
I changed it to use method chains syntax. It is just so much easier to read in my opinion.
I removed the unnecessary Skip(0).
The biggest change is in the Select part:
I changed FirstOrDefault to First, because you are accessing the properties of the return value anyway. This will throw a descriptive exception if no opportunity exists. That's better than what you had: In your case it would throw a NullReferenceException. That's bad, NullReferenceExceptions always indicate a bug in your program and are not descriptive at all.
I moved the part that selects the opportunity out of the initializer, so we need to do the sorting only once instead of twice.
There are quite a few problems in your query:
You cannot project into an entity (select new ProspectProfile). LINQ to Entities only supports projections into anonymous types (select new) or other types which are not part of your entity data model (select new MySpecialType)
ToString() for a numeric or DateTime type is not supported in LINQ to Entities (ServiceETA.ToString())
FirstOrDefault().ServiceETA (or FollowUpdate) will throw an exception if the Opportunities collection is empty and ServiceETA is a non-nullable value type (such as DateTime) because EF cannot materialize any value into such a variable.
Using .ToList() after your first query will execute the query in the database and load the full result. Your later Take happens in memory on the full list, not in the database. (You effectively load the whole result list from the database into memory and then throw away all objects except the first you have Takeen.
To resolve all four problems you can try the following:
var profilelst = dbContext.ProspectProfiles
.Where(p => p.CreateId == currentUser)
.Select(p => new
{
ProspectId = p.ProspectId,
Live = p.Live,
Name = p.Name,
LastOpportunity = p.Opportunities
.OrderByDescending(o => o.FollowUpDate)
.Select(o => new
{
ServiceETA = o.ServiceETA,
FollowUpDate = o.FollowUpDate
})
.FirstOrDefault()
})
.OrderByDescending(x => x.LastOpportunity.FollowUpDate)
.Skip(startIndex) // can be removed if startIndex is 0
.Take(endIndex)
.ToList();
This will give you a list of anonymous objects. If you need the result in a list of your entity ProspectProfile you must copy the values after this query. Note that LastOpportunity can be null in the result if a ProspectProfile has no Opportunities.

Categories

Resources