entity-framework - first - entity framework nuget
Marco de la entidad: volver a encontrar objetos agregados recientemente al contexto (6)
El siguiente método de extensión es para 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;
}
Cómo utilizar:
contextInstance.MyEntity.TryAttach(entityInstance, e => e.ID == entityInstance.ID);
por cierto: ¡me encantan los genéricos!
Estoy usando el marco de entidades y tengo un problema con los objetos de "re-localización" que acabo de crear ... básicamente dice así:
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 hay garantía de que el código funcione por cierto, es todo de mi cabeza)
¿Por qué la consulta "no encuentra" el EntityObject que acaba de crearse?
Si llamo a SaveChanges () después de AddToEntityObject, funciona (lo que no me sorprende), pero ¿por qué no lo extrae del caché correctamente?
Todavía soy verde en esto, así que espero que haya algo realmente fácil que estoy pasando por alto ...
Gracias
Esto sucede porque ents.EntityObject.WhatEver siempre consulta el origen de datos. Esta es una decisión de diseño. Lo hacen de esta manera, porque de lo contrario tendrían que ejecutar la consulta contra el origen de datos, contra el caché local y luego fusionar los resultados. Como señaló uno de los desarrolladores en un blog (no recuerdo dónde exactamente), no pudieron manejar esto de manera consistente.
Como se puede imaginar, hay muchos casos de esquina y borde que debe manejar correctamente. Puede encontrar una identificación que haya creado localmente, creada por otra persona en la base de datos. Esto te obligaría a estar preparado para manejar conflictos en (casi) todas las consultas. Tal vez podrían haber creado métodos para consultar la memoria caché local y los métodos para consultar el origen de datos, pero eso no es inteligente también.
Puede echar un vistazo a Transparent Lazy Loading para Entity Framework . Esto reemplaza el generador de código normal y obtienes entidades que pueblan sus colecciones de entidades relacionadas y referencias de entidades automáticamente en el acceso. Esto evita todo
if (!Entity.ReleatedEntities.IsLoaded)
{
Entity.RelatedEntities.Load();
}
fragmentos de código. Y puede consultar las colecciones porque siempre están cargadas implícitamente. Pero esta solución tampoco es perfecta. Hay algunos problemas. Por ejemplo, si crea una nueva entidad y accede a una colección de entidades relacionadas, obtendrá una excepción porque el código no puede recuperar las entidades relacionadas de la base de datos. También hay un problema relacionado con el enlace de datos y puede que yo no sepa más.
Lo bueno es que obtienes el código fuente y eres capaz de solucionar los problemas tú mismo y voy a examinar el primer problema si encuentro algo de tiempo. Pero estoy bastante seguro de que no será tan fácil de arreglar, porque espero que algunos casos simplemente no lleguen a la base de datos si la entidad recién se ha creado no es el comportamiento esperado.
Recientemente he tenido problemas con esta misma pregunta. Estoy publicando esta respuesta 2 años después de que se hizo la pregunta con la esperanza de que este fragmento de código pueda ayudar a alguien a buscar una respuesta.
Básicamente, he implementado un método de extensión (como lo sugirió Alex James) llamado "Buscar" que funciona de la misma manera que "Dónde", pero "Buscar" también verifica el ObjectContext para ver si hay alguna entidad adicional que satisfaga el predicado. Esto le permite encontrar una entidad incluso si aún no se ha guardado en la base de datos.
Find devuelve un IQueryable (de T) para que pueda usarlo como cualquier otro operador LINQ.
<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
Tienes un número de opciones. Puede extender ObjectContext
con otra clase parcial para crear su propio mecanismo para recuperar información recientemente agregada.
O simplemente podría poner un método de extensión en el ObjectContext
que mira a través del ObjectContext.ObjectStateManager
buscando ''añadido'' ObjectStateEntries
, y luego usar LINQ to Objects para encontrar lo que está buscando.
Yo estaba en la misma situación. Escribí este método de extensión que al menos para mí resuelve el problema (no tengo problemas con, por ejemplo, conflictos en mi contexto ...)
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);
}
el objeto recién agregado está en el origen de datos local, ya que aún no está en la base de datos, por lo que puede decir EntityObject search = ents.EntityObject.FirstOrDefault(o => o.Id == theId) ?? ents.EntityObject.Local.FirstOrDefault(o => o.Id == theId);
EntityObject search = ents.EntityObject.FirstOrDefault(o => o.Id == theId) ?? ents.EntityObject.Local.FirstOrDefault(o => o.Id == theId);