How to check for nulls using LINQ - linq

I have this code. How can I check for null values with the SingleOrDefault method?
public static List<ETY.Rol> GetRolesByApplicationAndCompany(this UsuarioContext usuario, int company, int app)
{
List<ETY.Company> lCompanies= usuario.Companies;
var roles = lCompanies.
SingleOrDefault(e => (e.Id == company)).Applications.
SingleOrDefault(a => a.Id == app).Roles;
return roles;
}

You could try looking at a Maybe/IfNotNull extension method (here and here).
Or use Linq syntax something like this (untested):
var q = from company in lCompanies.SingleOrDefault(e => e.Id == company)
where company != null
let application = company.Applications.SingleOrDefault(a => a.Id == app)
where application != null
select application.Roles;
(Greg Beech's answer is better if the Single condition is guaranteed)

Do you mean returning null or an empty list if any of the SingleOrDefault returns null? In that case:
var company = lCompanies.SingleOrDefault(e => (e.Id == company));
if(company != null) {
var application = company.Applications.SingleOrDefault(a => a.Id == app);
if(application!=null) {
return application.Roles;
}
}
return null; //Or: return new List<ETY.Rol>();

Rather than using SingleOrDefault you could write a chained query as follows. You lose the semantics of ensuring that there's only a single application or company, but if you know that to always be the case then it shouldn't be a problem.
return from c in lCompanies where c.Id == company
from a in c.Applications where a.Id == app
select a.Roles;

Related

automatically expand the result of an odata function

I defined an odata function to mimic $search which is not supported yet in the recent core release. I want to return the core entity plus an expanded entity which would translate into a js object on each Person in the returned json values array.
I tried odata/People/MyNS.Find(text='john', orderby='CreatedOn')?$expand=CurrentWork where CurrentWork is on People, but that did not work.
Thoughts on how to do this?
// my controller code for the function
[HttpGet]
public ActionResult<ICollection<People>> Find([FromODataUri] string text,
[FromODataUri] string orderBy)
{
if (text == null || text.Length == 0)
return Get().ToList();
if (orderBy == null || orderBy.Length == 0)
orderBy = "CreatedOn";
return _db.People
.Where(p => p.FirstName.Contains(text)
|| p.LastName.Contains(text)
|| p.Nickname.Contains(text))
.OrderBy(orderBy)
.Take(5000)
.ToList();
}
Regular expansion of CurrentWork in a non-function works fine e.g. odata/People?$expand=CurrentWork.
By looking at the Linq query, it's fetching only People data and not any of it's child collections. You should use Include to fetch data for child collections along with parent entity like below. Read more on loading related entities here.
// my controller code for the function
[HttpGet]
public ActionResult<ICollection<People>> Find([FromODataUri] string text,
[FromODataUri] string orderBy)
{
if (text == null || text.Length == 0)
return Get().ToList();
if (orderBy == null || orderBy.Length == 0)
orderBy = "CreatedOn";
return _db.People
.Where(p => p.FirstName.Contains(text)
|| p.LastName.Contains(text)
|| p.Nickname.Contains(text))
.Include(p => p.CurrentWork) // I have added this line
.OrderBy(orderBy)
.Take(5000)
.ToList();
}
Note: You still need to use $expand=CurrentWork as query string. Without this query string, server will remove child collections before sending response to client.
Here's what I came up with in the end. I noticed that the included entities were pulling in alot of data from the database so I reduced down the pull quite a bit by being specific. Include just pulled everything and I could not reduce the Include down directly so I had to use a Select.
[HttpGet]
public IOrderedQueryable Find2([FromODataUri] string text,
[FromODataUri] string orderBy)
{
if (orderBy == null || orderBy.Length == 0)
orderBy = "CreatedOn DESC";
if (text == null || text.Length == 0)
return Get().OrderBy(orderBy);
var r = LikeToRegular(text);
return _db.People
.AsNoTracking() // can't use if using lazy loading
.Select(p => new
{
p.FirstName,
p.LastName,
p.Nickname,
p.CreatedOn,
p.CurrentWork.Title,
p.CurrentWork.Company.CompanyName
})
// Forces local computation, so pulls entire people dataset :-(
.Where(x => Regex.IsMatch(x.LastName ?? "", r)
|| Regex.IsMatch(x.FirstName ?? "", r, RegexOptions.IgnoreCase)
|| Regex.IsMatch(x.Nickname ?? "", r, RegexOptions.IgnoreCase)
|| Regex.IsMatch($"{x.FirstName} {x.LastName}", r,
RegexOptions.IgnoreCase))
.OrderBy(orderBy);
}
// Allow some wildcards in the search...
public static String LikeToRegular(String value)
{
return "^" + Regex.Escape(value)
.Replace("_", ".")
.Replace("%", ".*") + "$";
}

Use method in entity framework query

Is there anyway around this error? I'd like to reuse the same lamba expression in other queries instead of having duplication. Can LinqKit or other linq expression do this?
Error
LINQ to Entities does not recognize the method 'Boolean GetEvent(Tournaments.Data.Entities.Event, System.String)' method, and this method cannot be translated into a store expression.
Code
public MobileEventDetailModel GetDetails(string applicationId)
{
var #event = (from e in _eventsRepository.DataContext.Events.Include(q => q.Assets.Select(a => a.Asset))
where GetEvent(e, applicationId)
select new
{
e.Id,
e.EventParent.Name,
LogoId = (from a in e.Assets
where a.Type == EventAssetType.Logo
select a.AssetId).FirstOrDefault()
}).FirstOrDefault();
return new MobileEventDetailModel
{
Id = #event.Id,
Name = #event.Name,
Logo = string.Format("{0}{1}{2}", Config.BaseUrl, Config.ImagesPath, #event.LogoId)
};
}
public bool GetEvent(Event #event, string applicationId)
{
return #event.Active && #event.Visible && #event.MobileEventApplications.Any(m =>
m.MobileApplication.ApplicationId == applicationId &&
(!m.MobileApplication.ActivationLength.HasValue || EntityFunctions.AddDays(DateTime.Now, 1) < EntityFunctions.AddMonths(m.MobileApplication.DateActivated, m.MobileApplication.ActivationLength.Value)));
}
You need to use an Expression:
public MobileEventDetailModel GetDetails(string applicationId)
{
var event = _eventsRepository.DataContext.Events.Include(q => q.Assets.Select(a => a.Asset))
.Where(GetEvent(applicationId))
.Select(a => new
{
a.Id,
a.EventParent.Name,
LogoId = (from b in a.Assets
where b.Type == EventAssetType.Logo
select b.AssetId).FirstOrDefault()
}).FirstOrDefault();
return new MobileEventDetailModel
{
Id = event.Id,
Name = event.Name,
Logo = string.Format("{0}{1}{2}", Config.BaseUrl, Config.ImagesPath, event.LogoId)
};
}
public Expression<Func<Event, bool>> GetEvent(int applicationId)
{
return = a => a.Active
&& a.Visible
&& a.MobileEventApplications
.Any(m => m.MobileApplication.ApplicationId == applicationId
&& (!m.MobileApplication.ActivationLength.HasValue
|| EntityFunctions.AddDays(DateTime.Now, 1)
< EntityFunctions
.AddMonths(m.MobileApplication.DateActivated, m.MobileApplication.ActivationLength.Value)
)
);
}
Update: Sorry it was late the other night, the changed version is hopefully more what you were looking for.

Deep LINQ projections, how?

Let's say I have a model created with EF 4.0
User
Roles
Permissions
Each entity has a DeleteDate property.
I want to get a specific user (with Name =...) and have the tree filled with items where DeletedDate == null..
This must be done with anonymous type projection as result, but I don't know how to accomplish this with a hierachy deeper than 2..
This is what I already have:
public MyProjection MyCall(string givenName)
{
var result = from s in context.Users
where (s.Name == givenName &&
s.DeletedDate == null)
select new
{
s,
roles = from r in s.Roles
where r.DeletedDate == null
select r
};
var outcome = result.FirstOrDefault();
if (outcome != null)
{
var myProjection = new MyProjection()
{
User = outcome.s,
Roles = outcome.roles
};
return myProjection;
}
return null;
}
Depending on your structure you could do something like this:
var result = m.Users.Where(u => u.DeletedDate == null)
.Select( u => new
{
u,
roles = u.Roles.Where(r => r.DeletedDate == null)
.Select(r => new
{
r,
permissions = r.Permissions.Where(p => p.DeletedDate == null)
})
}).FirstOrDefault(item => item.u.Name == givenName);
If you retrieve with the following:
var result = from s in MyUsers
where s.DeletedDate == null
select new aUser{
Roles = (from r in s.Roles
where r.DeletedDate == null
select r).ToList()
};
And then create a TreeView:
TreeView treeView = new TreeView();
Then set the ItemsSource of the TreeView to the IEnumerable:
treeView.ItemsSource = result;
Then build a HierarchicalDataTemplate in your TreeView to represent your Lists (similar to this or for more in depth this), then voila!

LINQ Where(), what to do if I need to get everything?

Consider the next example:
public List<Allergy> GetAllergies(int? ingredientId = null)
{
var allergies = new List<Allergy>();
var preSelect = ingredientId != null
? _dataContext.Ingredients.Where(x=> x.Id == ingredientId).SelectMany(x=>x.Allergies.AsQueryable())
: _dataContext.Allergies.AsQueryable();
preSelect.ToList().ForEach(x =>
{
var a = Mapper.Map<Database.Allergy, Allergy>(x);
allergies.Add(a);
});
return allergies;
}
That works, but I believe it's possible to get rid of null checking part and get all Ingredients if ingredientId is null right there in Where() body. How can we do that? Also any other suggestions to improve this piece of code will be appreciated.
Try this: You can think of the where clause as an if statement.
_dataContext.Ingredients.Where( x => x.Id != null && x.Id == ingredientId)
(Assuming x.Id is also of type int?)
Try this:
var preSelect =
_dataContext
.Ingredients
.Where
(
x=> (!ingredientId.HasValue || x.Id == ingredientId)
)
.SelectMany(x=>x.Allergies.AsQueryable());

Linq: Nested queries are better than joins, but what if you use 2 nested queries?

In her book Entity Framework Julie Lerman recommends using nested queries in preference to joins (scroll back a couple of pages).
In her example see populates 1 field this way, but what id you want to populate 2?
I have an example here where I would prefer to populate the Forename and Surname with the same nested query rather than 2 separate ones. I just need to know the correct syntax to do this.
public static List<RequestInfo> GetRequests(int _employeeId)
{
using (SHPContainerEntities db = new SHPContainerEntities())
{
return db.AnnualLeaveBookeds
.Where(x => x.NextApproverId == _employeeId ||
(x.ApproverId == _employeeId && x.ApprovalDate.HasValue == false))
.Select(y => new RequestInfo
{
AnnualLeaveDate = y.AnnualLeaveDate,
Forename = (
from e in db.Employees
where e.EmployeeId == y.EmployeeId
select e.Forename).FirstOrDefault(),
Surname = (
from e in db.Employees
where e.EmployeeId == y.EmployeeId
select e.Surname).FirstOrDefault(),
RequestDate = y.RequestDate,
CancelRequestDate = y.CancelRequestDate,
ApproveFlag = false,
RejectFlag = false,
Reason = string.Empty
})
.OrderBy(x => x.AnnualLeaveDate)
.ToList();
}
}
There's nothing wrong with your query, but you can write it in a way that is much simpler, without the nested queries:
public static List<RequestInfo> GetRequests(int employeeId)
{
using (SHPContainerEntities db = new SHPContainerEntities())
{
return (
from x in db.AnnualLeaveBookeds
where x.NextApproverId == employeeId ||
(x.ApproverId == employeeId && x.ApprovalDate == null)
orderby x.AnnualLeaveDate
select new RequestInfo
{
AnnualLeaveDate = x.AnnualLeaveDate,
Forename = x.Employee.Forename,
Surname = x.Employee.Surname,
RequestDate = x.RequestDate,
CancelRequestDate = x.CancelRequestDate,
ApproveFlag = false,
RejectFlag = false,
Reason = string.Empty
}).ToList();
}
}
See how I just removed your from e in db.Employees where ... select e.Forename) and simply replaced it with x.Employee.Forename. When your database contains the correct foreign key relationships, the EF designer will successfully generate a model that contain an Employee property on the AnnualLeaveBooked entity. Writing the query like this makes it much more readable.
I hope this helps.
try this
using (SHPContainerEntities db = new SHPContainerEntities())
{
return db.AnnualLeaveBookeds
.Where(x => x.NextApproverId == _employeeId ||
(x.ApproverId == _employeeId && x.ApprovalDate.HasValue == false))
.Select(y =>
{
var emp = db.Emplyees.Where(e => e.EmployeeId == y.EmployeeId);
return new RequestInfo
{
AnnualLeaveDate = y.AnnualLeaveDate,
Forename = emp.Forename,
Surname = emp.Surname,
RequestDate = y.RequestDate,
CancelRequestDate = y.CancelRequestDate,
ApproveFlag = false,
RejectFlag = false,
Reason = string.Empty
};
).OrderBy(x => x.AnnualLeaveDate).ToList();
}

Resources