Monday, June 27, 2016

Chapter 2 Automatic calculation of the properties of computable representations of models in .NET

For example, to support the last-described relationship, the programmer must write a large number of routine code (the situation is even more complicated if there is a chain of dependence is not one, but several collections):

Subscribe to ObservableCollection changes (via INotifyCollectionChanged interface) representing Orders.
Subscribe to PropertyChanged each item in the collection (order) to monitor changes in the properties of Quantity.
Maintain the relevant subscription date: unsubscribe from the elements removed from the collection, and to subscribe to add, unsubscribe when you change an instance of the collection itself from an old collection, and to subscribe to the new event.




To simplify this work and for the possibility of a declarative representation of dependency was created Dependency Tracker (DependenciesTracker), which will be discussed below.

Dependency Tracker (DependenciesTracker)

.NET Library DependenciesTracking implements automatic update computable properties and the ability to define dependencies in a declarative style. It is quite lightweight both in terms of ease of use, and in terms of implementation: for her work does not require any establishment of any wrappers on the properties (such as ObservableProperty <T>, IndependentProperty <T>, etc.), no inheritance models from any base class or properties necessary to mark any attributes. The implementation does not significantly uses reflection and is not based on copying assemblies after compilation. The main component of the assembly is the class DependenciesTracker, the use of which will continue to be covered in detail.

In general, for tracking joined dependent properties, it is necessary to make two simple things:

determine the dependence of the properties (for the class as a whole)
start tracking these relationships (for a sample, usually in the constructor).

These points are discussed below in the various examples.

Simple (single-level) depending

We begin with an example which has been described at the outset. Rewrite the class Order, so that the dependence on the Cost Price and Quantity tracked automatically and entailed recalculation Cost changing Price or Quantity. In accordance with the claims 1-2 for the need to implement this Order class as follows:

public class Order: INotifyPropertyChanged
    {
        private decimal _price;
        private int _quantity;
        private decimal _cost;
        public decimal Price
        {
            get {return _price; }
            set
            {
                if (value == _price) return;
                _price = value;
                OnPropertyChanged ();
            }
        }
        public int Quantity
        {
            get {return _quantity; }
            set
            {
                if (value == _quantity) return;
                _quantity = value;
                OnPropertyChanged ();
            }
        }
        public decimal Cost
        {
            get {return _cost; }
            private set
            {
                if (value == _cost) return;
                _cost = value;
                OnPropertyChanged ();
            }
        }
        // Define static "dependencies map" that will store depending on class
        private static readonly IDependenciesMap <Order> _dependenciesMap = new DependenciesMap <Order> ();
        static Order ()
        {
           // Define and add to the map depending
            _dependenciesMap.AddDependency (o => o.Cost, o => o.Price * o.Quantity, o => o.Price, o => o.Quantity)
        }
        private IDisposable _tracker;
        public Order ()
        {
            // Start monitor depending on the model of the current instance
            _dependenciesMap.StartTracking (this);
        }
        public event PropertyChangedEventHandler PropertyChanged;
        protected void OnPropertyChanged ([CallerMemberName] string propertyName = null)
        {
            var handler = PropertyChanged;
            if (handler = null!) handler (this, new PropertyChangedEventArgs (propertyName));
        }
The example shows that we use IDependenciesMap to identify their addiction and start to monitor. Let us consider this interface more.

 public interface IDependenciesMap<T> { IDependenciesMap<T> AddDependency<U>(Expression<Func<T, U>> dependentProperty, Func<T, U> calculator, Expression<Func<T, object>> obligatoryDependencyPath, params Expression<Func<T, object>>[] dependencyPaths); IDependenciesMap IDependenciesMap<T> AddDependency<U>(Action<T, U> setter, Func<T, U> calculator, Expression<Func<T, object>> obligatoryDependencyPath, params Expression<Func<T, object>>[] dependencyPaths); IDisposable StartTracking(T trackedObject); } IDisposable StartTracking(T trackedObject); }
In the example to add Depending we used the first version of the overloaded method AddDependency. It has the following parameters:

dependentProperty - expression (Expression), which describes the dependency property (o => o.Cost),
calculator - a method that calculates the value of the dependent property on a particular model instance (o => o.Price * o.Quantity),
obligatoryDependencyPath and dependencyPaths - Expression'y that describe the path from which the property zavisisit (o => o.Price, o => o.Quantity).

The second version of the first parameter takes AddDependency Setter dependent properties ((o, val) => o.Cost = val), instead Expression'a which it describes (and which eventually compiled into the same setter). The rest of the methods are similar.

In the second step we added StartTracking call to the constructor. This means that monitoring changes in the properties of ways depending begin immediately to create the order object. The method StartTracking made about the following:

makes it necessary to subscribe to changes in properties depending ways
there is an initial count and computable setting property values.

The method returns IDisposable, which can be used to stop the change tracking model at any stage of life cycle.

Depending on the properties of chains

Now complicate the example. To do this, transfer the Price and Quantity in a separate object OrderProperties:

public class OrderProperties: INotifyPropertyChanged
    {
        private int _price;
        private int _quantity;
        public int Price
        {
            get {return _price; }
            set
            {
                if (_price! = value)
                {
                    _price = value;
                    OnPropertyChanged ( "Price");
                }
            }
        }
        public int Quantity
        {
            get {return _quantity; }
            set
            {
                if (_quantity! = value)
                {
                    _quantity = value;
                    OnPropertyChanged ( "Quantity");
                }
            }
        }
        public event PropertyChangedEventHandler PropertyChanged;
        protected void OnPropertyChanged (string propertyName)
        {
            PropertyChangedEventHandler handler = PropertyChanged;
            if (handler = null!) handler (this, new PropertyChangedEventArgs (propertyName));
        }
    }
And we put the object inside OrderProperties Order, making thus the property Cost dependent OrderProperties.Price and OrderProperties.Quantity:
public class Order: INotifyPropertyChanged
    {
        private OrderProperties _properties;
        private int _cost;
        public OrderProperties Properties
        {
            get {return _properties; }
            set
            {
                if (_properties! = value)
                {
                    _properties = value;
                    OnPropertyChanged ( "Properties");
                }
            }
        }
        public int Cost
        {
            get {return _cost; }
            private set
            {
                if (_cost! = value)
                {
                    _cost = value;
                    OnPropertyChanged ( "Cost");
                }
            }
        }
        private static readonly IDependenciesMap <Order> _map = new DependenciesMap <Order> ();
        static Order ()
        {
            _map.AddDependency (o => o.Cost, o => o.Properties = null o.Properties.Price * o.Properties.Quantity:!? -1, o => o.Properties.Price, o => o. Properties.Quantity);
        }
        private IDisposable _tracker;
        public Order ()
        {
            _tracker = _map.StartTracking (this);
        }
        public event PropertyChangedEventHandler PropertyChanged;
        protected void OnPropertyChanged (string propertyName)
        {
            PropertyChangedEventHandler handler = PropertyChanged;
            if (handler = null!) handler (this, new PropertyChangedEventArgs (propertyName));
        }
    } 
Now Cost will be automatically recalculated when changing the Price and Quantity properties Properties in the order, or when you change an instance of the Properties. As you can see, to determine the dependence of the properties of the chain it was not more complicated than a simple one-level relationship. chapter 1.

No comments:

Post a Comment