Following on from previous questions using EF5 Code First, SQL Server 2008 R2 db, Generic Repository and Unit Of Work patterns
I have the following function
public static void MoveGraphicPosition(int queueId, int graphicId, bool moveUp)
{
using (var unitOfWork = new GraphicUnitOfWork(ConnGraphics, false))
{
var sourceGraphic = unitOfWork.GraphicRepository.FindSingle(g => g.Id == graphicId);
if (sourceGraphic == null) return;
var startPosition = sourceGraphic.QueuePosition;
Graphic targetGraphic;
if (moveUp)
{
targetGraphic =
unitOfWork.PlayoutQueueRepository.FindSingle(q => q.Id == queueId, q => q.Graphics)
.Graphics.Where(g => g.QueuePosition < startPosition)
.OrderByDescending(g => g.QueuePosition)
.Take(1).FirstOrDefault();
}
else
{
targetGraphic =
unitOfWork.PlayoutQueueRepository.FindSingle(q=> q.Id == queueId, q => q.Graphics)
.Graphics.Where(g => g.QueuePosition > startPosition)
.OrderBy(g => g.QueuePosition)
.Take(1).FirstOrDefault();
}
// Swop the positions
if (targetGraphic == null) return;
sourceGraphic.QueuePosition = targetGraphic.QueuePosition;
targetGraphic.QueuePosition = startPosition;
unitOfWork.GraphicRepository.Update(sourceGraphic);
unitOfWork.GraphicRepository.Update(targetGraphic);
// Save to database
unitOfWork.Save();
}
}
Running this method via a Web Api call it takes about 2 seconds to run, I am bit puzzled as to why it takes this long, was expecting less than a second, is there any advice as to speed this up.
All we are trying to do is change queue positions of two graphic objects - swop queue positions around - our current one with the next one in the queue based on position.
Not sure whether this is EF5 or my LINQ query being inefficient.
FindSingle on the repository looks like this
public T FindSingle(Expression<Func<T, bool>> predicate = null, params Expression<Func<T, object>>[] includes)
{
var set = FindIncluding(includes);
return (predicate == null) ? set.FirstOrDefault() : set.FirstOrDefault(predicate);
}
public IQueryable<T> FindIncluding(params Expression<Func<T, object>>[] includeProperties)
{
IQueryable<T> set = _context.GetEntitySet<T>();
if (includeProperties != null)
{
foreach (var include in includeProperties)
{
set = set.Include(include);
}
}
return set.AsQueryable();
}
I have a feeling might have to change this to take two objects in and then just do the update but this may be masking a problem which we will have to address at a later date as this should be straightforward.
It was the notification service we have setup on the save call (so other services/apps know what has been changed on the database) - this was causing an exception which increased the delay, once we put in the correct IP, the delay was around 0.7ms which is what I was expecting
Related
I'm using Entity Framework Core 6 and I want to find a series of entities in a DbSet. The entities I want to obtain are the ones match some properties in a list of input objects.
I've tried something like this:
public IEnumerable<MyEntity> FindEntities(IEnumerable<MyEntityDtos> entries)
{
return dbContext.MyDbSet.Where(r => entries.Any(e => e.Prop1 == r.Prop1 && e.Prop2 == r.Prop2));
}
But I get the classic EF Core exception saying that my LINQ cannot be translated to a database query (the problem in particular is the entries.Any(...) instruction)
I know I can just loop over the list of entries and obtain the entities one by one from the DbSet, but that is very slow, I was wondering if there was a more efficient way to do this in EF Core that I don't know about.
I think this should work:
public IEnumerable<MyEntity> FindEntities(IEnumerable<MyEntityDtos> entries)
{
var props1=entries.Select(x=>x.Prop1).ToArray();
var props2=entries.Select(x=>x.Prop2).ToArray();
return dbContext.MyDbSet.Where(r => props1.Contains(r.Prop1) && props2.Contains(r.Prop2));
}
In the end, I've done this:
public static IEnumerable<MyEntity> GetRangeByKey(this DbSet<MyEntity> dbSet, IEnumerable<MyEntity> toFind)
{
var keys = new HashSet<string>(toFind.Select(e => e.Id));
IEnumerable<MyEntity> result = null;
for (int i = 0; i < keys.Length; i += 1000)
{
var keyChunk = keys[i..(Math.Min(i + 1000, keys.Length))];
var res = dbSet.Where(x => keyChunk.Any(k => x.ResourceArn == k));
if (result == null)
{
result = res;
}
else
{
result = result.Concat(res);
}
}
return result;
}
Basically I get the keys to find in a HashSet and use it to perform a Where query, which will be translated to a SQL IN clause which is quite fast. I do it in chunks because there's a maximum number of values you can put in a IN clause before the DB engine refuses it.
I am doing chaining LINQ queries as show below. I am trying to find out the cause for the slowness of query.ToList();. The SQL queries are fast (milliseconds), but the code takes a minute. The reason for chaining is to reuse the repository function.
Is there any obvious reasons for slowness here?
How could I optimize this ?
How can I check the actual SQL query executed when
running query.ToList();?
//Client
var query = _service.GetResultsByStatus(status, bType, tType);
var result = query.ToList(); //takes a long time to execute
//Service function
public IEnumerable<CustomResult> GetResultsByStatus(string status, string bType, string tType) {
IEnumerable<CustomResult> result = null;
result = repo.GetResults(bType).Where(item => item.tStatus == status && (tType == null || item.tType == tType))
.Select(item => new CustomResult {
A = item.A,
B = item.B,
});
return result;
}
// Repository Function (reused in many places)
public IEnumerable<my_model> GetResults(string bType) {
return from p in dbContext.my_model()
where p.bType.Equals(bType)
select p;
}
Your .Where(item => item.tStatus == status && (tType == null || item.tType == tType)) and the .Select are being done "locally" on your PC... Tons of useless rows and columns are being returned by the SQL to be then "filtered" on your PC.
public IEnumerable<my_model> GetResults(string bType) {
return from p in dbContext.my_model()
where p.bType.Equals(bType)
select p;
}
Change it to
public IQueryable<my_model> GetResults(string bType) {
Normally IEnumerable<> means "downstream LINQ will be executed locally", IQueryable<> means "downstream LINQ will be executed on a server". In this case the Where and the Select are "downstream" from the transformation of the query in a IEnumerable<>. Note that while it is possible (and easy) to convert an IQueryable<> to an IEnumerable<>, the opposite normally isn't possible. The AsQueryable<> creates a "fake" IQueryable<> that is executed locally and is mainly useful in unit tests.
Is there a way to avoid multiple IF/CASE statements in C#?
In my app I will end up with 8+ fields used to create a Linq query where every expression can be null or != null so it will give me 64 scenarios.
I'm not providing any code samples because I can do it using IF/CASE and simplify it as much as I can.
If You are familiar with some useful approaches to that problem I will appreciate any advice.
Code Sample (it only includes two delegates but I'll have to add more to filter data)
Repository
public virtual IEnumerable<T> Get(Expression<Func<T, bool>> filter = null, Expression<Func<T, bool>> filter1 = null)
{
IQueryable<T> query = dbSet;
if (filter != null)
{
query = query.Where(filter);
return query.ToList();
}
if (filter1 != null)
{
query = query.Where(filter1);
return query.ToList();
}
else
{
return query.ToList();
}
}
Controller
public ActionResult Index(string searchFullName, string searchExtension)
{
var extensionList = new List<string>();
var extensions = from n in unitofwork.DomainRepository.Get()
select n.Extension;
extensionList.AddRange(extensions.Distinct());
ViewBag.searchExtension = new SelectList(extensionList);
if (!String.IsNullOrEmpty(searchFullName) && !String.IsNullOrEmpty(searchExtension))
{
return View(unitofwork.DomainRepository.Get(n => n.Extension == searchExtension && n.Name.Contains(searchFullName)));
}
else if (!String.IsNullOrEmpty(searchExtension))
{
return View(unitofwork.DomainRepository.Get(n => n.Extension == searchExtension));
}
else if (String.IsNullOrEmpty(searchFullName) && String.IsNullOrEmpty(searchExtension))
{
return View(unitofwork.DomainRepository.Get());
}
else if (!String.IsNullOrEmpty(searchFullName) && String.IsNullOrEmpty(searchExtension))
{
return View(unitofwork.DomainRepository.Get(n => n.Name.Contains(searchFullName)));
}
else
{
return View(unitofwork.DomainRepository.Get());
}
}
Yes it is possible, using Linq's powerful Aggregate method (a version of the fold function from functional programming).
public virtual IEnumerable<T> FilterOnAll(params Expression<Predicate<T> filters)
{
return filters.Aggregate(dbSet, (acc, element) => acc.Where(element));
}
public virtual IEnumerable<T> FilterOnAny(params Expression<Predicate<T> filters)
{
Expression<Predicate<T>> alwaysTrue = _ => true;
var compositeFilter = filters.Aggregate(alwaysTrue, (acc, element) => acc.Or(element));
return dbSet.Where(compositeFilter);
}
You can then compose these two builders to create pretty much any logical condition you want from within your controller.
Good luck.
What you can do is an or statement in your where clause
where (variable1 == null || b.data = variable1)
and just do that for all 8 if im understanding your problem if its null it passes over it as true else it checks against the data.
Your entire if .. elseif .. else block can be replaced by:
return View(unitofwork.DomainRepository.Get(n =>
(string.IsNullOrEmpty(searchFullName) || n.Name.Contains(searchFullName))
&& (string.IsNullOrEmpty(searchExtension) || n.Extension == searchExtension)));
I was working this weekend on parallelizing a section of code using Tasks to run all the queries I needed for a dashboard page.
What I have now is many copy/paste methods with almost exactly the same query and a different line at the very end of the method.
Is there a way to write a query against one object context then detach it and pass to a method?
I want to do something like this:
using(DbContext db = new DbContext)
{
var query = db.cars.where(x => x.make == "Ford");
int handleCounts = getDoorHandleCounts(query);
}
public int getDoorHandleCounts(type? query)
{
using(DbContext db = new DbContext())
{
return query.where(x => x.partType == "DoorHandle").Count();
}
}
Any ideas?
Keep in mind all my count() methods are launched from a Task array so they'll be running in parallel. I need a new object context to run each count query on.
I've done some googling and thought about trying to use a pre-compiled query and call it from different object context's, but my real query is kind of complex with allot of if blocks to determine the where condition. Can you compile a query that isn't really simple?
You can't detach and attach a query from/to a context. However, you could reuse the first expression:
Expression<Func<Car,bool>> InitialSelection()
{
return x => x.make == "Ford";
}
public int GetDoorHandleCounts()
{
using(DbContext db = new DbContext())
{
return db.Cars()
.Where(InitialSelection())
.Where(x => x.partType == "DoorHandle").Count();
}
}
And in your task:
int handleCounts = getDoorHandleCounts();
This only works if the initial query is "simple", i.e. does not contain joins and predicates on joined sets that you should repeat over and over in each getX method.
As an alternative, you could initialize a context and feed it to a method that returns a query:
public IQueryable<Car> GetInitialQuery(DbContext db)
{
return db.Cars().Join(....)
.Where(x => x.make == "Ford")
.Where(....);
}
public int GetDoorHandleCounts()
{
using(DbContext db = new DbContext())
{
return GetInitialQuery(db)
.Where(x => x.partType == "DoorHandle").Count();
}
}
Maybe I'm misunderstanding the question, but wouldn't this do what you're looking for?
using(DbContext db = new DbContext)
{
var carsResult = db.cars.where(x => x.make == "Ford");
int handleCounts = getDoorHandleCounts(carsResult);
}
public int getDoorHandleCounts(IEnumerable<Car> result)
{
return result.where(x => x.partType == "DoorHandle").Count();
}
Edit: never mind, I only now saw your mention of the Task array.
Change
public int getDoorHandleCounts(type? query)
to
public int getDoorHandleCounts(IQueryable<cars> query)
And replace cars with the of objects the query will return.
Edit
I would just recommend passing value you're looking to filter on, like this:
{
...
int handleCounts = getDoorHandleCounts("Ford");
}
public int getDoorHandleCounts(string Make)
{
using(DbContext db = new DbContext())
{
return db.cars.where(x => x.make == Make && x.partType == "DoorHandle").Count();
}
}
I have a collection of Obj's, I want to go through the collection, and set a property if a condition is true, so in normal world it would be:
foreach (var o in obj)
{
if (o.SomeProperty == Something)
{
o.SomeOtherProperty = true;
}
}
Anyway to do this, using Linq, to make it in a single line?
LINQ isn't all that useful for executing side effects, it's primarily intended for querying. In truth, the fact that it has deferred execution so engrained in its behaviour makes it a poor choice for executing side-effects, IMO.
The code you've got looks perfectly fine to me. If you did want to use LINQ though, I doubt you could improve much on:
foreach (var o in obj.Where(i => i.SomeProperty == Something))
{
o.SomeOtherProperty = true;
}
Now, that isn't all that better (arguably worse) than your original.
On the other hand, if you wanted to create a new, streaming sequence with projections of the original items having the desired characteristics, you could do something like:
var projection = obj.Where(i => i.SomeProperty == Something)
.Select(i => new Foo(i) { SomeOtherProperty = true });
// assuming your type has a copy-constructor
EDIT: If you don't want to heed the advice of the experts (read: Eric Lippert), you can write your own extension-method:
public static void Do<T>(this IEnumerable<T> source, Action<T> action)
{
if (source == null)
throw new ArgumentNullException("source");
if (action == null)
throw new ArgumentNullException("action");
foreach (T item in source)
action(item);
}
This will allow you to do:
obj.Where(o => o.SomeProperty == Something)
.Do(o => o.SomeOtherProperty = true);
obj.Where(i => i.SomeProperty == Something).ToList().ForEach(o => o.SomeOtherProperty = true);
Using an extension method:
public static int UpdateOnPredication<T>(this IEnumerable<T> source, Func<T, bool> predicate, Action<T> update)
{
//check the parameters here if (source==null) ...
var query = source.Where(predicate);
foreach (var item in query)
{
update(item);
}
return query.Count();
}
Usage:
results.UpdateOnPredication(x => x.ID > 1000, x => x.Status = 1);