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.
Related
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.
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));
So, I've got a "Speaker" class, with normal properties, and a plain old method like this:
public string FullNameFirstLast()
{
return FirstName + " " + LastName;
}
I'm using Entity Framework Code First Magic Unicorn edition, and I get this error:
System.NotSupportedException: LINQ to
Entities does not recognize the method
'System.String FullNameFirstLast()'
method, and this method cannot be
translated into a store expression.
Which seems odd, since it is a method and not a property, so I wonder why EF would care about it anyway... So I tried telling EF to ignore it like so:
modelBuilder.Entity<Speaker>()
.Ignore(ignore => ignore.FullNameFirstLast())
;
Which doesn't help, because now I get this error message instead:
System.InvalidOperationException: The
expression 'ignore =>
ignore.FullNameFirstLast()' is not a
valid property expression. The
expression should represent a
property: C#: 't => t.MyProperty'
VB.Net: 'Function(t) t.MyProperty'.
How do I do this? I'm assuming that I can have methods on my model objects with EF, right??
It is not a problem of mapping. It is problem of using the method in Linq-to-entities query. You can't use custom methods or custom properties in Linq-to-entities query because EF provider doesn't know how to translate them to SQL.
Basically I'd be looking to implement a method like this.
IQueryAble GetQuery<T>(Entities db) or extension method Entities.GetQuery<T>()
This way you could do things like this
public IQueryable<T> GetAll()
{
return yourEntityClasses.GetQuery<T>();
}
which would return a SELECT * FROM query expression and obviously from there you could make addtional generic methods for sorting, pagination, where expressions, etc on top of this would save you from having to repeat the code for these methods for each table. I know SubSonic3 does a very good job of this, but was trying to duplicate some of the functionality in an EntityFramework project I am working on. Only thing I see in EF is CreateQuery and ObjectQuery but both of those require you to pass a querystring in which would require you to know the table name.
Well it is possible to get the string that you need to put into the CreateQuery method automatically.
Basically it is just string.Format("[{0}]",entitySetName);
How do you get the entity set name, assuming you have don't use something called MEST, most people don't, you can use some a functions I wrote in this Tip to get the EntitySet for T and from that the name.
Once you do that it should be pretty trivial for you to write the Extension Method
i.e.
public static IQueryable<T> GetAll<T>(this ObjectContext context)
{
var wkspace = context.MetadataWorkspace;
EntitySet set = wkspace
.GetEntitySets(wkspace.GetCSpaceEntityType<T>())
.Single();
return context.CreateQuery<T>(string.Format("[{0}]",set.Name);
}
See the above tip for the other functions used.
Hope this helps
Alex
I am using the entity framework and I'm having a problem with "re-finding" objects I just created... basically it goes like this:
string theId = "someId";
private void Test()
{
using(MyEntities entities = new MyEntities())
{
EntityObject o = new EntityObject();
o.Id = theId;
entities.AddToEntityObject(o);
CallSomeOtherMethod(entities);
}
}
void CallSomeOtherMethod(MyEntities ents)
{
EntityObject search = ents.EntityObject.FirstOrDefault(o => o.Id == theId);
if(search == null)
{
Console.WriteLine("wha happened???");
}
}
(no guarantee the code works btw - it's all from my head)
Why doesn't the query "find" the EntityObject that was just created?
If I call SaveChanges() after the AddToEntityObject it works (which doesn't surprise me) but why doesn't it pull from the cache properly?
I'm still green on this stuff so I'm hoping that there's some really easy thing that I'm just overlooking...
Thanks
The newly added object is in the local DataSource, since it's not persisted yet in the database,
so you may say:
EntityObject search = ents.EntityObject.FirstOrDefault(o => o.Id == theId) ??
ents.EntityObject.Local.FirstOrDefault(o => o.Id == theId);
This happens because ents.EntityObject.WhatEver always queries the datasource. This is a design decision. They do it this way, because else they would have to execute the query against the datasource, against the local cache and then merge the results. As one of the developers pointed out in a blog (cannot remember where exactly) they were unable to handle this consistently.
As you can imagine there are a lot of corner an edge cases you have to handle properly. You could just find a id you created locally, created by someone else in the database. This would force you to be prepared to handle conflicts on (almost) every query. Maybe they could have made methods to query the local cache and methods to query the datasource, but that is not to smart, too.
You may have a look at Transparent Lazy Loading for Entity Framework. This replaces the normal code generator and you get entities that populate their related entity collections and entity references automatically on access. This avoids all the
if (!Entity.ReleatedEntities.IsLoaded)
{
Entity.RelatedEntities.Load();
}
code fragments. And you can query the collections because they are always implicitly loaded. But this solution is not perfect, too. There are some issues. For example, if you create a new entity and access a collection of related entities, you will get an exception because the code is unable to retrieve the related entities from the database. There is also an issue concerning data binding and may be some more I am not aware of.
The good thing is that you get the source code and are able to fix the issues yourself and I am going to examine the first issue if I find some time. But I am quite sure that it will not be that easy to fix, because I expect some case were just not hitting the database if the entity has just been created is not the expected behavior.
I was in the same situation. I wrote this extension method that at least for me solves the problem (I don't have issues with i.e conflicts in my context...)
public static IEnumerable<T> WhereInclAdded<T>(this ObjectSet<T> set, Expression<Func<T, bool>> predicate) where T : class
{
var dbResult = set.Where(predicate);
var offlineResult = set.Context.ObjectStateManager.GetObjectStateEntries(EntityState.Added).Select(entry => entry.Entity).OfType<T>().Where(predicate.Compile());
return offlineResult.Union(dbResult);
}
The extension method bellow is to DbSet<>
public static T TryAttach<T>(this DbSet<T> dbSet, T entity, Expression<Func<T, bool>> predicate) where T : class
{
T found = dbSet.Local.SingleOrDefault(predicate.Compile());
if (found == null) dbSet.Attach(entity);
return found ?? entity;
}
How to use:
contextInstance.MyEntity.TryAttach(entityInstance, e => e.ID == entityInstance.ID);
btw: I love generics!
I have recently struggled with this same question. I'm posting this answer 2 years after the question was asked in hopes that this bit of code may help someone searching for an answer.
I have basically implemented an extension method (as suggested by Alex James) called "Find" that operates in the same way that "Where" does, but "Find" also checks the ObjectContext to see if there are any Added entities that satisfy the given predicate. This allows you to find an entity even if it hasn't been saved to the database yet.
Find returns an IQueryable(of T) so that you can use it just like any other LINQ operator.
<Extension()>
Public Function Find(Of T As Class)(ByVal OSet As ObjectSet(Of T), _
ByVal predicate As Expression(Of Func(Of T, Boolean))) _
As System.Linq.IQueryable(Of T)
'Check the object context for Added objects first.
Dim AddedContextObjects = OSet.Context.ObjectStateManager _
.GetObjectStateEntries(EntityState.Added) _
.Select(Function(entity) entity.Entity).OfType(Of T)()
Dim Cpredicate = predicate.Compile
Dim MatchingObjects As New List(Of T)
For Each TObj As T In AddedContextObjects
If Cpredicate.Invoke(TObj) Then
MatchingObjects.Add(TObj)
End If
Next
'Now include a query to retrieve objects from the DB.
Dim DBObjects = OSet.Where(predicate)
If MatchingObjects.Count > 0 Then
'We found some added objects in the context.
'We want to return these objects as well as any Objects in DB
'that satisfy the predicate.
Return MatchingObjects.Union(DBObjects).AsQueryable
Else
'We didn't find any added objects in the context,
'so we just return the DB query.
Return DBObjects
End If
End Function
You have a number of options. You could extend the ObjectContext with another partial class to make your own mechanism for retrieving recently Added information.
Or you could just put an extension method on the ObjectContext that looks through the ObjectContext.ObjectStateManager looking for 'added' ObjectStateEntries, and then use LINQ to Objects to find what you are looking for.
Entity Framework 6
As per EF Docs Dbset always query against the database.
Note that DbSet and IDbSet always create queries against the database
and will always involve a round trip to the database even if the
entities returned already exist in the context. A query is executed
against the database when:
It is enumerated by a foreach (C#) or For Each (Visual Basic)
statement. It is enumerated by a collection operation such as ToArray,
ToDictionary, or ToList. LINQ operators such as First or Any are
specified in the outermost part of the query. The following methods
are called: the Load extension method on a DbSet,
DbEntityEntry.Reload, and Database.ExecuteSqlCommand. When results are
returned from the database, objects that do not exist in the context
are attached to the context. If an object is already in the context,
the existing object is returned (the current and original values of
the object's properties in the entry are not overwritten with database
values).
When you perform a query, entities that have been added to the context
but have not yet been saved to the database are not returned as part
of the result set. To get the data that is in the context, see Local Data
If a query returns no rows from the database, the result will be an
empty collection, rather than null.
Below is a simple snippet with local data:
await dbContext.Entity
.Where(e => e.Title.Contains("Text"))
.LoadAsync();
var locaEntities = dbContext.Entity.Local;
dbContext.Entity.Add(new Entity {});
// call save post atomic operation is finished.
await dbContext.SaveChangesAsync();