I was having some performance issues using SharpRepository, and after playing around the SQL Query Profiler I found the reason.
With EF I can do stuff like this:
var books = db.Books.Where(item => item.Year == '2016');
if (!string.IsNullorEmpty(search_author))
books = books.Where(item => item.Author.Contains(search_author);
return (books.ToList());
EF will not really do anything until books is used (last line) and then it will compile a query that will select only the small set of data matching year and author from the db.
But SharpRepository evaluates books at once, so this:
var books = book_repo.Books.FindAll(item => item.Year == '2016');
if (!string.IsNullorEmpty(search_author))
books = books.Where(item => item.Author.Contains(search_author);
return (books.ToList());
will compile a query like "select * from Books where Year == '2016'" at the first line, and get ALL those records from the database! Then at the second line it will make a search for the author within the C# code... That behaviour can be a major difference in performance when using large databases, and it explains why my queries timed out...
I tried using repo.GetAll().Where() instead of repo.FindAll().... but it worked the same way.
Am I misunderstanding something here, and is there a way around this issue?
You can use repo.AsQueryable() but by doing that you lose some of the functionality that SharpRepository can provide, like caching or and aspects/hooks you are using. It basically takes you out of the generic repo layer and lets you use the underlying LINQ provider. It has it's benefits for sure but in your case you can just build the Predicate conditionally and pass that in to the FindAll method.
You can do this by building an Expression predicate or using Specifications. Working with the Linq expressions does not always feel clean, but you can do it. Or you can use the Specification pattern built into SharpRepository.
ISpecification<Book> spec = new Specification<Book>(x => x.Year == 2016);
if (!string.IsNullorEmpty(search_author))
{
spec = spec.And(x => x.Author.Contains(search_author));
}
return repo.FindAll(spec);
For more info on Specifications you can look here: https://github.com/SharpRepository/SharpRepository/blob/develop/SharpRepository.Samples/HowToUseSpecifications.cs
Ivan Stoev provided this answer:
"The problem is that most of the repository methods return IEnumerable. Try repo.AsQueryable(). "
Related
I want to create queries using EF FromSqlInterpolated or FromSqlRaw that allows me to use Like clauses, but I don't know what is the right way to do it without opening the application to SqlInjection attacks.
One first approach has took me to the following code
var results = _context.Categories.FromSqlInterpolated(
$"Select * from Category where name like {"%" + partialName + "%"}");
First test worked fine, it returns results when providing expected strings, and returns nothing when i provide something like ';select * from Category Where name='Notes'--%';
Still I don't know much about SqlInjection, at least not enough to feel safe with the query shown before.
Does someone know if the query is safe, or if there is a right way to do it?
Thanks
From this document
The FromSqlInterpolated and ExecuteSqlInterpolated methods allow using
string interpolation syntax in a way that protects against SQL injection attacks.
var results = _context.Categories.FromSqlInterpolated(
$"Select * from Category where name like {"%" + partialName + "%"}");
Or you can also change your query to Linq-to-Entity like this way
var results = _context.Categories.Where(p => p.name.Contains(partialName ));
This is very obvious but tricky question. I could not find its answer on web or simply i am missing keywords that could find its answer.
Let's say we have many conditions based on which we want to filter data. These conditions are in multiple blocks. How to write them so that they work as AND clause but not OR providing they participate only in certain condition.
var query = _entities.AsQueryable();
if (model.CityId != default)
{
query = query.Where(x => x.CityId == model.CityId);
}
if (!string.IsNullOrWhiteSpace(model.PostalCode))
{
query = query.Where(x => x.Proppostcode == model.PostalCode);
}
if (!string.IsNullOrWhiteSpace(model.AirportCode))
{
query = query.Where(x => x.AirportCode == model.AirportCode);
}
Let me know guys if this question need more details. Thank you!
am I missing keywords that could find its answer?
Yes, This missing keyword is Dynamic Expression or Dynamic Query. If you follow these keywords you'll find a lot of solutions to your problem.
As this tutorial said:
Dynamic Query allows you to perform dynamic where clause, select, order by, with string expression at runtime.
you can follow below links, too:
CodeProject
Microsoft
good luck.
I have an existing app that I am trying to upgrade from MVC5/EF6 to MVC6/EF7. We dynamically create some of our SQL tables, and as a result, have made use of the
System.Data.Entity.Database.SqlQuery
method to automatically map to entities that we use throughout our application.
This method seems to have gone away (i.e. not part of
Microsoft.Data.Entity.Infrastructure.Database ) in EF7 (or is not yet implemented). Are there plans to re-implement this method in EF7 or is there another way to accomplish this? Our project is kind of dead in the water until we figure this out.
Edited on May 20, 2015
I've been trying to make this work with FromSql, since that's what's available in Beta4, but no matter what combination of concatenated string, parameters I try, I keep getting different versions of an "Incorrect Syntax near #xxxvariable" message.
var results = Set<AssessmentResult>().FromSql("dbo.GetAssessmentResults #FieldA='Data1', #FieldB='Data2', #UserId = 2303");
var results2 = Set<AssessmentResult>().FromSql("dbo.GetAssessmentResults #FieldA= {0}", intData);
Both of these calls result in
"Incorrect syntax near '#FieldA'"
Any ideas?
We recently introduced the .FromSql() extension method on DbSet. It has the added benefit that you can continue composing LINQ on top of it.
var customers = db.Customers
.FromSql("SELECT * FROM Customer")
.Where(c => c.Name.StartsWith("A"));
I am writing a RIA service, which is also exposed using SOAP.
One of its methods needs to read data from a very big table.
At the beginning I was doing something like:
public IQueryable<MyItem> GetMyItems()
{
return this.ObjectContext.MyItems.Where(x => x.StartDate >= start && x.EndDate <= end);
}
But then I stopped because I was worried about the performance.
As far as I understand MyItemsis fully loaded and "Where" just filters the elements that were loaded at the first access of the property MyItems. Because MyItemswill have really lots of rows, I don't think this is the right approach.
I tried to google a bit the question but no interesting results came up.
So, I was thinking I could create a new instance of the context inside the GetMyItems method and load MyItems selectively. Something like:
public IQueryable<MyItems> GetMyItems(string Username, DateTime Start, DateTime End)
{
using (MyEntities ctx = new MyEntities ())
{
var objQuery = ctx.CreateQuery<MyItems>(
"SELECT * FROM MyItems WHERE Username = #Username AND Timestamp >= #Start AND Timestamp <= #End",
new ObjectParameter("#Username", Username),
new ObjectParameter("#Start", Start),
new ObjectParameter("#End", End));
return objQuery.AsQueryable();
}
}
But I am not sure at all this is the correct way to do it.
Could you please assist me and point out the right approach to do this?
Thanks in advance,
Cheers,
Gianluca.
As far as I understand MyItemsis fully loaded and "Where" just filters the elements that were loaded at the first access of the property MyItems.
No. That's entirely wrong. Don't fix "performance problems" until you actually have them. The code you already have is likely to perform better than the code you propose replacing it with. It certainly won't behave in the way you describe. But don't take my word for it. Use the performance profiler. Use SQL Profiler. And test!
So I have a model created in Entity Framework 4 using the CTP4 code first features. This is all working well together.
I am attempting to add an advanced search feature to my application. This "advanced search" feature simply allows the users to enter multiple criteria to search by. For example:
Advanced Product Search
Name
Start Date
End Date
This would allow the user to search by the product name and also limit the results by the dates that they were created.
The problem is that I do not know how many of these fields will be used in any single search. How then can my Entity Framework query be constructed?
I have an example describing how to create a dynamic query for Entity Framework, however this does not seem to work for the POCO classes I created for Code First persistence.
What is the best way for to construct a query when the number of constraints are unknown?
So after some hours of work on this problem (and some help from our friend Google) I have found a workable solution to my problem. I created the following Linq expression extension:
using System;
using System.Linq;
using System.Linq.Expressions;
namespace MyCompany.MyApplication
{
public static class LinqExtensions
{
public static IQueryable<TSource> WhereIf<TSource>(this IQueryable<TSource> source, bool condition, Expression<Func<TSource, bool>> predicate)
{
if (condition)
return source.Where(predicate);
else
return source;
}
}
}
This extension allows for a Linq query to be created like this:
var products = context.Products.WhereIf(!String.IsNullOrEmpty(name), p => p.Name == name)
.WhereIf(startDate != null, p => p.CreatedDate >= startDate)
.WhereIf(endDate != null, p => p.CreatedDate <= endDate);
This allows each WhereIf statement to only affect the results if it meets the provided condition. The solution seems to work, but I'm always open to new ideas and/or constructive criticism.
John,
Your solution is absolutely awesome! But, just to share, I have been using this method above until I see your ideia.
var items = context.Items.Where(t => t.Title.Contains(keyword) && !String.IsNullOrEmpty(keyword));
So, it seems not to be the best solution for this, but for sure it is a way around.