Change orderby depending on value of var - c#

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
}

Related

Reusing queries with Entity Framework Core

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

Search stops working when adding a second filter query

I'm hoping you can help me with an issue I'm having. I'm new to ElasticSearch and am currently trying to implement a log search method that's going to be filtering through large amounts of logs. Currently, I can make it filter based on selected log types and that works fine, but as soon as I add the second filter, it no longer returns any results.
The only difference between the two filters I've added so far is, one is an int array, the other is a string list as seen in the current code, I've also tried converting it to a string array but to no help.
[Route("logs/search")]
[HttpGet]
public ActionResult Index(int[] logTypes, int entityType, string entitySource)
{
using (var db = new mtaEntities())
{
var targetSources = new List<string>();
switch (entityType)
{
// Username/charactername
case 0:
var characters = db.characters.Where(i => i.charactername.ToLower().Replace("_", " ") == entitySource.ToLower()).ToList();
accounts user = null;
if (characters.Any())
{
var accountId = characters.FirstOrDefault().account;
if (accountId != null)
{
user = db.accounts.FirstOrDefault(i => i.id == accountId);
characters.AddRange(db.characters.Where(i => i.account == user.id));
}
}
else
{
user = db.accounts.FirstOrDefault(i => i.username.ToLower() == entitySource.ToLower());
if (user != null)
{
characters.AddRange(db.characters.Where(i => i.account == user.id).ToList());
}
}
if (user != null)
{
targetSources.Add("AC-" + user.id);
foreach (var item in characters)
{
targetSources.Add("CH-" + item.id);
}
}
break;
}
var filters = new List<Func<QueryContainerDescriptor<Log>, QueryContainer>>();
filters.Add(fq => fq.Terms(te => te.Field(fi => fi.LogType).Terms(logTypes)));
filters.Add(fq => fq.Terms(te => te.Field(fi => fi.SourceUser).Terms(targetSources)));
var searchDescriptor = new SearchDescriptor<Log>();
searchDescriptor.Query(q => q.Bool(b => b.Filter(filters)));
var query = ElasticClient.Search<Log>(searchDescriptor);
var results = query.Documents;
return View(new Logs
{
IndexLastRebuilt = db.settings.FirstOrDefault(i => i.name == "elasticIndexLastRebuilt").value,
LogResults = results.ToList(),
LogTypes = db.logtypes.ToList()
});
}
}

Entity Framework Eager Loading not working

I want to load a list of objects from db using Entity Framework 6 and Eager loading. But Entity Framework instead uses lazy loading. I've used SQL profiler and the queries are executed when a property referring to the child entities is accessed.
By using the 'Include' you suppose to load related entities in one go but it's not happening. My Code is presented below:
using (var flightsPricingRulesContext = new FlightsPricingRulesDbContext())
{
flightsPricingRulesContext.Configuration.ValidateOnSaveEnabled = false;
flightsPricingRulesContext.Configuration.AutoDetectChangesEnabled = false;
var filter = PredicateBuilder.True<FlightsPricingRulesDataAccess.Models.ServiceFee>();
if (!String.IsNullOrEmpty(selectedMarketId))
{
var selectedMarket = Int32.Parse(selectedMarketId);
filter = filter.And(sf => sf.MarketId == selectedMarket);
}
if (!String.IsNullOrEmpty(selectedApplicationTypeId))
{
var selectedAppplicationType = Int32.Parse(selectedApplicationTypeId);
filter = filter.And(sf => sf.ApplicationType == selectedAppplicationType);
}
var Query =
from P in flightsPricingRulesContext.ServiceFee.AsExpandable().Where(filter)select P;
switch (orderby)
{
case null:
case "":
case "Id":
Query = String.IsNullOrEmpty(orderbydirection) || orderbydirection == "ASC"
?Query.OrderBy(p => p.Id): Query.OrderByDescending(p => p.Id);
break;
case "market":
Query = String.IsNullOrEmpty(orderbydirection) || orderbydirection == "ASC"
? Query.OrderBy(p => p.MarketId)
: Query.OrderByDescending(p => p.MarketId);
break;
}
var takeitems = 10 ;
var skipitems = (Int32.Parse(page) - 1) * 10);
//BY USING INCLUDE EAGER LOADING IS ENABLED
Query = Query.Skip(skipitems).Take(takeitems).
Include(sf => sf.ServiceFeeZone.Select(sfz => sfz.Zone)).
Include(sf => sf.ServiceFeeCarrier).
Include(sf => sf.ServiceFeeClassOfService).
Include(sf => sf.ServiceFeeDate).
Include(sf => sf.ServiceFeeMarkUpAssignment).
Include(sf => sf.ServiceFeeAssignment);
var results = Query.ToList();//HERE A COMPLETE QUERY SHOULD BE
//SENT TO THE DB FOR RETRIEVING ENTITES INCLUDING THEIR CHILDREN
var totalresults = flightsPricingRulesContext.ServiceFee.AsExpandable().Count(filter);
var pagedservicefees = new PagedServiceFee();
pagedservicefees.totalitems = totalresults.ToString();
pagedservicefees.servicefees = new List<FlightsPricingRules.Models.ServiceFee>();
foreach (var servicefeedto in results)
{
var servicefee = new FlightsPricingRules.Models.ServiceFee();
servicefee.id = servicefeedto.Id.ToString();
servicefee.marketId = servicefeedto.MarketId.ToString();
//.....
//SOME MORE PROPERTIES
//
//CHILD ENTITIES
//Zones
servicefee.zones = new List<Zone>();
//HERE AN ADDITIONAL QUERY IS MADE TO LOAD THE CHILD ENTITIES-WHY?
foreach (var zonedto in servicefeedto.ServiceFeeZone)
{
var zone = new Zone();
zone.id = zonedto.ZoneId.ToString();
zone.name = zonedto.Zone.Name;
servicefee.zones.Add(zone);
}
//Carriers
servicefee.carriers = new List<Carr>();
//ALSO HERE AND ADDITIONAL QUERY IS MADE
foreach (var carrierdto in servicefeedto.ServiceFeeCarrier)
{
var carrier = new Carr();
carrier.id = carrierdto.AirlineId.ToString();
servicefee.carriers.Add(carrier);
}
pagedservicefees.servicefees.Add(servicefee);
}
//.......
//SOME MORE CHILD ENTITIES
//
return Json(pagedservicefees, JsonRequestBehavior.DenyGet);
}
OK, I figured it out. It turns out that it does matter where you place the Include statements. I placed the Include statements before the .AsExpandable() and after the parent entity and now eager loading is performed. I can also verify with SQL profiler, the query contains all the necessary joins and it's executed really fast. The correct query is now:
var Query = from P in flightsPricingRulesContext.ServiceFee
.Include(sf => sf.ServiceFeeCarrier)
.Include(sf=>sf.ServiceFeeAssignment)
.Include(sf => sf.ServiceFeeClassOfService)
.Include(sf => sf.ServiceFeeDate)
.Include(sf => sf.ServiceFeeMarkUpAssignment)
.Include(sf => sf.ServiceFeeZone.Select(zo => zo.Zone))
.AsExpandable().Where(filter) select P;
Posting the answer in case someone encounters a same scenario.

Entity Framework SQL statement

I want to query the table News from the database called StiriDB. There I want take all the entries that have in the Description field the word Stored in SearchTxt. I can't quite figure out what the SqlQuery method wants from me ... This is the code:
public IQueryable<News> GetProducts()
{
var _db = new SiteStiri.Models.NewsContext();
String SearchTxt = Convert.ToString(Request.QueryString["Cauta"]);
String queryTxt = "Select * from StiriDB.News where Description like '%" + SearchTxt + "%'";
IQueryable<News> query = _db.News.SqlQuery<News>(queryTxt);
if ("DesDate".Equals(DropDownSelect.SelectedItem.Value))
{
query = query.OrderByDescending(u => u.ReleaseDate);
}
if ("AsDate".Equals(DropDownSelect.SelectedItem.Value))
{
query = query.OrderBy(u => u.ReleaseDate);
}
if ("AsAlp".Equals(DropDownSelect.SelectedItem.Value))
{
query = query.OrderBy(u => u.NewsTitle);
}
if ("DesApl".Equals(DropDownSelect.SelectedItem.Value))
{
query = query.OrderByDescending(u => u.NewsTitle);
}
return query;
}
Additional details : GetProducts is called by a ListView. SearchTxt is taken with QueryString of Request because it's an URL attribute. The many ifs are for sorting the data in ascending and descending order based on certain criteria (the ifs work, I just need the SqlQuery to work as intended);
Use Linq to Entities query, something like...
public IQueryable<News> GetProducts()
{
var ctx = new SiteStiri.Models.NewsContext();
var query = from n in ctx.News
where n.Description.Contains(SearchTxt)
select n;
if ("DesDate".Equals(DropDownSelect.SelectedItem.Value))
{
query = query.OrderByDescending(u => u.ReleaseDate);
}
else if ("AsDate".Equals(DropDownSelect.SelectedItem.Value))
{
query = query.OrderBy(u => u.ReleaseDate);
}
else if ("AsAlp".Equals(DropDownSelect.SelectedItem.Value))
{
query = query.OrderBy(u => u.NewsTitle);
}
else if ("DesApl".Equals(DropDownSelect.SelectedItem.Value))
{
query = query.OrderByDescending(u => u.NewsTitle);
}
return query;
}

How can I write a LINQ to SQL query to update tags?

I have an image site where users can tag photos much like you can tag a question on Stackoverflow.
I have the following tables:
Images [ID, URL, etc]
Tags [ID, TagName]
ImageTag [TagID, ImageID]
I want to write a method with the signature:
public void UpdateImageTags(int imageId, IEnumerable<string> currentTags)
This method will do the following:
Create any new Tags in currentTags that don't already exist in the Tags table.
Get the old ImageTag's for an image.
Delete any ImageTag's that no longer exist in the currentTags
Add any ImageTag's that are new between the currentTags and oldTags.
Here is my attempt at that method:
public void UpdateImageTags(int imageId, IEnumerable<string> currentTags)
{
using (var db = new ImagesDataContext())
{
var oldTags = db.ImageTags.Where(it => it.ImageId == imageId).Select(it => it.Tag.TagName);
var added = currentTags.Except(oldTags);
var removed = oldTags.Except(currentTags);
// Add any new tags that need created
foreach (var tag in added)
{
if (!db.Tags.Any(t => t.TagName == tag))
{
db.Tags.InsertOnSubmit(new Tag { TagName = tag });
}
}
db.SubmitChanges();
// Delete any ImageTags that need deleted.
var deletedImageTags = db.ImageTags.Where(it => removed.Contains(it.Tag.TagName));
db.ImageTags.DeleteAllOnSubmit(deletedImageTags);
// Add any ImageTags that need added.
var addedImageTags = db.Tags.Where(t => added.Contains(t.TagName)).Select(t => new ImageTag { ImageId = imageId, TagId = t.TagId });
db.ImageTags.InsertAllOnSubmit(addedImageTags);
db.SubmitChanges();
}
}
However, this fails on the line:
db.ImageTags.DeleteAllOnSubmit(deletedImageTags);
With the error:
Local sequence cannot be used in LINQ to SQL implementations of query
operators except the Contains operator.
Is there an easier way I can handle the operation of adding new tags, deleting old ImageTags, adding new ImageTags in LINQ to SQL?
Seems like this would be easiest
public void UpdateImageTags(int imageId, IEnumerable<string> currentTags)
{
using (var db = new ImagesDataContext())
{
var image = db.Images.Where(it => it.ImageId == imageId).First()
image.Tags.Clear();
foreach(string s in currentTags)
{
image.Tags.Add(new Tag() { TagName = s});
}
db.SubmitChanges();
}
}
This might have to be modified slightly for LinqtoSQL. EF is what i have been using most recently. Also this is dependent on Lazy loading being enabled. If it is not, you will have to force the include of the image tags.
Here is a helper method to deal with many-to-many relationships:
public static void UpdateReferences<FK, FKV>(
this EntitySet<FK> refs,
Expression<Func<FK, FKV>> fkexpr,
IEnumerable<FKV> values)
where FK : class
where FKV : class
{
Func<FK, FKV> fkvalue = fkexpr.Compile();
var fkmaker = MakeMaker(fkexpr);
var fkdelete = MakeDeleter(fkexpr);
var fks = refs.Select(fkvalue).ToList();
var added = values.Except(fks);
var removed = fks.Except(values);
foreach (var add in added)
{
refs.Add(fkmaker(add));
}
foreach (var r in removed)
{
var res = refs.Single(x => fkvalue(x) == r);
refs.Remove(res);
fkdelete(res);
}
}
static Func<FKV, FK> MakeMaker<FKV, FK>(Expression<Func<FK, FKV>> fkexpr)
{
var me = fkexpr.Body as MemberExpression;
var par = Expression.Parameter(typeof(FKV), "fkv");
var maker = Expression.Lambda(
Expression.MemberInit(Expression.New(typeof(FK)),
Expression.Bind(me.Member, par)), par);
var cmaker = maker.Compile() as Func<FKV, FK>;
return cmaker;
}
static Action<FK> MakeDeleter<FK, FKV>(Expression<Func<FK, FKV>> fkexpr)
{
var me = fkexpr.Body as MemberExpression;
var pi = me.Member as PropertyInfo;
var assoc = Attribute.GetCustomAttribute(pi, typeof(AssociationAttribute))
as AssociationAttribute;
if (assoc == null || !assoc.DeleteOnNull)
{
throw new ArgumentException("DeleteOnNull must be set to true");
}
var par = Expression.Parameter(typeof(FK), "fk");
var maker = Expression.Lambda(
Expression.Call(par, pi.GetSetMethod(),
Expression.Convert(Expression.Constant(null), typeof(FKV))), par);
var cmaker = maker.Compile() as Action<FK>;
return cmaker;
}
Usage:
IEnumerable<Tag> values = ...;
Image e = ...;
e.ImageTags.UpdateReferences(x => x.Tag, tags);

Categories