Reusing queries with Entity Framework Core - c#

I'm trying to make some queries using EF-Core and I have the following code
public List<Visitation> GetAllVisitations()
{
return this.hospital.Visitations
.Where(v => v.DoctorId == this.Doctor.Id)
.Select(v => new Visitation
{
Doctor = v.Doctor,
Patient = v.Patient,
Date = v.Date,
Comments = v.Comments
})
.ToList();
}
public List<Visitation> GetVisitationByPatient(int id)
{
var patient = this.GetPatientById(id);
return this.hospital.Visitations
.Where(v => v.PatientId == patient.Id)
.Select(v => new Visitation
{
Doctor = v.Doctor,
Patient = v.Patient,
Date = v.Date,
Comments = v.Comments
})
.ToList();
}
It is pretty obvious that the Select statement is the same in both methods. However I know that EF Core uses Expression<Func>, rather than Func therefore I do not know how to make an Expression, which can be used in both Select statements.

The query won't execute until you call .ToList(). So you may take the partial query up to the .Where() and pass it to a function that adds the Select() portion.
Something like this:
public List<Visitation> GetAllVisitations()
{
var query = this.hospital.Visitations
.Where(v => v.DoctorId == this.Doctor.Id);
return this.addTransformation(query)
.ToList();
}
public List<Visitation> GetVisitationByPatient(int id)
{
var patient = this.GetPatientById(id);
var query = this.hospital.Visitations
.Where(v => v.PatientId == patient.Id)
return this.addTransformation(query)
.ToList();
}
public IQueriable<Visitation> AddTransformation(IQueriable<Visitation> query)
{
return query.Select(v => new Visitation
{
Doctor = v.Doctor,
Patient = v.Patient,
Date = v.Date,
Comments = v.Comments
});
}

Related

Change orderby depending on value of var

I have a foreach with an ordebydescending(), but in one case I need to use an orderby() instead. Depending on the value of articleType how can I use an inline condition inside the foreach to allow this to happen.
This is the condition I need to build into to determine the use of orderbydescending or orderby
if (articleType == BusinessLogic.ArticleType.Webinar)
This is the full function
public static List<Article> GetArticles(BusinessLogic.ArticleType articleType, long languageID)
{
List<Article> articles = new List<Article>();
using (var db = new DatabaseConnection())
{
foreach (var record in db
.GetArticles(BusinessLogic.SystemComponentID, (int) articleType, languageID)
.OrderByDescending(c => c.Date_Time))
{
#region articleTextLanguageID
long articleTextLanguageID = GetArticleTextLanguageID(record.ID, languageID);
string previewImageName = GetArticle(record.ID, articleTextLanguageID).PreviewImageName;
#endregion
Article article = new Article()
{
ID = record.ID,
Title = record.Title,
Summary = record.Summary,
PreviewImageName = previewImageName,
Date = record.Date_Time,
ArticleTextLanguageID = articleTextLanguageID
};
articles.Add(article);
}
}
return articles;
}
Was thinking something along these lines, but its not working
foreach (var record in db
.GetArticles(BusinessLogic.SystemComponentID, (int)articleType, languageID)
.Where(articleType == BusinessLogic.ArticleType.Webinar.ToString()?.OrderByDescending(c => c.Date_Time)) : .OrderBy(c => c.Date_Time)))
The way to do this would be to construct the query in pieces. For example:
var query = db.GetArticles(BusinessLogic.SystemComponentID, (int)articleType, languageID);
if(articleType == BusinessLogic.ArticleType.Webinar)
{
query = query.OrderByDescending(c => c.Date_Time);
}
else
{
query = query.OrderBy(c => c.Date_Time);
}
foreach(var record in query)
{
// process
}
If you required additional sorting, you'd need an extra variable typed as IOrderedEnumerable/IOrderedQueryable (depending on what GetArticles returns) as an intermediate to chain ThenBy/ThemByDescending:
var source = db.GetArticles(BusinessLogic.SystemComponentID, (int)articleType, languageID);
IOrderedEnumerable<Article> query;
if(articleType == BusinessLogic.ArticleType.Webinar)
{
query = source.OrderByDescending(c => c.Date_Time);
}
else
{
query = source.OrderBy(c => c.Date_Time);
}
if(somethingElse)
{
query = query.ThenBy(c => c.OtherProperty);
}
foreach(var record in query)
{
// process
}
Based on your comment below, as notes above the second example would look more like the following (this means that db.GetArticles returns an IQueryable<Article> and not an IEnumerable<Article>):
var source = db.GetArticles(BusinessLogic.SystemComponentID, (int)articleType, languageID);
IOrderedQueryable<Article> query;
if(articleType == BusinessLogic.ArticleType.Webinar)
{
query = source.OrderByDescending(c => c.Date_Time);
}
else
{
query = source.OrderBy(c => c.Date_Time);
}
if(somethingElse)
query = query.ThenBy(c => c.OtherProperty);
foreach(var record in query)
{
// process
}
You could also shorten it to the following:
var source = db.GetArticles(BusinessLogic.SystemComponentID, (int)articleType, languageID);
var query = articleType == BusinessLogic.ArticleType.Webinar
? source.OrderByDescending(c => c.Date_Time)
: source.OrderBy(c => c.Date_Time);
if(somethingElse)
query = query.ThenBy(c => c.OtherProperty);
foreach(var record in query)
{
// process
}

LINQ Data Sorting

I am trying to fill select tag options from JQuery ajax call. I am using Asp.Net Core 2.1 Razor Pages, and PostgreSQL as DB.
Here is my Server side LINQ code
[HttpGet]
public ActionResult TypeofAccounts()
{
var result = (from N in _POSContext.TypeOfAccounts
select new { label = N.AccountType, id = N.AccountType });
return Json(result);
}
It works fine. Now, I want to sort those results from LINQ so I tried following ways but it always encounters Npgsql Exception "column \"label\" does not exist"
var result = (from N in _POSContext.TypeOfAccounts.OrderBy(x=>x.AccountType)
select new { label = N.AccountType, id = N.AccountType });
var result = (from N in _POSContext.TypeOfAccounts
select new { label = N.AccountType, id = N.AccountType }).OrderBy(x => x.label);
var result = (from N in _POSContext.TypeOfAccounts.OrderBy(x => x.AccountType)
where N.AccountType != null
select new { label = N.AccountType, id = N.AccountType });
I could see coloumn is missing in generated sql.
{SELECT x."AccountType" AS id
FROM "TypeOfAccounts" AS x
WHERE x."AccountType" IS NOT NULL
ORDER BY label}
You need to invoke the query from the database using ToList method, then selecting your object, like this:
var result = _POSContext.TypeOfAccounts
.Where(x => x.AccountType != null)
.OrderBy(x => x.AccountType)
.ToList()
.Select(x =>
new
{
label = x.AccountType,
id = x.AccountType
}
);
You can try this
var result = _POSContext.TypeOfAccounts
.Where(x => x.AccountType != null)
.OrderBy(x => x.AccountType)
.ToList()
.Select(x =>
new
{
label = x.AccountType,
id = x.AccountType
}
);

Entity Framework: How to query a number of related tables in a database making a single trip

I have a query that is currently far too slow.
I am trying to search a Code (a string) on the main page that will bring the user the relevant info.
Eg. The user can search a code from the main page and this will search for the code in Job, Work Phase, Wbs, Work Element, EA, Jobcard and Estimate and return the relevant info.
I make a number of trips to the database to collect the data i need when I believe it can be done in just one.
I have a number of tables that are all linked:
Contracts, Jobs, WorkPhases, Wbss, Engineering Activities, Jobcards and Estimates.
Contracts have a list of Jobs,
Jobs have a list of Workphases,
Workphases have a list of Wbss etc
Is there a quicker way to do this?
public Result Handle(Query query)
{
query.Code = query.Code ?? string.Empty;
var result = new Result();
//result.SetParametersFromPagedQuery(query);
result.Items = new List<Item>();
if (query.SearchPerformed)
{
var contracts = _db.Contracts.AsEnumerable().Where(x => x.Code == query.Code);
result.Items = result.Items.Concat(contracts.Select(x => new Item()
{
Code = x.Code,
Id = x.Id,
Name = x.Name,
Type = MainPageSearchEnum.Contract,
ContractName = x.Name,
Url = string.Format("Admin/Contract/Edit/{0}", x.Id)
})).ToList();
var jobs = _db.Jobs.AsEnumerable().Where(x => x.Code == query.Code);
result.Items = result.Items.Concat(jobs.Select(x => new Item()
{
Code = x.Code,
Id = x.Id,
Name = x.Name,
ContractName = x.Contract.Name,
Type = MainPageSearchEnum.Job,
Url = string.Format("Admin/Job/Edit/{0}", x.Id)
})).ToList();
//var workPhases = _db.WorkPhases.AsEnumerable().Where(x => x.ContractPhase.Code.ToLower() == query.Code.ToLower());
var workPhases = _db.WorkPhases.AsEnumerable().Where(x => x.ContractPhase.Code == query.Code);
result.Items = result.Items.Concat(workPhases.Select(x => new Item()
{
Code = x.ContractPhase.Code,
Id = x.Id,
Name = x.ContractPhase.Name,
Type = MainPageSearchEnum.WorkPhase,
Url = string.Format("Admin/WorkPhase/Edit/{0}", x.Id)
})).ToList();
var wbss = _db.WBSs.AsEnumerable().Where(x => x.Code == query.Code);
result.Items = result.Items.Concat(wbss.Select(x => new Item()
{
Code = x.Code,
Id = x.Id,
Name = x.Name,
Type = MainPageSearchEnum.WBS,
Url = string.Format("Admin/WBS/Edit/{0}", x.Id)
})).ToList();
var eas = _db.EngineeringActivities.AsEnumerable().Where(x => x.Code == query.Code);
result.Items = result.Items.Concat(eas.Select(x => new Item()
{
Code = x.Code,
Id = x.Id,
Name = x.Name,
Type = MainPageSearchEnum.EA,
Url = string.Format("Admin/EngineeringActivity/Edit/{0}", x.Id)
})).ToList();
var jcs = _db.Jobcards.AsEnumerable().Where(x => x.Code == query.Code);
result.Items = result.Items.Concat(jcs.Select(x => new Item()
{
Code = x.Code,
Id = x.Id,
Name = x.Name,
Type = MainPageSearchEnum.EA,
Url = string.Format("Admin/JobCard/Edit/{0}", x.Id)
})).ToList();
var estimates = _db.Estimates.AsEnumerable().Where(x => x.Code == query.Code);
result.Items = result.Items.Concat(estimates.Select(x => new Item()
{
Code = x.Code,
Id = x.Id,
Name = x.Name,
Type = MainPageSearchEnum.Estimate,
Url = string.Format("Estimation/Estimate/Edit/{0}", x.Id)
})).ToList();
}
return result;
}
Disclaimer: I'm the owner of the project Entity Framework Plus
This library has a Query Future feature which allows batching multiple queries in a single roundtrip.
Example:
// using Z.EntityFramework.Plus; // Don't forget to include this.
var ctx = new EntitiesContext();
// CREATE a pending list of future queries
var futureCountries = ctx.Countries.Where(x => x.IsActive).Future();
var futureStates = ctx.States.Where(x => x.IsActive).Future();
// TRIGGER all pending queries in one database round trip
// SELECT * FROM Country WHERE IsActive = true;
// SELECT * FROM State WHERE IsActive = true
var countries = futureCountries.ToList();
// futureStates is already resolved and contains the result
var states = futureStates.ToList();
Wiki: EF+ Query Future
Have you tried the Union / UnionAll operator?
It's purpose is exactly like you wish - combine the identical data from different sources.
Furthermore due to the concept of deferred execution your query will only be executed when you actually iterate over the result (or call a method that does that, for example - .ToList()
var contractsQuery = _db.Contracts.AsEnumerable().Where(x => x.Code == query.Code).Select(x=>new {Code=x.Code, Id=x.Id, ...});
var jobsQuery = _db.Jobs.AsEnumerable().Where(x => x.Code == query.Code).Select(x=>new{Code=x.Code, Id=x.Id, ...});
var workPhasesQuery = _db.WorkPhases.AsEnumerable().Where(x => x.ContractPhase.Code == query.Code).Select(x=>new{Code=x.Code, Id=x.Id, ...});
// and so on
var combinedQuery = contractsQuery.UnionAll(jobsQuery).UnionAll(workPhasesQuery ).UnionAll(...
var result = combinedQuery.ToList();
A similar question is Union in linq entity framework
Another code sample can be found here
Please notice that this is exactly the same concept of manipulating data as in T-SQL union, and under the covers you will get an sql query using a union operator
Yes there most certainly is a way to query multiple tables. You can use the Include() method extension for your query. for instance:
var examplelist = _db.Contracts.(v => v.id == "someid" && v.name == "anotherfilter").Include("theOtherTablesName").ToList();
You can include as many tables as you like this way. This is the recommended method.
You can also use the UnionAll() method but you'd have to define your queries separately for this

How to custom response in asp.net web api2

the problem in this function when add this two lines.
UserImage = GetImagePath(db.Users.FirstOrDefault(x => x.Id == p.User_ID).Image),
InsertDate = p.InsertDate.ToString("dd/MM/yyyy")
I need to custom Image Path and custom date format,show this problem.
LINQ to Entities does not recognize the method System.String ToString(System.String) method, and this method cannot be translated into a store expression
private static string GetImagePath(string ImageName)
{
return System.Configuration.ConfigurationManager.AppSettings["websiteurl"].ToString() + "/uploads/" + ImageName;
}
[HttpGet]
[Route("Comments")]
public IHttpActionResult Comments(string Post_ID)
{
var list = db
.ShalehComments
.Where(p => p.Shaleh_ID == Post_ID && p.ParentID == 0)
.Select(p => new {
ID = p.ID,
Comment = p.Comment,
User = db.Users.FirstOrDefault(x=> x.Id == p.User_ID).FullName,
UserImage = GetImagePath(db.Users.FirstOrDefault(x => x.Id == p.User_ID).Image),//here problem
InsertDate = p.InsertDate.ToString("dd/MM/yyyy")//and here
}).ToList();
return Ok(
new
{
result = true,
data = list
}
);
}
This error occurred because Entity Framework does not know how to execute .ToString() or GetImagePath method in sql. So you should load the data by using ToList.So you try something like:
[HttpGet]
[Route("Comments")]
public IHttpActionResult Comments(string Post_ID)
{
var list = db
.ShalehComments
.Where(p => p.Shaleh_ID == Post_ID && p.ParentID == 0)
.ToList()//add ToList
.Select(p => new {
ID = p.ID,
Comment = p.Comment,
User = db.Users.FirstOrDefault(x=> x.Id == p.User_ID).FullName,
UserImage = GetImagePath(db.Users.FirstOrDefault(x => x.Id == p.User_ID).Image),
InsertDate = p.InsertDate.ToString("dd/MM/yyyy")//and here
}).ToList();
return Ok(
new
{
result = true,
data = list
}
);
}

C# Linq query to return Dictionary<int,int[]>

Ok, so I have a need to create/return a Dictionary from the results of a Linq Query. I have tried just about everything I can think of and keep running into issues. Here is what I am currently attempting...
public static Dictionary<int,int[]> GetEntityAuthorizations(int userId)
{
using (MyDb db = new MyDb())
{
var query = db.EntityManagerRoleAssignments.Where(x => x.EntityManager.ManagerId == userId);
var entityId = query.Select(x => x.EntityManager.EntityId);
var roles = query.Select(x => x.RoleId).ToArray();
var result = query.ToDictionary(entityId, roles);
return result;
}
}
any help at all would be greatly appreciated. what i am looking for to be returned from this is a Dictionary where the Key is the entityId or EntityManager.EntityId and the Value(s) are an array of associated RoleId's.
Currently I am getting the following two errors at compile time, and other attempts have been errors similar but not exact to these.
Error 11 The type arguments for method 'System.Linq.Enumerable.ToDictionary<TSource,TKey>(System.Collections.Generic.IEnumerable, System.Func<TSource,TKey>, System.Collections.Generic.IEqualityComparer<TKey>)' cannot be inferred from the usage. Try specifying the type arguments explicitly.
Error 12 Cannot implicitly convert type 'System.Collections.Generic.Dictionary<TKey,Sqor.Database.DbEntityManagerRoleAssignment>' to 'System.Collections.Generic.Dictionary<int,int[]>'
UPDATE - Working Solution (Thanks to #D Stanley)
public static Dictionary<int,int[]> GetEntityAuthorizations(int userId)
{
using (SqorDb db = new SqorDb())
{
var query = db.EntityManagerRoleAssignments.Where(x => x.EntityManager.ManagerId == userId);
var entityGroups = query.GroupBy(x => x.EntityManager.EntityId);
var result = entityGroups.ToDictionary(e => e.Key,
g => g.Select(x => x.RoleId).ToArray()
);
return result;
}
}
It sounds like you want to group by the Entity ID and project the associated role IDs to an array:
using (MyDb db = new MyDb())
{
var query = db.EntityManagerRoleAssignments.Where(x => x.EntityManager.ManagerId == userId);
var entityGroups = query.GroupBy(x => x.EntityManager.EntityId);
var result = entityGroups.ToDictionary(e => e.Key,
g => g.Select(x => x.RoleId).ToArray()
);
return result;
}

Categories