Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Memento: auto track the entire graph via reflection recursive scan #118

Open
mauroservienti opened this issue Jan 10, 2015 · 1 comment
Open

Comments

@mauroservienti
Copy link
Member

mauroservienti commented Jan 10, 2015

Problem

When dealing with ChangeTrackingService and IMemento objects it's very easy to pollute the entity with ChangeTrackingService related code for the sake of simply supporting change tracking.

The basic usage on a flat entity is trivial, theoretically we could improve it via Roslyn, the only requirement is to use the Radical property system:

class Person : Entity
{
   public String Name
   {
      get { return this.GetPropertyValue(() => this.Name); }
      set { this.SetPropertyValue(() => this.Name, value); }
   }
}

Things are not so easy as the graph evolves:

class Person : Entity
{
   public Person(){ }

   protected override void OnMementoChanged(IChangeTrackingService newMemento, IChangeTrackingService oldMemento)
   {
      base.OnMementoChanged(newMemento, oldMemento);

      if(oldMemento !=null)
          oldMemento.Detatch(this.Address);

      if(newMemento !=null)
          newMemento.Attach(this.Address);
   }

   PersonAddress _address = new PersonAddress();
   public PersonAddress Address
   {
      get { return this._address; }
   }
}

When a property is a complex type that needs to be tracked as well, the responsibility to track it is on the entity forcing the user to know change tracking details. It's even worse when dealing with setters and nullable properties:

class Person : Entity
{
   public Person(){ }

   protected override void OnMementoChanged(IChangeTrackingService newMemento, IChangeTrackingService oldMemento)
   {
      base.OnMementoChanged(newMemento, oldMemento);

	  if(this.Address != null)
	  {
         if(oldMemento !=null)
             oldMemento.Detatch(this.Address);

         if(newMemento !=null)
             newMemento.Attach(this.Address);
      }
   }

   PersonAddress _address = new PersonAddress();
   public PersonAddress Address
   {
      get { return this._address; }
      set
      { 
      	if( this._address != value )
      	{
      	   if(this.Address != null)
	       {
               ((IMemento)this).Memento.Detatch( this.Address );
           }
      	   
      	   this._address == value;

           if(this.Address != null)
	       {
               ((IMemento)this).Memento.Attach( this.Address );
           }
      	}
      }
   }
}

The exact same problem applies to collections. That are basically complex IMemento types.

Ideas

AutoTrack

One option could be to inspect the graph when the tracking process starts, e.g. by doing:

var person = new Person();
memento.Attach( person, new TrackingOptions
{
   AutoTrack = true
} );

The above asks the ChangeTrackingService to scan the incoming graph, Person instance in this case, and automatically attach memento properties. If the entity is Entity (and we could enforce it) we can detect when properties change, get old and new value and automatically handle all the above scenarios.

Static Track Definition

One of the issues with the AutoTrack feature is for example detect circular references, another option could be (not mutually exclusive) to allow users to define statically what to track:

var person = new Person();
memento.Attach<Person>( person, new StaticTrackDefinition<Person>()
{
   Properties = new []{ p => p.Address }
} );

(the above syntax is invalid, it's just to give the idea)

The static approach complicates a lot things when it comes to complex graphs whose depth is more than 2 levels:

Order
   Rows
     OrderItem
         ProductDefinition
         PriceDefinition
         ...

If all the elements in the graph are IMemento instances manually defining what to track is awful.

Roslyn

Last option that comes to my mind is that the plumbing code could be written in the entity class via Roslyn at compile time. It can be super complex but it's an option.

Notes

This should not be the default behavior, users opt-in at Attach time. Obviously detaching a graph should do the reverse and detach all the auto-attached children.

@mauroservienti
Copy link
Member Author

@micdenny @nazarenomanco please take a look ☝️

@mauroservienti mauroservienti removed their assignment May 29, 2018
@mauroservienti mauroservienti removed this from the 1.7.0 milestone Jul 5, 2019
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

1 participant