Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is it possible to modify entity object within ObjectStateManagerChanged handler?

Here is a business logic I need to implement:

There are multiple entity types in data model, e.g.: User, UserProfile, UserProfileValue, UserExtendedData etc.

When creating some entities like User, I need auto create other entities logically linked (usually with 1:1 relationship in db) for example UserProfile.

Each entity has a controller with OnCreating/OnCreated, OnUpdating/OnUpdated methods, which are invoked from overridden SaveChanges methods of my datacontext class.

public override int SaveChanges(SaveOptions options)
{
    IEnumerable<ObjectStateEntry> addedEntities = ObjectStateManager.GetObjectStateEntries(EntityState.Added);
    IEnumerable<ObjectStateEntry> changedEntities = ObjectStateManager.GetObjectStateEntries(EntityState.Modified);
    IEnumerable<ObjectStateEntry> deletedEntities = ObjectStateManager.GetObjectStateEntries(EntityState.Deleted);

    InvokeSavingControllers(addedEntities, changedEntities, deletedEntities);

    //some other code

    int rowsAffected = base.SaveChanges(options);

    InvokeSavedControllers(addedEntities, changedEntities, deletedEntities);

    return rowsAffected;
}

if I implemented auto creation in my User.OnCreating() method then eventually no OnCreating() controllers will be invoked for all auto created children entities since the collection addedEntities is not updated with new entries.

One of my ideas is to perform auto creation of the dependent entities by handling ObjectStateManagerChanged of data context

private void ObjectStateManagerObjectStateManagerChanged(object sender, System.ComponentModel.CollectionChangeEventArgs e)
{
    if (e.Action == CollectionChangeAction.Add)
    {
        var state = ObjectStateManager.GetObjectStateEntry(e.Element).State;
        if (state == EntityState.Added)
        {
            //should call on creating handlers
            if (e.Element is User)
            {
                User user = (User)e.Element;
                user.UserProfile = UserProfile.CreateDefaultProfile();
            }
        }
    }
}

But unfortunately I am getting NullReferrence exception if I do it this way. Even if I try to modify the property of user within ObjectStateManagerObjectStateManagerChanged I get exception.

Any thoughts or suggestions on how to implement the required functionality?

EDIT:

Null Reference I am getting:

System.NullReferenceException was unhandled by user code
  Message=Object reference not set to an instance of an object.
  Source=System.Data.Entity
  StackTrace:
       at System.Data.Objects.ObjectContext.AddSingleObject(EntitySet entitySet, IEntityWrapper wrappedEntity, String argumentName)
       at System.Data.Objects.ObjectContext.AddObject(String entitySetName, Object entity)
       at Edm.Entity.Entities.AddObject(Object entity) 

And Edm.Entity.Entities.AddObject(Object entity) is where new User object is added to the context.

EDIT2: Not sure what I've changed but the same code as above started to throw the following exception:

<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<error xmlns="http://schemas.microsoft.com/ado/2007/08/dataservices/metadata">
  <code></code>
  <message xml:lang="en-US">An error occurred while processing this request.</message>
  <innererror>
    <message>Object reference not set to an instance of an object.</message>
    <type>System.NullReferenceException</type>
    <stacktrace>   at System.Data.Objects.DataClasses.EntityReference`1.Exclude()&#xD;
   at System.Data.Objects.DataClasses.RelationshipManager.RemoveRelatedEntitiesFromObjectStateManager(IEntityWrapper wrappedEntity)&#xD;
   at System.Data.Objects.ObjectContext.AddObject(String entitySetName, Object entity)&#xD;
   at System.Data.Services.Providers.ObjectContextServiceProvider.CreateResource(String containerName, String fullTypeName)&#xD;
   at System.Data.Services.UpdatableWrapper.CreateResource(String containerName, String fullTypeName)&#xD;
   at System.Data.Services.Serializers.SyndicationDeserializer.CreateObject(SegmentInfo segmentInfo, Boolean topLevel, SyndicationItem item)&#xD;
   at System.Data.Services.Serializers.SyndicationDeserializer.CreateSingleObject(SegmentInfo segmentInfo)&#xD;
   at System.Data.Services.Serializers.Deserializer.ReadEntity(RequestDescription requestDescription)&#xD;
   at System.Data.Services.Serializers.Deserializer.HandlePostRequest(RequestDescription requestDescription)&#xD;
   at System.Data.Services.DataService`1.HandlePostOperation(RequestDescription description, IDataService dataService)&#xD;
   at System.Data.Services.DataService`1.ProcessIncomingRequest(RequestDescription description, IDataService dataService)&#xD;
   at System.Data.Services.DataService`1.BatchDataService.HandleBatchContent(Stream responseStream)</stacktrace>
  </innererror>
</error>
like image 471
Paul Avatar asked Jun 26 '26 00:06

Paul


1 Answers

I have exactly the same issue. It seems to be a problem to add another EntityObject during the ObjectStateManagerChanged event. The strange thing is that my code (and your code probably too) is working fine in .net 3.5, but gives a NullReferenceException in 4.0.

I've debugged the .net framework, and figured out that it crashes in the internal ObjectContext.AddSingleObject method after it invokes the ObjectStateManagerChanged event. This exception occures because the internal ProcessedEntities hashset is made null at the end of the public AddObject method. But if the EntityObject is created during the ObjectStateManagerChanged event this hashset should not be made null.

This seems to be a .net framework 4.0 bug.

like image 166
user930428 Avatar answered Jun 28 '26 17:06

user930428



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!