Click here to Skip to main content
15,891,033 members
Articles / Web Development / HTML

Signum Framework Tutorials Part 2 – Southwind Logic

Rate me:
Please Sign up or sign in to vote.
4.45/5 (6 votes)
15 Nov 2012LGPL325 min read 31.4K   1K   22  
In this part, we will focus on writing business logic, LINQ queries and explain inheritance
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Signum.Engine;
using System.Data;
using Signum.Engine.Maps;
using Signum.Utilities;
using Signum.Entities;
using Signum.Engine.SchemaInfoTables;
using Signum.Engine.Properties;

namespace Signum.Engine
{
    internal static class SchemaComparer
    {
        static readonly Dictionary<string, DiffViewIndex> defaultIndexes = new Dictionary<string, DiffViewIndex>();

        public static SqlPreCommand SynchronizeSchema(Replacements replacements)
        {
            Dictionary<string, DiffTable> database = GetDatabaseDescription();

            Dictionary<string, ITable> model = Schema.Current.GetDatabaseTables(); 
                
               

            Dictionary<ITable, Dictionary<string, UniqueIndex>> modelIndices = model.Values.ToDictionary(t => t, t =>t.GeneratUniqueIndexes().ToDictionary(a=>a.IndexName, "Indexes for {0}".Formato(t.Name)));

            //use database without replacements to just remove indexes
            SqlPreCommand dropIndices =
                 Synchronizer.SynchronizeScript(database, model,
                 (tn, dif) => dif.Indices.Values.Select(dix=> dix.ViewName == null? SqlBuilder.DropIndex(tn, dix.IndexName): 
                                                                                SqlBuilder.DropViewIndex(dix.ViewName, dix.IndexName)).Concat(
                              dif.FreeIndices.Select(v=> SqlBuilder.DropIndex(tn, v.IndexName))).Combine(Spacing.Simple),
                 null,
                 (tn, dif, tab) => 
                     {
                         var removeIndexes = Synchronizer.SynchronizeScript(dif.Indices ?? defaultIndexes, modelIndices[tab],
                                    (i, dix) => dix.ViewName == null? SqlBuilder.DropIndex(tn, dix.IndexName): 
                                                                      SqlBuilder.DropViewIndex(dix.ViewName, dix.IndexName), null, null, Spacing.Simple);
                     
                         List<string> removedOrRenamedColumns = dif.Colums.Keys.Except(tab.Columns.Keys).ToList();

                         var removeFreeIndexes = dif.FreeIndices == null? null : 
                             (from i in dif.FreeIndices
                              where i.Columns.Any(c => removedOrRenamedColumns.Contains(c))
                              select i.Columns.All(c => removedOrRenamedColumns.Contains(c)) ?
                                 SqlBuilder.DropIndex(tn, i.IndexName) :
                                 SqlBuilder.DropIndexCommented(tn, i.IndexName)).Combine(Spacing.Simple);

                         return new[] { removeIndexes, removeFreeIndexes }.Combine(Spacing.Double);
                     },
                 Spacing.Double);

            SqlPreCommand dropForeignKeys =
                 Synchronizer.SynchronizeScript(database, model, 
                 (tn, dif) => dif.Colums.Values.Select(c => c.ForeingKeyName != null ? SqlBuilder.AlterTableDropConstraint(dif.Name, c.ForeingKeyName) : null).Combine(Spacing.Simple),
                 null,
                 (tn, dif, tab) => Synchronizer.SynchronizeScript(dif.Colums, tab.Columns,
                     (cn, col) => col.ForeingKeyName != null ? SqlBuilder.AlterTableDropConstraint(tn, col.ForeingKeyName) : null,
                     null,
                     (cn, coldb, colModel) => coldb.EqualForeignKey(tn, colModel) || coldb.ForeingKeyName == null ? null : SqlBuilder.AlterTableDropConstraint(tn, coldb.ForeingKeyName),
                     Spacing.Simple),
                 Spacing.Double);

            SqlPreCommand tables =
                Synchronizer.SynchronizeReplacing(replacements, Replacements.KeyTables,
                database,
                model,
                (tn, dif) => SqlBuilder.DropTable(tn),
                (tn, tab) => SqlBuilder.CreateTableSql(tab),
                (tn, dif, tab) =>
                    SqlPreCommand.Combine(Spacing.Simple,
                    dif.Name != tab.Name ? SqlBuilder.RenameTable(dif.Name, tab.Name) : null,
                    Synchronizer.SynchronizeReplacing(replacements, Replacements.KeyColumnsForTable(tn),
                    dif.Colums,
                    tab.Columns,
                    (cn, difCol) => SqlPreCommand.Combine(Spacing.Simple, 
                                    difCol.DefaultConstraintName.HasText() ?  SqlBuilder.AlterTableDropConstraint(tn, difCol.DefaultConstraintName) : null, 
                                    SqlBuilder.AlterTableDropColumn(tn, cn)),
                    (cn, tabCol) => SqlBuilder.AlterTableAddColumn(tn, tabCol),
                    (cn, difCol, tabCol) =>
                        SqlPreCommand.Combine(Spacing.Simple,
                            difCol.Name == tabCol.Name ? null : SqlBuilder.RenameColumn(tn, difCol.Name, tabCol.Name),
                            difCol.Equals(tabCol) ? null : SqlBuilder.AlterTableAlterColumn(tn, tabCol)),
                    Spacing.Simple)), Spacing.Double);

            var tableReplacements = replacements.TryGetC(Replacements.KeyTables);
            if (tableReplacements != null)
                replacements[Replacements.KeyTablesInverse] = tableReplacements.Inverse();

            SqlPreCommand addForeingKeys =
                 Synchronizer.SynchronizeScript(database, model,
                 null,
                 (tn, tab) => SqlBuilder.AlterTableForeignKeys(tab),
                 (tn, dif, tab) => Synchronizer.SynchronizeScript(dif.Colums, tab.Columns,
                     null,
                     (cn, colModel) => colModel.ReferenceTable != null ? SqlBuilder.AlterTableAddConstraintForeignKey(tn, colModel.Name, colModel.ReferenceTable.Name) : null,
                     (cn, coldb, colModel) => coldb.EqualForeignKey(tn, colModel) || colModel.ReferenceTable == null ? null : SqlBuilder.AlterTableAddConstraintForeignKey(tn, colModel.Name, colModel.ReferenceTable.Name),
                     Spacing.Simple),
                 Spacing.Double);


            SqlPreCommand addIndices =
                 Synchronizer.SynchronizeScript(database, model, 
                 null,
                 (tn, tab) => SqlBuilder.CreateAllIndices(tab, modelIndices[tab].Values), 
                 (tn, dif, tab) => 
                     {
                         var createIndexes = Synchronizer.SynchronizeScript(dif.Indices ?? defaultIndexes, modelIndices[tab], null,
                                    (i, index) => SqlBuilder.CreateUniqueIndex(index), null, Spacing.Simple);
                         
                         var createFreeIndexes = Synchronizer.SynchronizeScript(dif.Colums, tab.Columns, null, 
                             (cn, col)=>col.ReferenceTable == null? null: SqlBuilder.CreateMultipleIndex(tab, col), null, Spacing.Simple);
                             
                         return new[] { createIndexes, createFreeIndexes }.Combine(Spacing.Double);
                     },
                 Spacing.Double);

            return SqlPreCommand.Combine(Spacing.Triple, dropIndices, dropForeignKeys, tables, addForeingKeys, addIndices);
        }

        static Dictionary<string, DiffTable> GetDatabaseDescription()
        {
            var database = (from t in Database.View<SysTables>()
                            where !Database.View<SysExtendedProperties>().Any(a => a.major_id == t.object_id && a.name == "microsoft_database_tools_support")
                            select new DiffTable
                            {
                                Name = t.name,
                                Colums = (from c in Database.View<SysColumns>().Where(c => c.object_id == t.object_id)
                                          join type in Database.View<SysTypes>() on c.user_type_id equals type.user_type_id
                                          join ctr in Database.View<SysObjects>().DefaultIfEmpty() on c.default_object_id equals ctr.object_id
                                          select new DiffColumn
                                          {
                                              Name = c.name,
                                              DbType = ToSqlDbType(type.name),
                                              Nullable = c.is_nullable,
                                              Length = c.max_length,
                                              Precission = c.precision,
                                              Scale = c.scale,
                                              Identity = c.is_identity,
                                              DefaultConstraintName = ctr.name,
                                              PrimaryKey = (from i in Database.View<SysIndexes>()
                                                            where i.object_id == c.object_id && i.is_primary_key
                                                            join ic in Database.View<SysIndexColumn>() on new { i.object_id, i.index_id } equals new { ic.object_id, ic.index_id }
                                                            where ic.column_id == c.column_id
                                                            select i.name).Any(),
                                              ForeingKeyName = (from fkc in Database.View<SysForeignKeyColumns>()
                                                                where fkc.parent_object_id == c.object_id && fkc.parent_column_id == c.column_id
                                                                join fk in Database.View<SysForeignKeys>().DefaultIfEmpty() on fkc.constraint_object_id equals fk.object_id
                                                                select fk.name).SingleOrDefault(),
                                          }).ToDictionary(a => a.Name),

                                Indices = (from i in Database.View<SysIndexes>()
                                           where i.object_id == t.object_id && !i.is_primary_key && i.is_unique && i.name.StartsWith("IX_")
                                           select new DiffViewIndex { IndexName = i.name }).ToList().Concat(
                                           (from v in Database.View<SysViews>()
                                            where v.name.StartsWith("VIX_" + t.name + "_")
                                            join i in Database.View<SysIndexes>() on v.object_id equals i.object_id
                                            where !i.is_primary_key && i.is_unique && i.name.StartsWith("IX_")
                                            select new DiffViewIndex { IndexName = i.name, ViewName = v.name }).ToList()).ToDictionary(a=>a.IndexName),


                                FreeIndices = (from i in Database.View<SysIndexes>()
                                               where i.object_id == t.object_id && !i.is_primary_key && !(i.is_unique && i.name.StartsWith("IX_"))
                                               select new DiffFreeIndex
                                               {
                                                   IndexName = i.name,
                                                   Columns = (from ic in Database.View<SysIndexColumn>().Where(ic => ic.object_id == t.object_id && ic.index_id == i.index_id)
                                                              join c in Database.View<SysColumns>().Where(ic => ic.object_id == t.object_id)
                                                                 on ic.column_id equals c.column_id
                                                              select c.name).ToList()
                                               }).ToList()
                            }).ToDictionary(c => c.Name);

            return database;
        }


        public static SqlDbType ToSqlDbType(string str)
        {
            if(str == "numeric")
                return SqlDbType.Decimal;

            return str.ToEnum<SqlDbType>(true);
        }

        static SqlPreCommand SyncronizeTables(Dictionary<string, DiffTable> database, Dictionary<string, ITable> model, Func<string, DiffTable, SqlPreCommand> dropTable, Func<string, ITable, SqlPreCommand> createTable, Func<string, DiffColumn, SqlPreCommand> dropColumn, Func<string, IColumn, SqlPreCommand> createColumn, Func<string, DiffColumn, IColumn, SqlPreCommand> mergeColumn)
        {
            return Synchronizer.SynchronizeScript(database, model, dropTable, createTable, (tn, dif, tab) =>
                Synchronizer.SynchronizeScript(dif.Colums, tab.Columns, dropColumn, createColumn, mergeColumn, Spacing.Simple),
                Spacing.Double);
        }

        public static SqlPreCommand RenameFreeIndexes()
        {
            var indices = (from t in Database.View<SysTables>()
                           join i in Database.View<SysIndexes>() on t.object_id equals i.object_id
                           where !i.is_primary_key && !i.is_unique && i.name.StartsWith("IX_")
                           select new
                           {
                               Table = t.name,
                               Index = i.name,
                           }).ToList();

            return indices.Select(a => SqlBuilder.RenameIndex(a.Table, a.Index, "F" + a.Index)).Combine(Spacing.Simple);  
        }
    }

    internal class DiffTable
    {
        public string Name;
        public Dictionary<string, DiffColumn> Colums;

        public Dictionary<string, DiffViewIndex> Indices;
        public List<DiffFreeIndex> FreeIndices;
    }

    internal class DiffFreeIndex
    {
        public string IndexName; 
        public List<string> Columns;

        public override string ToString()
        {
            return "{0} ({1})".Formato(IndexName, Columns.ToString(", "));
        }
    }

    internal class DiffViewIndex
    {
        public string IndexName; 
        public string ViewName;

        public override string ToString()
        {
            if (ViewName == null)
                return IndexName;
            else
                return "{0} + {1}".Formato(ViewName, IndexName); 
        }
    }

    internal class DiffColumn : IEquatable<IColumn>
    {
        public string Name;
        public SqlDbType DbType;
        public bool Nullable;
        public int Length; 
        public int Precission;
        public int Scale;
        public bool Identity;
        public bool PrimaryKey;

        public string ForeingKeyName; 

        public string DefaultConstraintName;

        public bool Equals(IColumn other)
        {
            var result = 
                   DbType == other.SqlDbType
                && Nullable == other.Nullable
                && (other.Size == null || other.Size.Value == Precission || other.Size.Value == Length / 2 || other.Size.Value == int.MaxValue && Length == -1)
                && (other.Scale == null || other.Scale.Value == Scale) 
                && Identity == other.Identity
                && PrimaryKey == other.PrimaryKey;

            return result;
        }
 
        internal bool EqualForeignKey(string tableName, IColumn colModel)
        {
            return ForeingKeyName == colModel.ReferenceTable.TryCC(rt => SqlBuilder.ForeignKeyName(tableName, colModel.Name));  
        }
    }
}

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 GNU Lesser General Public License (LGPLv3)


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