2011-03-21 67 views
7

Digamos que tengo el siguiente modelo conceptual, hay strories que tienen etiquetas (más de uno, por lo que es una relación muchos a muchos), además cada etiqueta pertenece a una categoría en particular.Entity Framework: evitar la inserción de duplicados

Mis datos provienen de una fuente externa y antes de insertarlo deseo asegurarme de que no se agreguen etiquetas duplicadas.

Actualizado fragmento de código:

static void Main(string[] args) 
    { 
     Story story1 = new Story(); 
     story1.Title = "Introducing the Entity Framework"; 
     story1.Tags.Add(new Tag { Name = ".net", }); 
     story1.Tags.Add(new Tag { Name = "database" }); 

     Story story2 = new Story(); 
     story2.Title = "Working with Managed DirectX"; 
     story2.Tags.Add(new Tag { Name = ".net" }); 
     story2.Tags.Add(new Tag { Name = "graphics" }); 

     List<Story> stories = new List<Story>(); 
     stories.Add(story1); 
     stories.Add(story2); 

     EfQuestionEntities db = new EfQuestionEntities(); 

     Category category = (from c in db.Categories 
          where c.Name == "Programming" 
          select c).First(); 

     foreach (Story story in stories) 
     { 
      foreach (Tag tag in story.Tags) 
      { 
       Tag currentTag = tag; 
       currentTag = GetTag(tag.Name, category, db); 
      } 

      db.Stories.AddObject(story); 
     } 

     db.SaveChanges(); 
    } 

    public static Tag GetTag(string name, Category category, EfQuestionEntities db) 
    { 
     var dbTag = from t in db.Tags.Include("Category") 
        where t.Name == name 
        select t; 

     if (dbTag.Count() > 0) 
     { 
      return dbTag.First(); 
     } 

     var cachedTag = db.ObjectStateManager.GetObjectStateEntries(EntityState.Added). 
      Where(ose => ose.EntitySet == db.Tags.EntitySet). 
      Select(ose => ose.Entity). 
      Cast<Tag>().Where(x => x.Name == name); 

     if (cachedTag.Count() != 0) 
     { 
      return cachedTag.First(); 
     } 

     Tag tag = new Tag(); 
     tag.Name = name; 
     tag.Category = category; 

     db.Tags.AddObject(tag); 

     return tag; 
    } 

Sin embargo, me sale una excepción sobre un objeto con el mismo EntityKey que ya está presente en el ObjectContext.

Además, si elimino la instrucción else obtendré una excepción sobre la violación de una restricción FK, por lo que parece que su atributo Categoría está establecido en nulo.

Respuesta

4

He tenido el mismo problema con EF. Esto es lo que terminé haciendo:

  1. En vez de hacer story1.Tags.Add(new Tag { Name = ".net", }) ti mismo, todo encaminado Tag creación a través de un método de ayuda como esto: story1.Tags.Add(GetTag(".net")).
  2. El método GetTag comprueba las etiquetas en el contexto para ver si debe devolver una entidad existente, como usted. Si lo hace, lo devuelve.
  3. Si no hay una entidad existente, comprueba el ObjectStateManager para ver si hay Tag entidades agregadas al contexto pero no escritas en el db. Si encuentra un Tag coincidente, lo devuelve.
  4. Si aún no ha encontrado el Tag, crea un nuevo Tag, lo agrega al contexto y luego lo devuelve.

En esencia, esto se asegurará de que hay más de una instancia de cualquier Tag (ya sea existente o la recién creada) será utilizado a lo largo de su programa.

Se eliminó algún código de ejemplo de mi proyecto (usa InventoryItem en lugar de Tag, pero se entiende la idea).

La comprobación en el paso 3 se hace de esta manera:

// Second choice: maybe it's not in the database yet, but it's awaiting insertion? 
inventoryItem = context.ObjectStateManager.GetObjectStateEntries(EntityState.Added) 
    .Where(ose => ose.EntitySet == context.InventoryItems.EntitySet) 
    .Select(ose => ose.Entity) 
    .Cast<InventoryItem>() 
    .Where(equalityPredicate.Compile()) 
    .SingleOrDefault(); 

if (inventoryItem != null) { 
    return inventoryItem; 
} 

Si el Tag no se encuentra en el paso 3, aquí está el código para el paso 4:

inventoryItem = new InventoryItem(); 
context.InventoryItems.AddObject(inventoryItem); 
return inventoryItem; 

Actualización:

Se debe utilizar así:

Story story1 = new Story(); 
story1.Title = "Introducing the Entity Framework"; 
story1.Tags.Add(GetTag(".net", category, db)); 
story1.Tags.Add(GetTag("database", category, db)); 
+0

¡Gracias! Voy a intentar eso ahora. Por cierto, ¿sigues usando EF después de enfrentar caídas de pozo como esa? –

+0

@Mike: lo soy. No había nada que no pudiera encontrar una forma aceptable de resolver dadas mis modestas necesidades, y lo que estaba usando antes de que EF (SubSonic) fuera aún peor. – Jon

+0

@Jon: Gracias también por ese fragmento de código.Desafortunadamente, sigo recibiendo esa excepción de violación FK que debe significar que hay entidades rastreadas con Categoría = nula –

Cuestiones relacionadas