2010-12-13 27 views
6

¿Cuál es la forma correcta y rápida de guardar entidades combinadas de POCO nuevas y modificadas?Cómo guardar entidades separadas (nuevas + modificadas) separadas en Entity Framework?

estaba pensando acerca de estos métodos:

private void Method_2(IList<Entity> entities) //detached entities 
    { 
     //This method is using SELECT to check if entity exist 
     using (var context = new ModelContainer()) 
     { 
      foreach (Entity entity in entities) 
      { 
       var foundEntity = context.CreateObjectSet<Entity>().SingleOrDefault(t => t.Id == entity.Id); 
       context.Detach(foundEntity); //Remove it from ObjectStateManager 

       if (foundEntity != null)//It is modified entity 
       { 
        context.AttachTo("EntitySet", entity); //Attach our entity 
        context.ObjectStateManager.ChangeObjectState(entity, EntityState.Modified); //We know it exists 
       } 
       else//It is new entity 
       { 
        context.CreateObjectSet<Entity>().AddObject(entity); 
       } 
      } 
      context.SaveChanges(); 
     } 
    } 

    private void Method_1(IList<Entity> entities) //detached entities 
    { 
     //This method doesn't select anything from DB, but i have ta call Savechanges after each object 
     using (var context = new ModelContainer()) 
     { 
      foreach (Entity entity in entities) 
      { 
       try 
       { 
        context.AttachTo("EntitySet", entity); 
        context.ObjectStateManager.ChangeObjectState(entity, EntityState.Modified); 
        context.SaveChanges(); 
       } 
       catch (OptimisticConcurrencyException) 
       { 
        context.ObjectStateManager.ChangeObjectState(entity, EntityState.Added); 
        context.SaveChanges(); 
       } 
      } 
     } 
    } 

Cuando se trabaja en el entorno individual que tiene que saber qué entidad se añadió y que es modificada - es su responsabilidad de mantener esta información y proporcionar a ObjectContext.

Bueno, estoy de acuerdo con esta afirmación si te encuentras en una situación en la que necesitas usar un código EF como este en EF definitivamente algo está mal con tu decisión. He elegido la herramienta incorrecta para este trabajo.

+0

El uso de Entity Framework para sincronizar DBs a través de WCF fue una mala idea, funciona muy lento, terminé usando DTO con una guarida de acceso de datos ADO .NET ligera y personalizada. –

Respuesta

4

Cuando trabaje en un entorno aislado, debe saber qué entidad se ha agregado y cuál se ha modificado; es su responsabilidad guardar esta información y proporcionarla a ObjectContext.

La manera más fácil es:

foreach (var entity in entities) 
{ 
    if (entity.Id == 0) // 0 = default value: means new entity 
    { 
    // Add object 
    } 
    else 
    { 
    // Attach object and set state to modified 
    } 
} 

El ejemplo requiere que haya alguna clave primaria generada automáticamente db (Id).

Su Método 2 es posible con algunas modificaciones. No es necesario separar la entidad cuando la carga. En su lugar, use ApplyCurrentValues. El enfoque con la entidad de carga primero es muy útil cuando se decide trabajar con gráficos de objetos en lugar de una sola entidad. Pero en el caso del gráfico de objetos, debe realizar la sincronización manualmente. ApplyCurrentValues ​​funciona solo para propiedades escalares (sin navegación). Puede tratar de optimizar aún más su método para cargar enitites necesarios en una ida y vuelta a la base de datos en lugar de cargar entidades una a una.

Su método 1 es una solución terrible. El uso de excepciones planteadas en el servidor de la base de datos para controlar el flujo del programa es un mal enfoque.

+0

Recibo entidades con WCF para sincronizar bases de datos, por lo que el valor predeterminado para PK no significa nada. –

0

Quizás eche un vistazo a las entidades Self Tracking POCO. En mi humilde opinión, son perfectos para cualquier escenario que requiera que la entidad se separe del contexto. Se encarga de todo el código de plomería para usted.

+0

Mi servicio WCF no solo es utilizado por clientes .NET, también tenemos clientes PHP, y de todos modos no creo que sea una buena idea confiar en la información del cliente sobre el estado de la entidad, PHP no rastrea nada ... –

1

Estoy de acuerdo con @Ladislav - Method_1 es un mal enfoque. Deje que la base de datos genere excepciones que sean capturadas por EF; no intente y trague estas excepciones usted mismo.

Su en el camino correcto con el Método 1.

Aquí es cómo lo hago - como también tengo un contexto individual (POCO de, sin el control de cambios, ASP.NET MVC).

interfaz BLL: (tenga en cuenta que tengo TPT en mi modelo, por lo tanto, los genéricos "Post" es abstracta.)

void Add(Post post); 
void Update<TPost>(TPost post) where TPost : Post, new(); 

La restricción new() es crucial - verá por qué en breve.

No voy a mostrar cómo lo hago "ADD", porque es simple como usted piensa - AddObject(entity);

La "actualización" es la parte difícil:

public class GenericRepository<T> : IRepository<T> where T : class 
{ 
    public void Update<T2>(T2 entity) where T2: class, new() 
    { 
     var stub = new T2(); // create stub, now you see why we need new() constraint 

     object entityKey = null; 
     // ..snip code to get entity key via attribute on all domain entities 
     // once we have key, set on stub. 

     // check if entity is already attached.. 
     ObjectStateEntry entry; 
     bool attach; 

     if (CurrentContext.ObjectStateManager.TryGetObjectStateEntry(CurrentContext.CreateEntityKey(CurrentContext.GetEntityName<T>(), stub), out entry)) 
     { 
     // Re-attach if necessary. 
     attach = entry.State == EntityState.Detached; 
     } 
     else 
     { 
     // Attach for first time. 
     attach = true; 
     } 

     if (attach) 
     CurrentEntitySet.Attach(stub as T); 

     // Update Model. (override stub values attached to graph) 
     CurrentContext.ApplyCurrentValues(CurrentContext.GetEntityName<T>(), entity); 
    } 
} 

y que funciona para mí.

En cuanto a la clave de entidad, he utilizado atributos en mis clases de dominio. Una alternativa (a la que me voy a mover) es que todas mis entidades de dominio implementen una interfaz, que especifica que todas las entidades de dominio deben tener una propiedad llamada "EntityKey". Entonces usaré esa interfaz en mis restricciones. Básicamente, necesitaba una forma dinámica de crear entidades de stub en un repositorio genérico.

No me gusta personalmente la idea de "comprobar la ID, si es> 0, entonces es una actualización". Debido a que estoy trabajando con ASP.NET MVC, si yo (u otro desarrollador) olvida vincular el ID a la Vista, no se pasará, aunque puede ser una actualización, porque el ID == 0 será agregado.

Me gusta ser explícito sobre las operaciones. De esta forma, puedo realizar una lógica de validación separada Add/Update.

+0

No es tan terrible excepción, solo significa que se actualizaron 0 filas mientras que 1 se esperaba. –

+0

, pero su código de ejecución en el bloque catch es una mala idea. El bloque catch debe realizar el registro, volver a lanzar, etc., no volver a llamar a SaveChanges. – RPM1984

Cuestiones relacionadas