Click here to Skip to main content
15,894,646 members
Articles / Web Development / ASP.NET

Signum Framework Principles

Rate me:
Please Sign up or sign in to vote.
4.74/5 (27 votes)
25 Jul 2011CPOL18 min read 99.4K   1.1K   86  
Explains the philosophy behind Signum Framework, an ORM with a full LINQ Provider that encourages an entities-first approach.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Signum.Entities;
using System.ComponentModel;
using System.Reflection;
using System.Linq.Expressions;
using Signum.Utilities;
using System.Runtime.Serialization.Formatters.Binary;
using System.IO;
using System.Collections.Specialized;
using Signum.Utilities.Reflection;
using System.Runtime.Serialization;
using System.Collections.ObjectModel;

namespace Signum.Entities
{
    [Serializable]
    public abstract class ModifiableEntity : Modifiable, INotifyPropertyChanged, IDataErrorInfo, ICloneable
    {
        static readonly string[] ModifiableEntityProperties = typeof(ModifiableEntity)
            .GetProperties(BindingFlags.Instance | BindingFlags.Public)
            .Select(p => p.Name)
            .ToArray();


        [Ignore]
        protected bool selfModified = true;

        internal ModifiableEntity() { }

        public override bool SelfModified
        {
            get { return selfModified; }
            internal set { selfModified = value; }
        }

        protected bool Set<T>(ref T variable, T value, string propertyName)
        {
            if (EqualityComparer<T>.Default.Equals(variable, value))
                return false;

            if (variable is INotifyCollectionChanged && NotifyCollectionChangedAttribute.HasToNotify(GetType(), propertyName))
                ((INotifyCollectionChanged)variable).CollectionChanged -= ChildCollectionChanged;

            if (variable is INotifyPropertyChanged && NotifyPropertyChangedAttribute.HasToNotify(GetType(), propertyName))
                ((INotifyPropertyChanged)variable).PropertyChanged -= ChildItemPropertyChanged; 

            variable = value;

            if (variable is INotifyCollectionChanged && NotifyCollectionChangedAttribute.HasToNotify(GetType(), propertyName))
                ((INotifyCollectionChanged)variable).CollectionChanged += ChildCollectionChanged;

            if (variable is INotifyPropertyChanged && NotifyPropertyChangedAttribute.HasToNotify(GetType(), propertyName))
                ((INotifyPropertyChanged)variable).PropertyChanged += ChildItemPropertyChanged; 

            selfModified = true;
            Notify(propertyName);
            NotifyError();

            return true;
        }

        protected void NotifyToString()
        {
            Notify("ToStringMethod");
        }

        protected void NotifyError()
        {
            Notify("Error");
        }

        protected void NotifyError(string propertyName)
        {
            Notify(propertyName);
            Notify("Error");
        }

        public string ToStringMethod
        {
            get { return ToString(); }
        }

        public bool SetToStr<T>(ref T variable, T valor, string propertyName)
        {
            if (this.Set(ref variable, valor, propertyName))
            {
                NotifyToString();
                return true;
            }
            return false;
        }

        #region Eventos Coleccion

        protected internal override void PostRetrieving()
        {
            RebindEvents();

            base.PostRetrieving();
        }

        protected virtual void RebindEvents()
        {
            foreach (Func<object, object> getter in NotifyCollectionChangedAttribute.FieldsToNotify(GetType()))
            {
                INotifyCollectionChanged notify = (INotifyCollectionChanged)getter(this);
                if (notify != null)
                    notify.CollectionChanged += ChildCollectionChanged;
            }

            foreach (Func<object, object> getter in NotifyPropertyChangedAttribute.FieldsToNotify(GetType()))
            {
                INotifyPropertyChanged notify = (INotifyPropertyChanged)getter(this);
                if (notify != null)
                    notify.PropertyChanged += ChildItemPropertyChanged;
            }
        }

   

        [OnDeserialized]
        private void OnDeserialized(StreamingContext context)
        {
            RebindEvents();
        }

        protected virtual void ChildCollectionChanged(object sender, NotifyCollectionChangedEventArgs args)
        {

        }

        protected virtual void ChildItemPropertyChanged(object sender, PropertyChangedEventArgs e)
        {

        }
        #endregion

        protected void Notify(string propertyName)
        {
            if (PropertyChanged != null)
                PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
        }

        [Ignore]
        int idTemporal = MyRandom.Current.Next();

        public override int GetHashCode()
        {
            return GetType().FullName.GetHashCode() ^ idTemporal;
        }
        
        [field:NonSerialized, Ignore]
        public event PropertyChangedEventHandler PropertyChanged;

        #region IDataErrorInfo Members

        internal class PropertyPack
        {
            public PropertyPack(PropertyInfo pi, Func<object, object> getValue, Action<object, object> setValue)
            {
                this.PropertyInfo = pi;
                Validators = pi.GetCustomAttributes(typeof(ValidatorAttribute), true).OfType<ValidatorAttribute>().ToReadOnly();
                this.GetValue = getValue;
                this.SetValue = setValue; 
            }

            public readonly Func<object, object> GetValue;
            public readonly Action<object, object> SetValue;
            public readonly PropertyInfo PropertyInfo;
            public readonly ReadOnlyCollection<ValidatorAttribute> Validators; 
        }

        internal static Dictionary<string, PropertyPack> GetPropertyValidators(Type type)
        {
            lock (validators)
            {
                return validators.GetOrCreate(type, () =>
                    MemberEntryFactory.GenerateIList(type, MemberOptions.Properties | MemberOptions.Getter| MemberOptions.Setters| MemberOptions.Untyped)
                    .Cast<IMemberEntry>()
                    .Where(p => !ModifiableEntityProperties.Contains(p.Name))
                    .ToDictionary(p => p.Name, p => new PropertyPack((PropertyInfo)p.MemberInfo, p.UntypedGetter, p.UntypedSetter)));
            }
        }

        static Dictionary<Type, Dictionary<string, PropertyPack>> validators = new Dictionary<Type, Dictionary<string, PropertyPack>>(); 
        

        public string Error
        {
            get { return IntegrityCheck(); }
        }

        //override for full entitity integrity check. Remember to call base. 
        public override string IntegrityCheck()
        {
            return GetPropertyValidators(GetType()).Select(k => this[k.Key]).NotNull().ToString("\r\n");
        }

        //override for per-property checks
        public virtual string this[string columnName]
        {
            get
            {
                if (columnName == null)
                    return Error;
                else
                {
                    PropertyPack pp = GetPropertyValidators(GetType())[columnName];
                    object val = pp.GetValue(this);
                    return pp.Validators.Select(v => v.Error(val)).NotNull().Select(e => e.Formato(columnName)).FirstOrDefault();
                }
            }
        }

        #endregion


        #region ICloneable Members

        object ICloneable.Clone()
        {
            BinaryFormatter bf = new BinaryFormatter();
            using (MemoryStream stream = new MemoryStream())
            {
                bf.Serialize(stream, this);
                stream.Seek(0, SeekOrigin.Begin);
                return bf.Deserialize(stream);
            }
        }

        #endregion
    }

   
}

By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.

If a file you wish to view isn't highlighted, and is a text file (not binary), please let us know and we'll add colourisation support for it.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Software Developer (Senior) Signum Software
Spain Spain
I'm Computer Scientist, one of the founders of Signum Software, and the lead developer behind Signum Framework.

www.signumframework.com

I love programming in C#, Linq, Compilers, Algorithms, Functional Programming, Computer Graphics, Maths...

Comments and Discussions