SQL Virtual Column in Linq - c#

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();

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.

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;

Error using Serge Zab's helper for optgroup dropdowns

I'm trying to work with the optgroup dropdown helper from Serge Zab that can be found here.
This is my category table:
As you can see I have a category and a category can also be categoryparent from a category. I want to have the parentcategorys as optgroup and the children as options of the optgroup. (Only the children can be selected)
In my ViewModel:
public short? CategoryId { get; set; }
public IEnumerable<ReUzze.Helpers.GroupedSelectListItem> GroupedTypeOptions { get; set; }
In my Controller:
[Authorize] // USER NEEDS TO BE AUTHORIZED
public ActionResult Create()
{
ViewBag.DropDownList = ReUzze.Helpers.EnumHelper.SelectListFor<Condition>();
var model = new ReUzze.Models.EntityViewModel();
PutTypeDropDownInto(model);
return View(model);
}
[NonAction]
private void PutTypeDropDownInto(ReUzze.Models.EntityViewModel model)
{
model.GroupedTypeOptions = this.UnitOfWork.CategoryRepository.Get()
.OrderBy(t => t.ParentCategory.Name).ThenBy(t => t.Name)
.Select(t => new GroupedSelectListItem
{
GroupKey = t.ParentId.ToString(),
GroupName = t.ParentCategory.Name,
Text = t.Name,
Value = t.Id.ToString()
}
);
}
In my View:
#Html.DropDownGroupListFor(m => m.CategoryId, Model.GroupedTypeOptions, "[Select a type]")
When I try to run this I always get the error:
Object reference not set to an instance of an object.
I get this error on this rule : .OrderBy(t => t.ParentCategory.Name).ThenBy(t => t.Name)
Can anybody help me find a solution for this problem?
Your error message suggests that either a t is null or a t.ParentCategory is null.
You can fix the error by simply checking for nulls, but this may or may not give you the desired output, depending on whether you also want to include categories that don't have a parent.
model.GroupedTypeOptions = this.UnitOfWork.CategoryRepository.Get()
.Where(t => t.ParentCategory != null)
.OrderBy(t => t.ParentCategory.Name).ThenBy(t => t.Name)
.Select(t => new GroupedSelectListItem
{
GroupKey = t.ParentId.ToString(),
GroupName = t.ParentCategory.Name,
Text = t.Name,
Value = t.Id.ToString()
});
I'm assuming your CategoryRepository can't return a null t, but if it can, you'd adapt the where to be:
.Where(t => t != null && t.ParentCategory != null)
The problem is that not all of your entities returned will have a ParentCategory.
It seems you should only be selecting the children and not the parents.
Try this:
model.GroupedTypeOptions = this.UnitOfWork.CategoryRepository.Get()
.Where(t => t.ParentCategory != null)
.OrderBy(t => t.ParentCategory.Name).ThenBy(t => t.Name)
.Select(t => new GroupedSelectListItem
{
GroupKey = t.ParentId.ToString(),
GroupName = t.ParentCategory.Name,
Text = t.Name,
Value = t.Id.ToString()
}
);

Linq OrderedQueryAble Error

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))

Categories

Resources