07.11.2009
22:58

Workaround: Ein Objekt mit demselben Schlüssel ist bereits im ObjectStateManager vorhanden...

Wer mit dem Entity Framework in der Version 1 arbeitet, kennt sicherlich die verschiedenen Stufen der Herausforderungen. Eine dieser Herausforderung in einer mehrschichtigen Anwendung kann zum Beispiel sein:

Ein Objekt mit demselben Schlüssel ist bereits im ObjectStateManager vorhanden. Der ObjectStateManager kann nicht mehrere Objekte mit demselben Schlüssel nachverfolgen. 

Im Blog http://bernhardelbl.spaces.live.com/blog/cns!DB54AE2C5D84DB78!238.entry fand ich eine interessante Zusammenfassung und ein Beispiel, dass ich in meine Workaround EF - Extensions aufgenommen habe. Es funktionierte bei mir nur nicht auf Anhieb. Die erste Fehlermeldung die mich begrüsste war:

Die Auflistung wurde geändert. Der Enumerationsvorgang kann möglicherweise nicht ausgeführt werden.

Nach dieser Anpassung kamen noch ein paar Sideeffects aber mittlerweile läuft es bei mir. Da das Beispiel mit rekursiven Durchläufen arbeitet, habe ich noch eine Abbruchbedingung sowie zwei Erweiterungsmethoden hinzugefügt.

   32   public static class EfExtensions

   33   {

  200     // Other entity workarounds....

  201 

  202     /// <summary>

  203     /// Adds the or attach.

  204     /// </summary>

  205     /// <param name="context">The context.</param>

  206     /// <param name="entitySetName">Name of the entity set.</param>

  207     /// <param name="entity">The entity.</param>

  208     public static void AddOrAttach(this ObjectContext context, string entitySetName, EntityObject entity)

  209     {

  210       AddObjectOrAttach(context, entitySetName, entity, 2);

  211     }

  212 

  213     public static void AddOrAttach(this ObjectContext context, string entitySetName, EntityObject entity, int maxDepth)

  214     {

  215       AddObjectOrAttach(context, entitySetName, entity, maxDepth);

  216     }

  217 

  218     /// <summary>

  219     /// Adds or attaches an entity object to the context

  220     /// </summary>

  221     /// <param name="context">The context.</param>

  222     /// <param name="entitySetName">Name of the entity set.</param>

  223     /// <param name="entity">The entity.</param>

  224     /// <param name="maxDepth">The max depth.</param>

  225     public static void AddObjectOrAttach(

  226       ObjectContext context,

  227       string entitySetName,

  228       EntityObject entity,

  229       int maxDepth)

  230     {

  231       if (entity.EntityKey == null || entity.EntityKey.EntityKeyValues == null)

  232       {

  233         AddObject(context, entitySetName, entity, maxDepth);

  234       }

  235       else

  236       {

  237         Attach(context, entity, maxDepth);

  238       }

  239     }

  240 

  241     /// <summary>

  242     /// Adds an entity object to the context

  243     /// </summary>

  244     /// <param name="context">The context.</param>

  245     /// <param name="entitySetName">Name of the entity set.</param>

  246     /// <param name="entity">The entity.</param>

  247     /// <param name="maxDepth">The max depth.</param>

  248     public static void AddObject(

  249       ObjectContext context,

  250       string entitySetName,

  251       IEntityWithRelationships entity,

  252       int maxDepth)

  253     {

  254       // remove all relations

  255       List<RelationsShipMapping> map = new List<RelationsShipMapping>();

  256       RemoveRelationships(map, entity, -1, maxDepth);

  257 

  258       // add the entity

  259       context.AddObject(entitySetName, entity);

  260 

  261       // recreate all relationships

  262       AddRelationships(context, map, entity as EntityObject);

  263     }

  264 

  265     /// <summary>

  266     /// Attaches an entity object to the context

  267     /// </summary>

  268     public static void Attach(ObjectContext context, EntityObject entity, int maxDepth)

  269     {

  270       // remove all relations

  271       List<RelationsShipMapping> map = new List<RelationsShipMapping>();

  272       RemoveRelationships(map, entity, -1, maxDepth);

  273 

  274       // attach the entity

  275       context.Attach(entity);

  276 

  277       // recreate all relationships

  278       AddRelationships(context, map, entity);

  279     }

  280 

  281     /// <summary>

  282     /// Attaches all related entity object and recreates all relationships

  283     /// </summary>

  284     static void AddRelationships(ObjectContext context, List<RelationsShipMapping> map, EntityObject addedEntity)

  285     {

  286       int layer = -1;

  287       // loop through all layers

  288       do

  289       {

  290         layer++;

  291         // return all entity object from a specific layer

  292         IEnumerable<RelationsShipMapping> rs = from r in map

  293                                               where r.Layer == layer

  294                                               select r;

  295 

  296         // check if there are no further related entity objects

  297         if (rs.Count<RelationsShipMapping>() == 0)

  298           break;

  299 

  300         // move through the remembered mappings of the current layer

  301         foreach (RelationsShipMapping mapping in rs)

  302         {

  303           // check if we need to attach the related entity object

  304           IEntityWithKey entityWithKey = mapping.TargetEntity as IEntityWithKey;

  305 

  306           if (entityWithKey != null && entityWithKey.EntityKey != null && entityWithKey.EntityKey.EntityKeyValues != null)

  307           {

  308             // search entities in the object context.

  309             mapping.TargetEntity = GetCurrentEntityObject(context, mapping.TargetEntity as EntityObject, addedEntity);

  310             mapping.Entity = GetCurrentEntityObject(context, mapping.Entity as EntityObject, addedEntity);

  311           }

  312 

  313           // check if it´s a EntityCollection or EntityReference relationship

  314           if (mapping.IsReference == false)

  315           {

  316             // add the related collection entity object

  317             MethodInfo mi = typeof(RelationshipManager).GetMethod("GetRelatedCollection").MakeGenericMethod(mapping.TargetEntity.GetType());

  318 

  319             object col = mi.Invoke(

  320                 mapping.Entity.RelationshipManager,

  321                 new object[] { mapping.RelationshipName, mapping.TargetRole });

  322 

  323             MethodInfo miContains = col.GetType().GetMethod("Contains");

  324             var isAdded = miContains.Invoke(col, new object[] { mapping.TargetEntity });

  325 

  326             if ((bool)isAdded == false && ((EntityObject)mapping.TargetEntity).EntityState != EntityState.Added)

  327             {

  328               MethodInfo miAdd = col.GetType().GetMethod("Add");

  329               miAdd.Invoke(col, new object[] { mapping.TargetEntity });

  330             }

  331           }

  332           else

  333           {

  334             // set the related reference entity object

  335             MethodInfo mi = typeof(RelationshipManager).GetMethod("GetRelatedReference").MakeGenericMethod(mapping.TargetEntity.GetType());

  336 

  337             EntityReference er = (EntityReference)mi.Invoke(

  338                 mapping.Entity.RelationshipManager,

  339                 new object[] { mapping.RelationshipName, mapping.TargetRole });

  340             if (er.GetType().GetProperty("Value").GetValue(er, null) == null)

  341             {

  342               er.GetType().GetProperty("Value").SetValue(er, mapping.TargetEntity, null);

  343             }

  344           }

  345         }

  346       }

  347       while (true);

  348     }

  349 

  350     private static IEntityWithRelationships GetCurrentEntityObject(ObjectContext context, EntityObject entity, EntityObject addedEntity)

  351     {

  352       if (entity.EntityState == EntityState.Detached)

  353       {

  354         object currentEntityInDb = null;

  355         Type type = context.GetType();

  356         context.MetadataWorkspace.LoadFromAssembly(type.Assembly);

  357 

  358         if (context.TryGetObjectByKey(entity.EntityKey, out currentEntityInDb))

  359         {

  360           return currentEntityInDb as IEntityWithRelationships;

  361         }

  362         else

  363         {

  364           if (entity.EntityKey.EntitySetName == addedEntity.EntityKey.EntitySetName &&

  365             entity.EntityKey.EntityKeyValues == addedEntity.EntityKey.EntityKeyValues)

  366           {

  367             return addedEntity;

  368           }

  369           else

  370           {

  371             context.Attach(entity);

  372           }

  373         }

  374       }

  375 

  376       return entity;

  377     }

  378 

  379     /// <summary>

  380     /// Removes all related entity objects

  381     /// </summary>

  382     /// <param name="map">The map.</param>

  383     /// <param name="entity">The entity.</param>

  384     /// <param name="layer">The layer.</param>

  385     /// <param name="maxDepth">The max depth.</param>

  386     private static void RemoveRelationships(

  387       List<RelationsShipMapping> map,

  388       IEntityWithRelationships entity,

  389       int layer,

  390       int maxDepth)

  391     {

  392       // the layer represents the stack in the relationships

  393       // for example: the related entities to main entity object is layer 0,

  394       // and the related entites to the related entites is layer 1, etc...

  395       layer++;

  396 

  397       // get a collection of all related conceptual entities.

  398       IEnumerable<IRelatedEnd> en = entity.RelationshipManager.GetAllRelatedEnds();

  399 

  400       foreach (IRelatedEnd end in en)

  401       {

  402         // check if the relation is an EntityCollection

  403         IEnumerable col = end as IEnumerable;

  404 

  405         if (col != null)

  406         {

  407           string colRelationshipName = (string)col.GetType().GetProperty("RelationshipName").GetValue(col, null);

  408 

  409           string colSourceRoleName = (string)col.GetType().GetProperty("SourceRoleName").GetValue(col, null);

  410 

  411           string colTargetRoleName = (string)col.GetType().

  412             GetProperty("TargetRoleName").GetValue(col, null);

  413 

  414           List<IEntityWithRelationships> relationships = GetRelationships(col);

  415 

  416           List<IEntityWithRelationships> mem = new List<IEntityWithRelationships>();

  417 

  418           for (int idx = relationships.Count - 1; idx >= 0; idx--)

  419           {

  420             IEntityWithRelationships item = relationships[idx];

  421 

  422             // check if the relationship is already added the map

  423             int alreadyExists = (from c in map

  424                                 where c.RelationshipName == colRelationshipName &&

  425                                 c.TargetRole == colSourceRoleName

  426                                 select c).Count();

  427 

  428             if (alreadyExists > 0)

  429               continue;

  430 

  431             // remember the relationships and add it to the map

  432             RelationsShipMapping mapping = new RelationsShipMapping();

  433             mapping.Layer = layer;

  434             mapping.RelationshipName = colRelationshipName;

  435             mapping.TargetRole = colTargetRoleName;

  436             mapping.Entity = entity;

  437             mapping.TargetEntity = item;

  438             mapping.IsReference = false;

  439             map.Add(mapping);

  440             mem.Add(item);

  441             RemoveRelationships(map, item, layer, maxDepth);

  442           }

  443 

  444           // remove the entity collection relationships

  445           foreach (IEntityWithRelationships item in mem)

  446           {

  447             end.Remove(item);

  448           }

  449         }

  450 

  451         // check if the relation is an EntityReference

  452         EntityReference er = end as EntityReference;

  453 

  454         if (er != null)

  455         {

  456           System.Reflection.PropertyInfo pValue = er.GetType().GetProperty("Value");

  457           IEntityWithRelationships value = (IEntityWithRelationships)pValue.GetValue(er, null);

  458 

  459           if (value == null || er == null)

  460             continue;

  461 

  462           // check if the reference is already added the map

  463           int alreadyExists = (from c in map

  464                               where c.RelationshipName == er.RelationshipName &&

  465                               c.TargetRole == er.SourceRoleName

  466                               select c).Count();

  467 

  468           if (alreadyExists > 0)

  469             continue;

  470 

  471           // remember the relationships and add it to the map

  472           RelationsShipMapping mapping = new RelationsShipMapping();

  473           mapping.Layer = layer;

  474           mapping.RelationshipName = er.RelationshipName;

  475           mapping.TargetRole = er.TargetRoleName;

  476           mapping.Entity = entity;

  477           mapping.TargetEntity = value;

  478           mapping.IsReference = true;

  479           map.Add(mapping);

  480 

  481           // remove the entity reference relationships

  482           pValue.SetValue(er, null, null);

  483           RemoveRelationships(map, value, layer, maxDepth);

  484         }

  485       }

  486     }

  487 

  488     /// <summary>

  489     /// Gets the relationships.

  490     /// </summary>

  491     /// <param name="col">The col.</param>

  492     /// <returns>The IEntityWithRelationships collection.</returns>

  493     private static List<IEntityWithRelationships> GetRelationships(IEnumerable col)

  494     {

  495       IEnumerator enu = col.GetEnumerator();

  496       List<IEntityWithRelationships> relationships = new List<IEntityWithRelationships>();

  497       while (enu.MoveNext())

  498       {

  499         relationships.Add(enu.Current as IEntityWithRelationships);

  500       }

  501 

  502       return relationships;

  503     }

  504 

  505     /// <summary>

  506     /// Used to remember relationships in the entity object

  507     /// </summary>

  508     internal class RelationsShipMapping

  509     {

  510       public int Layer { get; set; }

  511       public string RelationshipName { get; set; }

  512       public string TargetRole { get; set; }

  513       public IEntityWithRelationships Entity { get; set; }

  514       public IEntityWithRelationships TargetEntity { get; set; }

  515       public bool IsReference { get; set; }

  516     }

  517   }

Noch einen Hinweis zum Schluss: Ich setze den Workaround zurzeit in einer Situation ein, in der dieser gut funktioniert. Die Rekursionstiefe ist hierbei auf 2 Ebenen gesetzt. Dieses Beispiel verarbeitet zwar auch Collections, ist aber nicht für Massenoperationen geeignet.

Bleibt nur zu hoffen, dass in der Version 2 soetwas nicht mehr notwendig ist. ;-)

  •  
  • 0 Kommentar(e)
  •  

Mein Kommentar

Über jeden weiteren Kommentar in diesem Post benachrichtigen.

Zurück

Translate this page

Kategorien

  • [-].NET Development (215)
  • [-]Datenbank (26)
  • HTML (1)
  • Konfiguration (12)
  • Mind Map (10)
  • Off-topic (9)
  • Open Source (3)
  • Qualität (7)
  • Sharepoint (6)
  • Sicherheit (2)

Archiv

Social Bookmarking

Bookmark bei: Mr. Wong Bookmark bei: Webnews Bookmark bei: Icio Bookmark bei: Oneview Bookmark bei: Linkarena Bookmark bei: Favoriten Bookmark bei: Seekxl Bookmark bei: Favit Bookmark bei: Social Bookmarking Tool Bookmark bei: Power Oldie Bookmark bei: Bookmarks.cc Bookmark bei: Newskick Bookmark bei: Newsider Bookmark bei: Linksilo Bookmark bei: Readster Bookmark bei: Folkd Bookmark bei: Yigg Bookmark bei: Digg Bookmark bei: Del.icio.us Bookmark bei: Reddit Bookmark bei: Simpy Bookmark bei: StumbleUpon Bookmark bei: Slashdot Bookmark bei: Netscape Bookmark bei: Furl Bookmark bei: Yahoo Bookmark bei: Spurl Bookmark bei: Google Bookmark bei: Blinklist Bookmark bei: Blogmarks Bookmark bei: Diigo Bookmark bei: Technorati Bookmark bei: Newsvine Bookmark bei: Blinkbits Bookmark bei: Ma.Gnolia Bookmark bei: Smarking Bookmark bei: Netvouz Information