C# Entity Framework : include with string parameter versus include with lambda expression [duplicate] - entity-framework

I am using entity framework and I want to execute a query and would like to know which is the besr way to execute a query. Which is best practice and why, and which one is more per-formant.
Option 1)
return
this.Storage.Customer.OfType<Preferred>()
.Include("Order")
.Where("it.Id = #customerId AND it.CustomerType = #cusType", new ObjectParameter("customerId ", customerId), new ObjectParameter("cusType", (int)cusType))
.Execute(MergeOption.OverwriteChanges)
.SingleOrDefault();
OR
return
this.Storage.Customer.OfType<Preferred>()
.Include(b => b.Order)
.Where(cust => cust.Id == customerId && cust.CustomerType== (int)cusType)
.SingleOrDefault();
The second question is why in option 2 us the .Execute not available? It appears red.
Thanks in advance.

The performance difference should be negligible compared with the actual data access, but you need to measure it to determine for sure.
Include with a lambda just uses reflection to get the property name then calls the version with a string parameter, so the only overhead is parsing the expression. (This is an implementation detail, however, so it is subject to change)
The benefit of using a lambda is type safety - using the wrong property name in a lambda will break the build, but using the wrong string will only fail at run-time.
The reason Execute is not available is because Include with a lambda parameter is an extension method on IQueryable<T> that returns an IQueryable<T> in order to chain methods like Where. Include with a string parameter is a method on ObjectQuery<T> that returns an ObjectQuery<T>. Execute is a method on ObjectQuery<T>, not IQueryable<T> so it is not available when you use IQueryable methods.

Related

How can a variable be used during the setting of an Entity Framework entity (versus hardcoding)?

This works:
string sql = $"SELECT * FROM {tableName}";
var executeSQL = db.Set<MyTableContextType>().FromSqlRaw(sql).ToList();
But this code will be accepting requests for multiple tables (past MyTableContextType). Rather than hardcoding MyTableContextType, I'd like to do something like this:
dynamic entityType = db.Model.FindEntityType(stringMyType);
string sql = $"SELECT * FROM {tableName}";
var executeSQL = db.Set<entityType>().FromSqlRaw(sql).ToList();
All of the above works, except the last line which gives this error:
'entityType' is a variable but is used like a type.
How can a variable be fed into db.Set<>?
If there is a better way, I am open to that too.
.NET Core 3.1
& EF Core 3.1
It won't work since Generics is a compile-time concern, but type from FindEntityType is a runtime concern.
In your case, extracting db.Set<MyTableContextType>().FromSqlRaw(sql).ToList() to generic method may help.
Something like
private TEntity SelectAll<TEntity>(string tableName) => db.Set<TEntity>().FromSqlRaw($"SELECT * FROM {tableName}").ToList();
Alternative way to solve it - use LINQ to SQL methods instead of raw SQL in order to select the data.

Extend DbFunctions EF6

Is it possible to extend DbFunctions, assuming you are using existing DbFunctions as helper methods. I am essentially rewriting the exact same line of sql code again and again. Are there any alternatives?
Update:
Here is an example of what I'm trying to do, but I want to define my own Add function, not use one that I've constructed in my database
var locations = context.Data.Where(e => Functions.Add(e.X, e.Y) >= 10)
Yes of course you can extend it. Class DbFunctions only contains code that helps the provider of your IQueryable to translate the expression into SQL.
An IEnumerable holds all code to create an Enumerator for you upon request. The Enumerator represents a sequence. Upon request it will give you the first element of the sequence, and once you've got an element it will give you the next one (provided there is one).
An IQueryable works differently. Usually an IQueryable is not meant to be performed by your process, but for instance a database, a remote web site, a CSV file controller, etc.
That is why you need to tell an object that produces IQueryables for which process it must create the IQueryable. In case of Entity Framework you inform the DbContext which database to use.
The IQueryable object holds an Expression to be performed and a Provider. The provider knows which process will perform the query. It also knows how to translate the Expression into the format that the other process understands. Quite often this will be SQL.
If you investigate the remarks section of the MSDN descriptions of IQueryable functions like Where, GroupBy, Select, you'll see that most of these functions will only change the Expression.
As long as you don't ask for the Enumerator, usually implicitly by asking for the first element of a sequence, like in ToList, foreach, FirstOrDefault, etc, the Provider has nothing to do.
But once you ask for the Enumerator, the Expression will be translated by the Provider, who will use the translation to query the data from the other process and create an Enumerator object that can give you the first element of the sequence, and the next ones.
DbFunctions are used when the Provider translates the Expression into SQL. If you create a Queryable with DbFunctions and in your debugger look at the created Expression, you'll still find the used DbFunctions.
The DbFunctions only translate the input into SQL. If does not perform the query itself. The translation is done in local memory.
Having understood this, you can use any function as long as it only changes the Expression into new Expressions into formats that your provider understands.
This means you can't use any of your own functions, or classes. There are even several LINQ functions you can't use
See supported and non-supported LINQ methods
However, if your extension functions input an IQueryable and output an IQueryable, then your extension function will only change the Expression. As long as you fill the Expression with supported LINQ methods you're fine
So if you want to extend IQueryable with a function that returns an IQueryable containing only the invoice that are due to day:
public static IQueryable<Invoice> WhereDueToday(this IQueryable<Invoice> invoices)
{ // returns all invoices that must be paid today
return invoices
.Where(invoice => DbFunctions.TruncateTime(invoice.DueDate) == DateTime.Today);
}
Usage:
IQueryable<Invoice> invoices = dbContext.Invoices
.Where(invoice => ..);
IQueryable<Invoice> invoicesDueToDay = invoices
.WhereDueToday();
You can define a method that returns an Expression and use that in your where clause. Since you want to pass different properties of the object in you can't just write one expressions
public Expression<Func<T, bool>> MyFunc<T>(Expression<T, int> property1, Expression<T, int> property2, int greaterThan)
{
// Build expression tree
}
I realise "Build Expression tree" isn't hugely useful, but if you don't really want to do add writing out the code to build "add" isn't going to help you either.
If there are just a couple of combinations it might be easier to just hard code for those
public Expression<Func<T, bool>> MyFunc<T>(PropertiesEnum p, int greaterThan)
{
switch(p)
{
case (p.XandY):
return item => (item.X + item.Y) > greaterThan;
case (p.XandZ):
return item => (item.X + item.Z) > greaterThan;
case (p.YandZ):
return item => (item.X + item.Z) > greaterThan;
// other cases
}
}
You would call this like:
var locations = context.Data.Where(MyFunc(PropertiesEnum.XandY, 10));

MongoDB C# Select specific columns

I know that MongoDb C# driver doesn't support projections so I searched a little bit and I found that many people uses a mongoCursor to perform such queries, I'm trying to select only specific fields and my code is the following:
public T GetSingle<T>(Expression<Func<T, bool>> criteria,params Expression<Func<T, object>>[] fields) where T : class
{
Collection = GetCollection<T>();
return Collection.FindAs<T>(Query<T>.Where(criteria)).SetFields(Fields<T>.Include(fields)).SetLimit(1).SingleOrDefault();
}
I got and custom repository for users on top of that:
public User GetByEmail(string mail, params Expression<Func<User, object>>[] fields)
{
return GetSingle<User>(x=>x.Email==mail,fields);
}
this is the usage:
_repository.GetByEmail(email, x=>x.Id,x=>x.DisplayName,x=>x.ProfilePicture)
but I'm getting the fields included in the parameter but also all the Enums,dates and Boolean values that are part of the class User, the values that are string and not included in the field list are null so that's fine
what can I do to avoid that?
By using SetFields, you can specify what goes through the wire. However, you're still asking the driver to return hydrated objects of type T, User in this case.
Now, similar to say an int, enum and boolean are value types, so their value can't be null. So this is strictly a C#-problem: there is simply no value for these properties to indicate that they don't exist. Instead, they assume a default value (e.g. false for bool and 0 for numeric types). A string, on the other hand, is a reference type so it can be null.
Strategies
Make the properties nullable You can use nullable fields in your models, e.g.:
class User {
public bool? GetMailNotifications { get; set; }
}
That way, the value type can have one of its valid values or be null. This can, however, be clumsy to work with because you'll have to do null checks and use myUser.GetMailNotifications.Value or the myUser.GetMailNotifications.GetValueOrDefault helper whenever you want to access the property.
Simply include the fields instead this doesn't answer the question of how to it, but there are at least three good reasons why it's a good idea to include them:
When passing a User object around, it's desirable that the object is in a valid state. Otherwise, you might pass a partially hydrated object to a method which passes it further and at some point, someone attempts an operation that doesn't make sense because the object is incomplete
It's easier to use
The performance benefit is negligible, unless you're embedding huge arrays which I would suggest to refrain from anyway and which isn't the case here.
So the question is: why do you want to make all the effort of excluding certain fields?

IQueryable doesn't implement IDbAsyncEnumerable

The question was originally asked at http://entityframework.codeplex.com/discussions/399499#post928179 .
Good day! Please tell me if it is wrong place to post this question.
I have a query as follows:
IQueryable<Card> cardsQuery =
dataContext.Cards
.Where(predicate)
.OrderByDescending(kc => kc.SendDate)
.AsQueryable();
Then I try:
Task<Card[]> result = cardsQuery.ToArrayAsync();
And then exception rises:
The source IQueryable doesn't implement IDbAsyncEnumerable<Models.Card>
I use modified version of 'EF 5.x DbCotext generator'.
How to avoid it?
UPDATE
Important remark is that I have method to produce IQuerayble<Card> as follows:
class Repository {
public IQueryable<Card> GetKudosCards(Func<Card, bool> predicate) {
IEnumerable<KudosCard> kudosCards = kudosCardsQuery.Where(predicate);
return kudosCards
.OrderByDescending(kc => kc.SendDate)
.AsQueryable();
}
}
What is the point of calling AsQueryable? If you compose a query with the extension methods starting from an IQueryable source collection (e.g. DbSet, ObjectSet), the query will be IQueryable too.
The purpose of AsQueryable is to wrap an IEnumerable collection with an IQueryable proxy/adapter that uses a Linq provider that is capable of compiling IQueryable queries into a Linq to Object queries. This can be useful in scenarios when you would like to use inmemory data queries.
Why is the AsQueryable call necessary? What if you just simply remove it?
Update
Okey, now it seems I understand your problem. After a quick look on the ODataQueryOptions.ApplyTo I realized that it just extends the underlying expression tree of the query. You can still use it to run the query in the way you want, however you need a little trick to transform the query back to generic.
IQueryable<Card> cardsQuery =
dataContext.Cards
.Where(predicate)
.OrderByDescending(kc => kc.SendDate);
IQueryable odataQuery = queryOptions.ApplyTo(cardsQuery);
// The OData query option applier creates a non generic query, transform it back to generic
cardsQuery = cardsQuery.Provider.CreateQuery<Card>(odataQuery.Expression);
Task<Card[]> result = cardsQuery.ToArrayAsync();
The problem is as follows.
I have a method:
class Repository {
public IQueryable<Card> GetKudosCards(Func<Card, bool> predicate) {
IEnumerable<KudosCard> kudosCards = kudosCardsQuery.Where(predicate);
return kudosCards
.OrderByDescending(kc => kc.SendDate)
.AsQueryable();
}
}
The problem is that kudosCards has type IEnumerable<KudosCard>. That throws exception. If I change predicate type to Expression<Func<Card, bool> predicate then everything works just fine.
I had the same problem when I was using the LinqKit library expression builder, which in the end, was producing AsQueryable(), and very surprisingly it was happening for me from the XUnit Integration Tests call.
I was going wild about why the same problem wasn't happening when calling the same API endpoint via Swagger.
It turned out I had to do an elementary change.
I had to replace:
using System.Data.Entity;
with:
using Microsoft.EntityFrameworkCore;
I had the same problem when I was using the LinqKit library expression builder, which in the end, was producing AsQueryable().
And very surprisingly, it was happening for me from the XUnit Integration Tests call.
I was going wild about why the same problem wasn't happening when calling the same API endpoint via Swagger.
It turned out I had to do an elementary change.
I had to replace the:
using System.Data.Entity;
with:
using Microsoft.EntityFrameworkCore;
In the place where I was calling the LinqKit expression method.
If you want to use the List<T> for Linq query, Avoid chaining it with ToListAsync(). It uses the class IDbAsyncEnumerable that is not available to a normal List<T> object

Returning an EF model class

I can successfully return a model from my controller like this:
return View(lemonadedb.Messages.ToList() );
It's interpreted perfectly by my view.
Now I only want to show the messages where Messages.user == Membership.GetUser().ToString().
But when I do this:
return View(lemonadedb.Messages.Where( p => p.user == Membership.GetUser().ToString()).ToList());
I get:
'LINQ to Entities does not recognize
the method 'System.String ToString()'
method, and this method cannot be
translated into a store expression.'
I need some way to narrow down the results of the messages table.
Should I use the find() method somehow? I thought it was only for ID's.
How should I do this?
The reason you're having this issue is that Entity Framework is trying to evaluate the expression Membership.GetUser().ToString() into an SQL query. You need to create a new variable to store the value of this expression and pass it into your query. Entity Framework will then just interpret this as you expect.
The following should work:
var user = Membership.GetUser().ToString();
return View(lemonadedb.Messages.Where(p => p.user == user).ToList());
I suspect this is a very common mistake that people make when writing Entity Framework queries.