Click here to Skip to main content
15,886,199 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 98.9K   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 System.Data.SqlClient;
using Signum.Utilities;
using Signum.Utilities.DataStructures;
using Signum.Entities;
using Signum.Engine.Properties;
using System.Text.RegularExpressions;

namespace Signum.Engine
{
    public enum Spacing
    {
        Simple,
        Double,
        Triple
    }

    public abstract class SqlPreCommand
    {
        public class SqlPair
        {
            public readonly SqlPreCommand New;
            public readonly SqlPreCommand Remainig;

            public SqlPair(SqlPreCommand @new, SqlPreCommand remaining)
            {
                this.New = @new;
                this.Remainig = remaining;
            }
        }

        public abstract IEnumerable<SqlPreCommandSimple> Leaves();

        protected internal abstract void GenerateScript(StringBuilder sb);

        protected internal abstract void GenerateParameters(List<SqlParameter> list);

        protected internal abstract int NumParameters { get; }

        public abstract SqlPreCommandSimple ToSimple();

        public override string ToString()
        {
            return this.PlainSql();
        }

        protected internal abstract SqlPair Split(ref int remainingParameters);

        public IEnumerable<SqlPreCommand> Splits(int numParams)
        {
            SqlPreCommand rem = this;
            while (rem != null)
            {
                int remainingParams = numParams;
                SqlPreCommand.SqlPair pair = rem.Split(ref remainingParams);
                if (pair.New == null)
                    throw new ApplicationException(Resources.ThereIsASqlPreComandSimpleWithMoreThan0Parameters.Formato(numParams));

                yield return pair.New;

                rem = pair.Remainig;
            }
        }

        public static SqlPreCommand Combine(Spacing spacing, params SqlPreCommand[] sentences)
        {
            if (sentences.Contains(null))
                sentences = sentences.NotNull().ToArray();

            if (sentences.Length == 0)
                return null;

            if (sentences.Length == 1)
                return sentences[0];

            return new SqlPreCommandConcat(spacing, sentences);
        }
    }

    public static class SqlPreCommandExtensions
    {
        public static SqlPreCommand Combine(this IEnumerable<SqlPreCommand> preCommands, Spacing spacing)
        {
            return SqlPreCommand.Combine(spacing, preCommands.ToArray());
        }

        static readonly Regex regex = new Regex(@"@[_\p{Ll}\p{Lu}\p{Lt}\p{Lo}\p{Nl}][_\p{Ll}\p{Lu}\p{Lt}\p{Lo}\p{Nl}\p{Nd}]*");
        /// <summary>
        /// For debugging purposes
        /// </summary>
        public static string PlainSql(this SqlPreCommand command)
        {
            SqlPreCommandSimple cs = command.ToSimple();
            if (cs.Parameters == null)
                return cs.Sql;

            var dic = cs.Parameters.ToDictionary(a=>a.ParameterName, a=>Encode(a.Value)); 

            return regex.Replace(cs.Sql, m=> dic.TryGetC(m.Value) ?? m.Value);
        }

        static string Encode(object value)
        {
            if (value == null || value == DBNull.Value)
                return "NULL";

            if (value is string)
                return "\'" + ((string)value).Replace("'", "''") + "'";

            if (value is DateTime)
                ((DateTime)value).ToUniversalTime();

            if (value is bool)
                (((bool)value) ? 1 : 0).ToString();

            return value.ToString();
        }
    }

    public class SqlPreCommandSimple : SqlPreCommand
    {
        public string Sql { get; private set; }
        public List<SqlParameter> Parameters { get; private set; }
        internal IdentifiableEntity EntitiesToUpdate { get;  set; }

        public SqlPreCommandSimple(string sql)
        {
            this.Sql = sql;
        }

        public SqlPreCommandSimple(string sql, List<SqlParameter> parameters)
        {
            this.Sql = sql;
            this.Parameters = parameters;
        }

        public override IEnumerable<SqlPreCommandSimple> Leaves()
        {
            yield return this;
        }

        protected internal override void GenerateScript(StringBuilder sb)
        {
            sb.Append(Sql);
        }

        protected internal override void GenerateParameters(List<SqlParameter> list)
        {
            if (Parameters != null)
                list.AddRange(Parameters);
        }

        public override SqlPreCommandSimple ToSimple()
        {
            return this;
        }

        protected internal override int NumParameters
        {
            get { return Parameters.TryCS(p => p.Count) ?? 0; }
        }

        protected internal override SqlPair Split(ref int remainingParameters)
        {
            return (remainingParameters -= this.NumParameters) >= 0 ?
                 new SqlPair(this, null) :
                 new SqlPair(null, this);
        }
    }

    public class SqlPreCommandConcat : SqlPreCommand
    {
        public Spacing Spacing { get; private set; }
        public SqlPreCommand[] Commands { get; private set; }

        internal SqlPreCommandConcat(Spacing spacing, SqlPreCommand[] commands)
        {
            this.Spacing = spacing;
            this.Commands = commands;
        }

        public override IEnumerable<SqlPreCommandSimple> Leaves()
        {
            return Commands.SelectMany(c => c.Leaves());
        }

        protected internal override void GenerateScript(StringBuilder sb)
        {
            string sep = separators[Spacing];
            bool borrar = false;
            foreach (SqlPreCommand com in Commands)
            {
                com.GenerateScript(sb);
                sb.Append(sep);
                borrar = true;
            }

            if (borrar) sb.Remove(sb.Length - sep.Length, sep.Length);
        }

        protected internal override void GenerateParameters(List<SqlParameter> list)
        {
            foreach (SqlPreCommand com in Commands)
                com.GenerateParameters(list);
        }

        public override SqlPreCommandSimple ToSimple()
        {
            StringBuilder sb = new StringBuilder();
            GenerateScript(sb);

            List<SqlParameter> parameters = new List<SqlParameter>();
            GenerateParameters(parameters);

            return new SqlPreCommandSimple(sb.ToString(), parameters);
        }

        static Dictionary<Spacing, string> separators = new Dictionary<Spacing, string>()
        {
            {Spacing.Simple, ";\r\n"},
            {Spacing.Double, ";\r\n\r\n"},
            {Spacing.Triple, ";\r\n\r\n\r\n"},
        };

        protected internal override int NumParameters
        {
            get { return Commands.Sum(c => c.NumParameters); }
        }

        protected internal override SqlPair Split(ref int remParameters)
        {
            SqlPair lastPair = null;
            int i = 0;
            for (; i < Commands.Length && (lastPair == null || lastPair.Remainig == null); i++)
                lastPair = Commands[i].Split(ref remParameters);

            //i es la posicion del siguiente a procesar
            if (i == Commands.Length && lastPair.Remainig == null)
                return new SqlPair(this, null);

            if (i == 0 || i == 1 && lastPair.New == null)
                return new SqlPair(null, this);

            return new SqlPair(
               Commands.Take(i - 1).And(lastPair.New).Combine(Spacing),
               Commands.Skip(i).PreAnd(lastPair.Remainig).Combine(Spacing));
        }

    }

}

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