Linq OrderedQueryAble Error - c#

First Thing's First I have a class to manipulate some data through a linq variable:
public class Result
{
public bool LongerThan10Seconds { get; set; }
public int Id { get; set; }
public DateTime CompletionTime { get; set; }
}
Then in a Separate class I'm using this to grab info from a linq var
using (var data = new ProjectEntities())
{
Result lastResult = null;
List<Result> dataResults = new List<Result>();
foreach(var subResult in data.Status.Select(x => x.ID).Distinct().Select(Id => data.Status.Where(x => x.ID == Id).OrderBy(x => x.Time)))
{
if (lastResult != null)
{
if (subResult.CompletionTime.Subtract(lastResult.CompletionTime).Seconds > 10)
dataResults.Add(subResult);
}
lastResult = subResult;
}
however I get the error:
Linq.IOrderedQueryAble does not contain a definition for 'CompletionTime' and no Extension method 'CompletionTime' accepting a first argument of type 'System.Linq.IOrderedQueryable.
Is anyone able to provide a solution to get around this would be much appreciated been trying to figure it out for a while but seems a bit difficult in terms of a DateTime Variable.

Your problem is that subResult holds an IOrderedQueryable (presumably an IOrderedQueryable<Result>) rather than a Result.
You have this in the foreach: var subResult in data.Status.Select(x => x.ID).Distinct().Select(Id => data.Status.Where(x => x.ID == Id).OrderBy(x => x.Time)). Notice what's inside the Select: Id => data.Status.Where(x => x.ID == Id).OrderBy(x => x.Time). That will return an IOrderedQueryable<T>, not a T (where T is whatever type is in the data.Status collection).
You need to either get a single value out of that IOrderedQueryable, using First() or some similar method, like this:
var subResult in data.Status.Select(x => x.ID).Distinct().Select(Id => data.Status.Where(x => x.ID == Id).OrderBy(x => x.Time).First())
... or flatten your IEnumerable<IQueryable<T>> to an IEnumerable<T>:
var subResult in data.Status.Select(x => x.ID).Distinct().SelectMany(Id => data.Status.Where(x => x.ID == Id).OrderBy(x => x.Time))
Edit: You may also be having an issue where C# is uncertain what type the var subResult is. If they're all Result type objects, try replacing var subResult with Result subResult.

Looks like you have to use SelectMany instead your second Select method call
data.Status.Select(x => x.ID).Distinct()
.SelectMany(Id => data.Status.Where(x => x.ID == Id).OrderBy(x => x.Time))

Related

Issue while trying to make a common method

I am trying to refactor some code. So the code I am trying to refactor is this
var userApp = string.Join(",", items.Where(x => x.LicenseType == 1).Select(x => x.GroupName));
var groupApp = string.Join(",", items.Where(x => x.LicenseType == 2).Select(x => x.GroupName));
var totalUsedLicense = GetTotalFreeLicense(schoolCode, userApp, groupApp);
foreach (var item in totalUsedLicense)
{
items.FirstOrDefault(x => x.GroupName == item.GroupName).AvailableLicense = items.FirstOrDefault(x => x.GroupName == item.GroupName).TotalLicense - item.Count;
}
Type of items can be List<ApplicationListDto> or List<AdUserApplicationDto> - they both inherit from BaseApplicationDto,
where the common properties are.
Now I want to make a function where I can pass items as a parameter. How should I do this?
I am trying to get rid of the redundant code here
if (isList)
{
if (data.Count <= 0) return;
List<AdUserApplicationDto> userApplicationDto = data;
var items = userApplicationDto;
var userApp = string.Join(",", items.Where(x => x.LicenseType == 1).Select(x => x.GroupName));
var groupApp = string.Join(",", items.Where(x => x.LicenseType == 2).Select(x => x.GroupName));
var totalUsedLicense = GetTotalFreeLicense(schoolCode, userApp, groupApp);
foreach (var item in totalUsedLicense)
{
items.FirstOrDefault(x => x.GroupName == item.GroupName).AvailableLicense = items.FirstOrDefault(x => x.GroupName == item.GroupName).TotalLicense - item.Count;
}
}
else
{
Page<ApplicationListDto> userApplicationDto = data;
if (userApplicationDto.TotalItems <= 0) return;
var items = userApplicationDto.Items;
var userApp = string.Join(",", items.Where(x => x.LicenseType == 1).Select(x => x.GroupName));
var groupApp = string.Join(",", items.Where(x => x.LicenseType == 2).Select(x => x.GroupName));
var totalUsedLicense = GetTotalFreeLicense(schoolCode, userApp, groupApp);
foreach (var item in totalUsedLicense) {
items.FirstOrDefault(x => x.GroupName == item.GroupName).AvailableLicense = items.FirstOrDefault(x => x.GroupName == item.GroupName).TotalLicense - item.Count;
}
}
If you want a method to accept different types of list with a common base type, do this:
public void SomeAction<T>(List<T> list) where T : BaseApplicationDto
{
// ...
}
You use the type of the base class as the type of the list..
public abstract class Common
{
public string CommonString { get; set; }
}
public class B : Common
{
}
public class A : Common
{
}
public class ABConsumer
{
public void DoSomething(List<Common> myList)
{
List<Common> EmptyStrings = myList.Where(x => x.CommonString == string.Empty).ToList();
}
}
You can now access properties of the base class of both classes.
This is simple inheritance.
Edit
It'll require a cast to from any given type: A or B to Common before it can be passed. If casting is an issue another good example using conditional generics can be used as shown in #ikkentims answer.

SQL Virtual Column in Linq

Is there a way to create the equivalent of a SQL virtual/computed column in a linq query within C# dotnetcore MVC page using Razor Pages and Entity Framework...
SELECT 0 as 'sortField'
FROM
database
WHERE
foo = bar
...but using method syntax, formed like so?:
Foo = await
(
_context.Foo
.Where(r => !StatusExceptionList.Contains(r.Status))
.Where(r => (Convert.ToDateTime(r.Statusdate) - today).TotalDays < 31)
.Where(r => r.DSRPID == PID)
.OrderBy(r => r.Submitteddate)
.ThenBy(r => r.Statusdate)
.ThenBy(r => r.recordnum)
)
.Union
(
_context.Foo
.Where(r => !DraftStatusExceptionList.Contains(r.Status))
.Where(r => r.DSRPID == PID)
.Where(r => r.csstatus != "not needed" || !String.IsNullOrEmpty(r.csstatus))
.Where(r => !_context.Foo
.Where(rr => rr.DSRPID == PID)
.Select(rr => rr.Fooid)
.Contains(r.Fooid)
)
.OrderBy(r => r.Submitteddate)
.ThenBy(r => r.Statusdate)
.ThenBy(r => r.recordnum)
)
.ToListAsync();
I believe you can do it like this:
class TableResults
{
public int sortField
}
from t in context.Table select new TableResults { sortField = 0 }
So you would have to have a class with a property called sortField.
you can use anonymous type like below in linq
from result in database
where foo = bar
select new { sortField=0 }
equivalent of a SQL virtual/computed column will be readonly property.
You can do it in your entity class if instance will contain all required values for calculating it's value
public class Foo
{
public int Id { get; set; }
public string Name { get; set; }
public int SortValue => Id == 42 ? 0 : Id;
}
_context.Foo.Where(f => somecondition)
.OrderBy(f => f.SortValue)
.ToList();
In case instance doesn't have all required values for calculation you can use anonymous type
_context.Foo.Where(f => somecondition)
.Select(f => new
{
Id = f.Id,
Name = f.Name,
SortValue = f.Name == "External name" ? 0 : 1
})
.OrderBy(f => f.SortValue)
.ToList();

c# - query nested type with LINQ

I have a model that looks like the following:
public class MyType{
public string Id {get;set;}
public string Name{get;set;}
public List<MyType> Children{get;set;}
}
and in my data I have just two level data, meaning my objects will look like:
{
MyType{"1","firstParent",
{
MyType{"2","firstChild",null},
MyType{"3","secondChild",null}}
},
MyType{"4","secondParent",
{
MyType{"5","firstChild",null},
MyType{"6","secondChild",null}}
}
}
How do I query to get MyType object with a specific Id where it might be a parent or child?
The following will return only parents.
collection.FirstOrDefault(c => c.id==id)
You can use Any with a recursive local function to find objects on any level (your data structure would seem to indicate a deeper level is possible)
bool hasIdOrChildren(MyType t, string localId)
{
return t.Id == localId || (t.Children != null && t.Children.Any(o => hasIdOrChildren(o, localId)));
}
collection.FirstOrDefault(c => hasIdOrChildren(c, id));
Or using pre C#7 syntax:
Func<MyType, string, bool> hasIdOrChildren = null;
hasIdOrChildren = (MyType t, string localId) =>
{
return t.Id == localId || (t.Children != null && t.Children.Any(o => hasIdOrChildren(o, localId)));
};
collection.FirstOrDefault(c => hasIdOrChildren(c, id));
If you are only interested in one level, you can drop the reclusiveness:
collection.FirstOrDefault(c => c.Id == id || (c.Children != null && c.Children.Any(o => o.Id == id)));
Edit
The code above gives the parent if any child has the id, you can also flatten the whole tree structure using SelectMany also with a recursive function:
IEnumerable<MyType> flattenTree(MyType t)
{
if(t.Children == null)
{
return new[] { t };
}
return new[] { t }
.Concat(t.Children.SelectMany(flattenTree));
};
collection
.SelectMany(flattenTree)
.FirstOrDefault(c => c.Id == id);
This method can be useful for any type of processing where you need to flatten the tree.
You could build a list of all MyType including children and then query on it like this :
collection.SelectMany(c => c.Children).Concat(collection).Where(c => c.id == id)
I think you're looking for
var flattenedList = IEnumerable.SelectMany(i => i.ItemsInList);
This flattens the list and gives back one list with all items in it.
In your case you need to select
collection.SelectMany(c => c.Type).Concat(collection).Where(item => item.Id == 5);
MSDN
You still got the childs in your joined parents here, but you can still erase them or ignore them.
I think, you should flatten collection using SelectMany method, then use FirstOrDefault to get element by id:
MyType selected = collection
.SelectMany(obj => new MyType[] {obj, obj.NestedList})
.FirstOrDefault(obj => obj.id == id);

Use the result of SelectSubQuery in a condition with NHibernate QueryOver

I'm using NHibernate with IQueryOver to retrieve a List<Message>. Users can mark a Message as a favourite. Each Message has a property public bool IsFavourite which contains true when at least one User has marked that Message as their favourite.
So in my query I use SelectSubQuery to retrieve the number of times it was marked as favourite. So far so good. I also want to use the result from that subquery in a condition to set IsFavourite.
My query right now looks like this.
Message messageAlias = null;
MessageDTO messageDto = null;
var messages = GetSessionFactory().GetCurrentSession()
.QueryOver<Message>(() => messageAlias)
.SelectList(list => list
.Select(() => messageList.Id).WithAlias(() => messageDto.Id)
.Select(() => messageList.Title).WithAlias(() => messageDto.Title)
.SelectSubQuery(
QueryOver.Of<UserMessageFavourite>()
.Where(f => f.Message.Id == messageAlias.Id).ToRowCountQuery()).WithAlias(() => messageDto.FavouriteCount)
)
)
The property MessageDto,FavouriteCount is merely there so I can set the IsFavourite property. So what I would like to do is to use the SubQuery result in a condition and set the result of that condition to IsFavourite like .SelectSubQuery(subquery.ToRowCountQuery()) > 0).WithAlias(() => messageDto.IsFavourite)
If you already have FavouriteCount in your MessageDTO entity then you can just use readonly C# property like this:
class MessageDTO {
//other properties
public int FavouriteCount { get; set; }
public bool IsFavorite => FavouriteCount > 0;
}
Otherwise you can use custom projection:
Message messageAlias = null;
MessageDTO messageDto = null;
var projection = Projections.Conditional(
Subqueries.Exists(QueryOver.Of<UserMessageFavourite>()
.Where(f => f.Message.Id == messageAlias.Id).DetachedCriteria)),
Projections.Constant(true),
Projections.Constant(false));
var messages = GetSessionFactory().GetCurrentSession()
.QueryOver<Message>(() => messageAlias)
.SelectList(list => list
.Select(() => messageList.Id).WithAlias(() => messageDto.Id)
.Select(() => messageList.Title).WithAlias(() => messageDto.Title)
.Select(projection).WithAlias(() => messageDto.IsFavorite)
)

Retrieving entities with related tables c# using REST API2

I am building a web API that is suppose to populate data from a linked child table using a where clause.
I have attempted using include() with where() as per eager loading but without success.
public IQueryable<Market> getAllActive()
{
return db.Markets.Where(c => c.IsActive == true).Include(d => d.TravelCentres.Where(e => e.IsActive == true));
}
On researching, there are recommendations that I use explicit loading but it keeps error about the need to cast the data type. I am lost of ideas at the moment and will appreciate any help. Here is my code:
private TravelCentresDbContext db = new TravelCentresDbContext();
public IQueryable<Market> getAllActive()
{
//return db.Markets.Where(c => c.IsActive == true).Include(d => d.TravelCentres);
var result = db.Markets
.Where(c => c.IsActive == true)
.Select(p => new
{
Market = p.MarketId,
TravelCentres = p.TravelCentres.Where(x => x.IsActive == true)
});
return (IQueryable<Market>)result;
}
I get this exception message Unable to cast object of type
'System.Data.Entity.Infrastructure.DbQuery1[<>f__AnonymousType42[System.String,System.Collections.Generic.IEnumerable1[TravelCentres.Models.TravelCentre]]]'
to type 'System.Linq.IQueryable1[TravelCentres.Models.Market]'.
Blockquote
result is not an IQuerytable<Market>, it's an IQueryable of an anonymous type with properties Market and TravelCenters. So (IQueryable<Market>)result is an invalid cast. It would be advisable to create a model with Market and TravelCenters properties and then return that.
public class MyModel
{
public int MarketId { get; set; }
public IEnumerable<TravelCentre> TravelCentres { get; set; }
}
.
var result = db.Markets
.Where(c => c.IsActive == true)
.Select(p => new MyModel()
{
Market = p.MarketId,
TravelCentres = p.TravelCentres.Where(x => x.IsActive == true)
});
return (IQueryable<MyModel>)result;

Categories

Resources