How can I update entity from expression?
I dynamically build which properties(columns) should be updated. Function that do that returns Expression<Func<T, T>> where T is entity.
I have loaded entity from database using Where and Single.
Expression<Func<T, T>> expr = this.CallVirtualMethodAndGetExpression(ModelFromRequest);
DbUser entity = this.context.Users.Where(t => t.Id == 1).Single();
some magic ??? //tried expr.Compile()(entity) but it does not work.
this.context.SaveChanges();
//use DbUser with updated properties of course.
I know I could use EF Plus (batch update), but I want to stick with SaveChanges (I need DbUser after SaveChanges and I want to avoid sending another request).
Updated:
I have entity object and expression. Now, I need to edit entity object based on expression and called SaveChanges which should update entity.
the magic line you are looking for:
var resultOfTypeT = expr.Compile().Invoke(entity)
But then entity needs to be an instance of type T , not DbUser
So this does not make sense to me.
So i cant give you exact code to compile for you, since i cant see the method signature where T is defined. and also do not know what the method signature of this.CallVirtualMethodAndGetExpression(ModelFromRequest) looks like.
But the Compile() will change the Expression<Func<T,T>> to just Func<T,T> which can then Invoke the defined functionality for Func<T,T> if you have a valid instance of T to pass to it.
Now your entity is not of type T, since it is of type DbUser So you are probably going to need to change your Method Signature or get your DbUser in a generic way or something. If you are only going to need to pass a DbUser instance, you do not need c# Generics. but cant really tell with just the current information available.
Related
Code First is a terrific feature. But I could not figure out how to persist an object without creating a specialized DbContext first. Is it possible at all? I'd like to see something along these lines:
var dbConn = new SqlCeConnection("Data Source=Test.sdf");
using (var db = new DbContext(dbConn, true))
{
var cmp = new Company { CompanyName = "Microsoft" };
db.Set(typeof(Company)).Add(cmp);
var recordsAffected = db.SaveChanges();
Console.WriteLine(
"Saved {0} entities to the database, press any key to exit.",
recordsAffected);
Console.ReadKey();
}
Is it possible to dynamically register a class for model creation? There must be a way!
No, as I know there is not any possibility to create DbContext without any information about mapping. Specialized context with predefined DbSets is necessary to define mappings and database initialization. You can probably use base DbContext only if you provide informations about mapping through its constructor by passing DbCompiledModel created manually before using DbContext but I haven't tried this yet.
The problem is that DbSets and overriden OnModelCreating are used to infer needed mapping. If you don't have DbSets or OnModelCreating defined DbContext can't infer and cache mapping. Mapping metadata are created and compiled only once for each context type (until application restarts). This operation is considered as very slow. If you don't have specific context type EF can't probably infer mapping and even if it can it will probably need to create and compile metadata for each instance of the context (like for anonymous types).
If you use DbCompiledModel created manually it will be your responsibility to reuse it.
It's one of the parameters supplied to the CreateMetadata method (which you override if extending metadata support).
ModelMetadata CreateMetadata(IEnumerable<Attribute> attributes,
Type containerType,
Func<object> modelAccessor, <<--THIS ONE
Type modelType,
string propertyName)
I had assumed that it allowed you to access the model object itself (e.g. for setting metadata based on model values), however when I try to use it to cast to my model object I just get null.
Entity ent = (Entity)modelAccessor(); // = Null
If I've missunderstood, can anyone explain what it's purpose is? Or alternatively, how to properly use it?
Thanks
We originally had that as "object model", rather than "Func modelAccessor". We had to change it late in MVC 2's ship cycle.
The purpose is to delay retrieving the actual value of the model until such point as you know you're going to need it (that is, until you call ModelMetadata.Model).
The problem it solves is actually a rather esoteric one related to model binding against a LINQ to SQL class that has a foreign key reference in it. The problem is, if you've retrieved the child object which is represented by a foreign key relationship (which usually means a delay load of that object), then you're no longer allowed to choose a new child object by setting the foreign key ID property. It's very common to model bind the foreign key ID (and not the whole foreign key entity) when model binding, but if we'd retrieved the foreign key entity object (for the purposes of populating the ModelMetadata class) then that binding would no longer be legal, and actually throw an exception. Since ModelMetadata is used for both directions of models -- inbound, via model binding, and outbound, via HTML generation -- we needed to introduce the layer of indirection to protect your ability to use it in both scenarios without disrupting LINQ to SQL's rules.
The modelAccessor parameter does not point to an instance of the object, but rather it is a function that will access some attribute of your object. The Func "encapsulates a method that has no parameters and returns a value of the type specified by the TResult parameter." For example, if we have following class:
public class Bar(){
[DisplayName("I am Foo.")]
public string Foo{get;}
}
When the CreateMetaData is called, it will be to create meta data for the Foo property and the modelAccessor will be a function that returns the value of Foo.
I did a little digging and found a way to get to the instance of the object, but it requires using reflection. You can do the following to get the Bar class in my example:
if (modelAccessor != null)
{
//Use reflection to get the private field that holds the Bar object.
FieldInfo container = modelAccessor.Target.GetType().GetField("container");
//Invoke field on the modelAccessor target to get the instance of the Bar object.
Bar myObject = (Bar)container.GetValue(modelAccessor.Target);
}
I've only run this against a simple test case, so your mileage may vary, but hopefully this will help clarify what is going on.
In the context of Entity Framework 4, the default behavior when adding a function import is to call it via ExecuteFunction<T>(), where T must apparently implement some property change notification stuff. (In my case it's generating a complex type derived from ComplexObject.)
I don't need or want any change notifications, and I'm required to send POCOs up the line after these sproc calls.
Is there a way to get a POCO directly from an EF sproc call? If not, does anyone have any recommendations on turning my sproc result into a POCO?
(I've played briefly with the POCO Template, but it doesn't seem to support stored procedures in any way.)
ExecuteFunction<T> returns an ObjectResult<T>, which implements IEnumerable<T>, so you can project the T onto anything via LINQ. E.g.:
IEnumerable<MyPoco> = from f in Context.MyFunction()
select new MyPoco
{
A = f.A,
B = f.B
};
I have an ADO.Net Data Service that I am using to do a data import. There are a number of entities that are linked to by most entities. To do that during import I create those entities first, save them and then use .SetLink(EntityImport, "NavigationProperty", CreatedEntity). Now the first issue that I ran into was that the context did not always know about CreatedEntity (this is due to each of the entities being imported independently and a creation of a context as each item is created - I'd like to retain this functionality - i.e. I'm trying to avoid "just use one context" as the answer).
So I have a .AddToCreatedEntityType(CreatedEntity) before attempting to call SetLink. This of course works for the first time, but on the second pass I get the error message "the context is already tracking the entity".
Is there a way to check if the context is already tracking the entity (context.Contains(CreatedEntity) isn't yet implemented)? I was thinking about attempting a try catch and just avoiding the error, but that seems to create a new CreatedEntity each pass. It is looking like I need to use a LINQ to Data Services to get that CreatedEntity each time, but that seems innefficient - any suggestions?
I think you should look at the EntityState property of your entity.
Only if it is of the value EntityState.Detached than you have to add it to your context.
Do not forget the following remark:
This enumeration has a FlagsAttribute
attribute that allows a bitwise
combination of its member values.
I would create a extension method:
public static class EntityObjectExtensions
{
public static Boolean IsTracked(this EntityObject self)
{
return (self.EntityState & EntityState.Detached) != EntityState.Detached;
}
}
When trying to check whether the context was tracking the entity that I wanted to update (or add) I was pretty disapointed when I found that the context.Entites.Contains(currentItem) didn't work.
I got around it using:
if (context.Entities.Where(entities => entities.Entity == currentItem).Any())
{
this.service.UpdateObject(currentItem);
}
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();