linq2db / linq2db

Linq to database provider.
MIT License
2.99k stars 457 forks source link

[T4] MonoDevelop (AKA VS for macOS) support #1971

Closed goors closed 2 years ago

goors commented 5 years ago

I have this issue on Osx with Visual studio.

UserDb.tt(5,5): Error: Could not resolve include file '$(LinqToDBT4SqlServerTemplatesDirectory)LinqToDB.SqlServer.Tools.ttinclude'.

Where does LinqToDBT4SqlServerTemplatesDirectory is coming from? I tried feeding in the absolute path where nuget package is located but then these ttinclude files complain about missing this dll or that dll, that is all because of the path problems.

Is this a bug or?

frankiDotNet commented 5 years ago

I am not sure, but try to locate it with macros:

<#@ template language="C#" debug="True" hostSpecific="True"                          #>
<#@ output extension=".generated.cs"                                                 #>
<#@ include file="$(ProjectDir)LinqToDB.Templates\LinqToDB.SqlServer.Tools.ttinclude" #>
<#@ include file="$(ProjectDir)LinqToDB.Templates\PluralizationService.ttinclude"    #>
MaceWindu commented 5 years ago

This property declared in nuget props file in build folder, but I suspect you will need to review all T4 includes and fix pathes manually, as we never expected it to run on non-windows systems.

I'm surprised that T4 templates supported in VS for osx as even on Windows VS runs them using netfx even for .net core projects...

goors commented 5 years ago

@Franki1986 same thing. It looks in a project dir, but LinqToDB.Templates is in /home/.nuget/.... dir.

goors commented 5 years ago

@MaceWindu is there any way that you guys can fix this? I mean to install windows only to be able to use t4 is kind of, i dont know, does not make much sense to do. I did change manually all include packages path with absolute path but then I start getting error Could not load file or assembly 'System.Data.Entity.Design

MaceWindu commented 5 years ago

System.Data.Entity.Design used by pluralization logic, so probably you can try to remove it. It is in PluralizationService.ttinclude - just remove PluralizationService class use (it is optional here).

In theory we can fix osx suppport, but without access to VS for OSX it will be hard (and I'm not willing to buy mac just for that :smile: ). Problem is that while T4 itself is simple and should work if we fix some platform specific issues like used classes (PluralizationService) and pathes, we also depend on:

We actually want to move from T4 to more independent tooling, something like efcore have for scaffolding, but we don't have enough resources for it, so who knows when it will happen.

I think we can do following - if you will provide list of all changes you did to make it work, we can try to update our T4 packages to work on mac (maybe several test iterations needed).

goors commented 5 years ago

Hi, thanks for the reply. I did this:

  1. edit .tt file and remove pluralization and add absolute path to LinqToDB.SqlServer.Tools.ttinclude
  2. edit LinqToDB.Tools.ttinclude and add absolute path to linq2db.dll

What I am getting now is The type or namespace name GeneratedTextTransformation could not be found.

Funny thing is when i print string path = getPathFor($(LinqToDBT4SqlServerTemplatesDirectory)) it is in ocrrect place on windows $HOME/.nuget/etc.... but when i try to print that path on mac i get empty string.

MaceWindu commented 5 years ago

The type or namespace name GeneratedTextTransformation could not be found. that is very strange, as it is a type, T4 creates. Could it be that you removed hostSpecific="True" from template tag?

goors commented 5 years ago

No, hostSpecific="True" is there.

MaceWindu commented 5 years ago

I'm lost. Could you create new T4 template (vs for mac should have them) and see if there is something special in it? or maybe just attach them to this issue

goors commented 5 years ago

@MaceWindu hi, sorry for the late reply. Here is my config:

<#@ template language="C#" debug="True" hostSpecific="True"                       #>
<#@ output extension=".generated.cs"                                              #>
<#@ include file="$(ProjectDir)/LinqToDB.Templates/LinqToDB.MySql.Tools.ttinclude" #>
<#@ include file="$(ProjectDir)/LinqToDB.Templates/PluralizationService.ttinclude" #>
<#
    /*
        1. Copy this file to a folder where you would like to generate your data model,
           rename it, and delete .txt extension. For example:

            MyProject
                DataModels
                    MyDatabase.tt

        2. Modify the connection settings below to connect to your database.

        3. Add connection string to the web/app.config file:

            <connectionStrings>
                <add name="MyDatabase" providerName="System.Data.SqlClient"
                    connectionString="Data Source=.;Database=MyDatabase;User Id=User;Password=TestPassword;" />
            </connectionStrings>

        4. To access your database use the following code:

            using (var db = new MyDatabaseDB())
            {
                var q =
                    from c in db.Customers
                    select c;

                foreach (var c in q)
                    Console.WriteLine(c.ContactName);
            }

        5. See more at https://github.com/linq2db/t4models

        If you need to use the Microsoft.SqlServer.Types namespace, install the Microsoft.SqlServer.Types nuget,
        and replace the following include at the top of this file:

            "$(ProjectDir)LinqToDB.Templates\LinqToDB.SqlServer.Tools.ttinclude"

        with

            "$(ProjectDir)LinqToDB.Templates\LinqToDB.SqlServer.SqlTypes.Tools.ttinclude"
    */

    NamespaceName = "blog.db";
    DataContextName = "BlogDb";
    GenerateConstructors = false;

    LoadMySqlMetadata("sql7.freemysqlhosting.net", "", "", "");
#>

// Disable documentation warnings
#pragma warning disable 1591

<#
    GenerateModel();
#>

This will not work since that pluralize require that System.Data.Entity.Design. After removing it I end up with this error: The type or namespace name GeneratedTextTransformation could not be found. This I got many times since static Action<GeneratedTextTransformation,string> WriteUsing = (tt,s) => tt.WriteLine("using {0};", s); is called many times.

Besides this, I had to change few files, in some i had to add MySql.Data.dll abs. path and in the other i had to add linq2db.dll abs. path.

It would be great if you guys can somehow fix this.

Just as a notice, this works on windows without any problems :)

MaceWindu commented 5 years ago

No, I want to see built-on template, this comment says there are templates like this. Maybe it will have some VS for mac-specific code, which will help us to understand what's wrong.

MaceWindu commented 5 years ago

Fun fact: referenced issue is the only place I found, that mentions that VS for macos supports T4...

goors commented 5 years ago

When i add new .tt file it generates this by default in that .tt file:


<#@ assembly name="System.Core" #>
<#@ import namespace="System.Linq" #>
<#@ import namespace="System.Text" #>
<#@ import namespace="System.Collections.Generic" #>```
MaceWindu commented 5 years ago

hmm, are there any more complex built-in templates with actual functionality? Issue mentions 3 templates..

goors commented 5 years ago

It does. Check photo.

Screenshot-2019-11-15-at-12-30-54

That razor is empty, then t4 and preprocessed generate the same thing as I wrote.

MaceWindu commented 5 years ago

That's a pity. Oh well, I think I will need to find some time and play with virtualbox-ed osx :smiling_imp:

I have one idea you can try right now - enable debug mode for template and try to find generated .cs file - maybe on osx Microsoft generates class with other name, not GeneratedTextTransformation.

set debug="True" in template tag and then try to find generated intermediate cs file (not sure where, somewhere in tmp I assume)

goors commented 5 years ago

I did try to put debug, but it will not even try to run it since it will report this GeneratedTextTransformation error. Freaking hell, laptop 3000e and you can not run something. Frustrating right? Worse thing, is I have windows installed just because of this but is slow as hell on this hardware. It is like apple put in something like if windows delayAll(10sec) :)

MaceWindu commented 5 years ago

You sure? It should generate transformation file, because this error produced when T4 engine tries to compile generated transformation and then execute it. If it really doesn't generate it, maybe you can try same debug with built-in template?

MaceWindu commented 5 years ago

Old, but still good article about t4 debug http://olegsych.com/T4Toolbox/tutorial/troubleshooting-code-generation-errors.html not sure how much of it applies to osx, but I hope at least last section works - this is about file I want to see

goors commented 5 years ago

Here is temp file. https://srv-file7.gofile.io/download/ZuU3Wh/temp.rtf

MaceWindu commented 5 years ago

You are not authorized to download this file.

MaceWindu commented 5 years ago

you can attach file to issue

goors commented 5 years ago
//------------------------------------------------------------------------------
// <auto-generated>
//     This code was generated by a tool.
//     Runtime Version:4.0.30319.42000
//
//     Changes to this file may cause incorrect behavior and will be lost if
//     the code is regenerated.
// </auto-generated>
//------------------------------------------------------------------------------

namespace Microsoft.VisualStudio.TextTemplating {
    using System.Data;
    using LinqToDB.Data;
    using System.Text;
    using LinqToDB.SchemaProvider;
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using MonoDevelop.TextTemplating;

    public partial class GeneratedTextTransformation776afa5f : global::Microsoft.VisualStudio.TextTemplating.TextTransformation {

        private global::Microsoft.VisualStudio.TextTemplating.ITextTemplatingEngineHost hostValue;

        #line 5 "/Users/nikola/projects/blog/LinqToDB.Templates/T4Model.ttinclude"

static Action<GeneratedTextTransformation,string> WriteComment    = (tt,s) => tt.WriteLine("//{0}", s);

Action BeforeGenerateModel = () => {};

bool GenerateProcedureErrors = true;

void GenerateModel()
{
    Model.SetTree();

    BeforeGenerateModel();

    if (GenerationEnvironment.Length > 0 && GenerationEnvironment.ToString().Trim().Length == 0)
        GenerationEnvironment.Length = 0;

    WriteComment(this, "---------------------------------------------------------------------------------------------------");
    WriteComment(this, " <auto-generated>");
    WriteComment(this, "    This code was generated by T4Model template for T4 (https://github.com/linq2db/t4models).");
    WriteComment(this, "    Changes to this file may cause incorrect behavior and will be lost if the code is regenerated.");
    WriteComment(this, " </auto-generated>");
    WriteComment(this, "---------------------------------------------------------------------------------------------------");

    Model.Render(this);
}

void Trim()
{
    var arr = new[] { '\r', '\n', ' ' };
    while (GenerationEnvironment.Length > 0 && arr.Contains(GenerationEnvironment[GenerationEnvironment.Length - 1]))
        GenerationEnvironment.Length--;

    WriteLine("");
}

static Action<GeneratedTextTransformation,string> WriteUsing = (tt,s) => tt.WriteLine("using {0};", s);

void RenderUsings(List<string> usings)
{
    var q =
        from ns in usings.Distinct()
        group ns by ns.Split('.')[0];

    var groups =
        (from ns in q where ns.Key == "System"                select ns).Concat
        (from ns in q where ns.Key != "System" orderby ns.Key select ns);

    foreach (var gr in groups)
    {
        foreach (var ns in from s in gr orderby s select s)
            WriteUsing(this, ns);

        WriteLine("");
    }

    Trim();
}

// Base data types.
//
public interface ITree
{
    ITree              Parent { get; set; }
    IEnumerable<ITree> GetNodes();
    void               SetTree();
}

ModelSource Model = new ModelSource();

public partial class ModelSource : ITree
{
    public int CurrentNamespace = 0;

    public List<string>    Usings     = new List<String>    { "System"        };
    public List<Namespace> Namespaces = new List<Namespace> { new Namespace() };

    public Namespace      Namespace { get { return Namespaces[CurrentNamespace];       } }
    public List<TypeBase> Types     { get { return Namespaces[CurrentNamespace].Types; } }

    public virtual void Render(GeneratedTextTransformation tt)
    {
        tt.RenderUsings(Usings);
        tt.WriteLine("");

        foreach (var nm in Namespaces)
        {
            nm.Render(tt);
            tt.WriteLine("");
        }

        tt.Trim();
    }

    public ITree              Parent     { get; set; }
    public IEnumerable<ITree> GetNodes() { return Namespaces; }

    public void SetTree()
    {
        foreach (var ch in GetNodes())
        {
            ch.Parent = this;
            ch.SetTree();
        }
    }
}

static Action<GeneratedTextTransformation,string> WriteBeginNamespace = (tt,s) => { tt.WriteLine("namespace {0}", s); tt.WriteLine("{"); };
static Action<GeneratedTextTransformation>        WriteEndNamespace   =  tt    => tt.WriteLine("}");

public partial class Namespace : ITree
{
    public string         Name;
    public List<TypeBase> Types  = new List<TypeBase>();
    public List<string>   Usings = new List<string>();

    public virtual void Render(GeneratedTextTransformation tt)
    {
        if (!string.IsNullOrEmpty(Name))
        {
            WriteBeginNamespace(tt, Name);
            tt.PushIndent("\t");
        }

        tt.RenderUsings(Usings);

        foreach (var t in Types)
        {
            t.Render(tt);
            tt.WriteLine("");
        }

        tt.Trim();

        if (!string.IsNullOrEmpty(Name))
        {
            tt.PopIndent();
            WriteEndNamespace(tt);
        }
    }

    public ITree              Parent     { get; set; }
    public IEnumerable<ITree> GetNodes() { return Types; }

    public void SetTree()
    {
        foreach (var ch in GetNodes())
        {
            ch.Parent = this;
            ch.SetTree();
        }
    }
}

public interface IClassMember : ITree
{
}

public enum AccessModifier
{
    Public,
    Protected,
    Internal,
    Private,
    Partial
}

public abstract partial class TypeBase : IClassMember
{
    public AccessModifier  AccessModifier = AccessModifier.Public;
    public string          Name;
    public bool            IsPartial  = true;
    public List<string>    Comment    = new List<string>();
    public List<Attribute> Attributes = new List<Attribute>();
    public string          Conditional;

    public abstract void Render(GeneratedTextTransformation tt);

    protected virtual void BeginConditional(GeneratedTextTransformation tt)
    {
        if (Conditional != null)
        {
            tt.RemoveSpace();
            tt.WriteLine("#if " + Conditional);
            tt.WriteLine("");
        }
    }

    protected virtual void EndConditional(GeneratedTextTransformation tt)
    {
        if (Conditional != null)
        {
            tt.RemoveSpace();
            tt.WriteLine("");
            tt.RemoveSpace();
            tt.WriteLine("#endif");
        }
    }

    public          ITree              Parent { get; set; }
    public abstract IEnumerable<ITree> GetNodes();
    public abstract void               SetTree ();
}

static Action<GeneratedTextTransformation,Class> WriteBeginClass = (tt,cl) =>
{
    tt.Write(cl.AccessModifier.ToString().ToLower() + " ");
    if (cl.IsStatic)  tt.Write("static ");
    if (cl.IsPartial) tt.Write("partial ", cl.Name);
    tt.Write("class {0}", cl.Name);

    if (!string.IsNullOrEmpty(cl.BaseClass) || cl.Interfaces.Count > 0)
    {
        var arr = new[] { cl.BaseClass }.Concat(cl.Interfaces)
            .Where(n => n != null)
            .ToArray();

        tt.Write(" : ");
        tt.Write(string.Join(", ", arr));
    }

    tt.WriteLine("");
    tt.WriteLine("{");
};

static Action<GeneratedTextTransformation> WriteEndClass = tt => tt.WriteLine("}");

public partial class Class : TypeBase
{
    public string             BaseClass;
    public bool               IsStatic   = false;
    public List<string>       Interfaces = new List<string>();
    public List<IClassMember> Members    = new List<IClassMember>();

    public Class()
    {
    }

    public Class(string name, params IClassMember[] members)
    {
        Name = name;
        Members.AddRange(members);
    }

    public override void Render(GeneratedTextTransformation tt)
    {
        BeginConditional(tt);

        foreach (var c in Comment)
            tt.WriteLine("//" + c);

        if (Attributes.Count > 0)
        {
            var aa = Attributes.Where(a => !a.IsSeparated).ToList();

            if (aa.Count > 0)
            {
                tt.Write("[");

                for (var i = 0; i < aa.Count; i++)
                {
                    if (i > 0) SkipSpacesAndInsert(tt, ", ");
                    aa[i].Render(tt);
                }

                tt.WriteLine("]");
            }

            aa = Attributes.Where(a => a.IsSeparated).ToList();

            foreach (var a in aa)
            {
                tt.Write("[");
                a.Render(tt);
                tt.WriteLine("]");
            }
        }

        WriteBeginClass(tt, this);
        tt.PushIndent("\t");

        foreach (var cm in Members)
        {
            if (cm is MemberBase)
            {
                var m = (MemberBase)cm;

                if (!(m is MemberGroup))
                    m.BeginConditional(tt, false);

                foreach (var c in m.Comment)
                    WriteComment(tt, c);

                if (m.Attributes.Count > 0)
                {
                    var q =
                        from a in m.Attributes
                        group a by a.Conditional ?? "";

                    foreach (var g in q)
                    {
                        if (g.Key.Length > 0)
                        {
                            tt.RemoveSpace();
                            tt.WriteLine("#if " + g.Key);
                        }

                        var attrs = g.ToList();

                        tt.Write("[");

                        for (var i = 0; i < attrs.Count; i++)
                        {
                            if (i > 0) SkipSpacesAndInsert(tt, ", ");
                            attrs[i].Render(tt);
                        }

                        tt.WriteLine("]");

                        if (g.Key.Length > 0)
                        {
                            tt.RemoveSpace();
                            tt.WriteLine("#endif");
                        }
                    }
                }

                m.Render(tt, false);
                if (m.InsertBlankLineAfter)
                    tt.WriteLine("");

                if (!(m is MemberGroup))
                    m.EndConditional(tt, false);
            }
            else if (cm is TypeBase)
            {
                var t = (TypeBase)cm;

                t.Render(tt);
                tt.WriteLine("");
            }
        }

        tt.Trim();

        tt.PopIndent();
        WriteEndClass(tt);

        EndConditional(tt);
    }

    public override IEnumerable<ITree> GetNodes()
    {
        return Members;
    }

    public override void SetTree()
    {
        foreach (var ch in GetNodes())
        {
            ch.Parent = this;
            ch.SetTree();
        }
    }
}

public abstract partial class MemberBase : IClassMember
{
    public string          ID;
    public AccessModifier  AccessModifier = AccessModifier.Public;
    public string          Name;
    public string          Type;
    public List<string>    Comment = new List<string>();
    public string          EndLineComment;
    public List<Attribute> Attributes = new List<Attribute>();
    public bool            InsertBlankLineAfter = true;
    public string          Conditional;

    public int AccessModifierLen;
    public int ModifierLen;
    public int TypeLen;
    public int NameLen;
    public int ParamLen;
    public int BodyLen;

    public virtual  int  CalcModifierLen() { return 0; }
    public abstract int  CalcBodyLen    ();
    public virtual  int  CalcParamLen   () { return 0; }
    public abstract void Render         (GeneratedTextTransformation tt, bool isCompact);

    public virtual void BeginConditional(GeneratedTextTransformation tt, bool isCompact)
    {
        if (Conditional != null)
        {
            tt.RemoveSpace();
            tt.WriteLine("#if " + Conditional);
            if (!isCompact)
                tt.WriteLine("");
        }
    }

    public virtual void EndConditional(GeneratedTextTransformation tt, bool isCompact)
    {
        if (Conditional != null)
        {
            tt.RemoveSpace();
            tt.WriteLine("#endif");
            if (!isCompact)
                tt.WriteLine("");
        }
    }

    public         ITree              Parent     { get; set; }
    public virtual IEnumerable<ITree> GetNodes() { return Enumerable.Empty<ITree>(); }
    public virtual void               SetTree () {}
}

static Action<GeneratedTextTransformation,string> BeginRegion = (tt,s) => { tt.WriteLine("#region {0}", s); };
static Action<GeneratedTextTransformation>        EndRegion   = (tt)   => { tt.WriteLine("#endregion");     };

public partial class MemberGroup : MemberBase
{
    public string             Region;
    public bool               IsCompact;
    public bool               IsPropertyGroup;
    public List<IClassMember> Members = new List<IClassMember>();
    public List<string>       Errors  = new List<string>();

    public override int  CalcBodyLen() { return 0; }

    public override void Render(GeneratedTextTransformation tt, bool isCompact)
    {
        if (!string.IsNullOrEmpty(Region))
        {
            BeginRegion(tt, Region);
            tt.WriteLine("");
        }

        BeginConditional(tt, isCompact);

        if (Errors.Count > 0 && tt.GenerateProcedureErrors)
        {
            tt.RemoveSpace();
            WriteComment(tt, " Use 'GenerateProcedureErrors=false' to disable errors.");
            foreach (var error in Errors)
            {
                tt.Error(error);

                foreach (var e in error.Split('\n'))
                {
                    tt.RemoveSpace();
                    tt.WriteLine("#error " + e.Trim('\r'));
                }
            }

            tt.WriteLine("");
        }

        if (IsCompact)
        {
            var allMembers = GetTreeNodes(this).OfType<MemberBase>().Where(m => !(m is MemberGroup)).ToList();

            if (allMembers.Count > 0)
            {
                int max = allMembers.Max(m => m.AccessModifier.ToString().Length);
                foreach (var m in allMembers)
                    m.AccessModifierLen = max;

                max = allMembers.Max(m => m.CalcModifierLen());
                foreach (var m in allMembers)
                    m.ModifierLen = max;

                max = allMembers.Max(m => (m.Type ?? "").Length);
                foreach (var m in allMembers)
                    m.TypeLen = max;

                var notHasGetter = allMembers.OfType<Property>().Any(m => m.IsAuto && !m.HasGetter);
                var notHasSetter = allMembers.OfType<Property>().Any(m => m.IsAuto && !m.HasSetter);

                foreach (var p in allMembers.OfType<Property>())
                {
                    if (notHasGetter) p.GetterLen = 13;
                    if (notHasSetter) p.SetterLen = 13;
                }

                max = allMembers.Max(m => m.Name.Length);
                foreach (var m in allMembers)
                    m.NameLen = max;

                max = allMembers.Max(m => m.CalcParamLen());
                foreach (var m in allMembers)
                    m.ParamLen = max;

                max = allMembers.Max(m => m.CalcBodyLen());
                foreach (var m in allMembers)
                    m.BodyLen = max;

                var members =
                (
                    from m in allMembers
                    select new
                    {
                        m,
                        attrs =
                        (
                            from a in m.Attributes
                            group a by a.Name into gr
                            select gr.Select((a,i) => new { a, name = a.Name + "." + i }).ToList() into s
                            from a in s
                            select a
                        ).ToList()
                    }
                ).ToList();

                var attrWeight =
                (
                    from m in members
                    from a in m.attrs
                    group a by a.name into gr
                    select new { gr.Key, Count = gr.Count() }
                ).ToDictionary(a => a.Key, a => a.Count);

                var q =
                    from m in members
                    where m.attrs.Count > 0
                    select new { m, w = m.attrs.Sum(aa => attrWeight[aa.name]) } into m
                    orderby m.w descending
                    select m.m;

                var attrs = new List<string>();

                foreach (var m in q)
                {
                    var list = m.attrs.Select(a => a.name).ToList();

                    if (attrs.Count == 0)
                        attrs.AddRange(list);
                    else
                    {
                        for (var i = 0; i < list.Count; i++)
                        {
                            var nm = list[i];

                            if (!attrs.Contains(nm))
                            {
                                for (var j = i + 1; j < list.Count; j++)
                                {
                                    var idx = attrs.IndexOf(list[j]);

                                    if (idx >= 0)
                                    {
                                        attrs.Insert(idx, nm);
                                        break;
                                    }
                                }
                            }

                            if (!attrs.Contains(nm))
                                attrs.Add(nm);
                        }
                    }
                }

                var mms = members.Select(m =>
                {
                    var arr = new Attribute[attrs.Count];

                    foreach (var a in m.attrs)
                        arr[attrs.IndexOf(a.name)] = a.a;

                    return new { m.m, attrs = arr.ToList() };
                }).ToList();

                var idxs = Enumerable.Range(0, attrs.Count).Select(_ => new List<int>()).ToList();

                for (var i = 0; i < mms.Count; i++)
                    for (var j = 0; j < mms[i].attrs.Count; j++)
                        if (mms[i].attrs[j] != null)
                            idxs[j].Add(i);

                var toRemove = new List<int>();

                for (int i = 1; i < idxs.Count; i++)
                {
                    for (int j = 0; j < i; j++)
                    {
                        if (idxs[j] == null)
                            continue;

                        if (idxs[i].Intersect(idxs[j]).Count() == 0)
                        {
                            foreach (var m in mms)
                            {
                                if (m.attrs[i] != null)
                                {
                                    m.attrs[j] = m.attrs[i];
                                    m.attrs[i] = null;
                                }
                            }

                            idxs[j].AddRange(idxs[i]);
                            idxs[i] = null;
                            toRemove.Add(i);
                            break;
                        }
                    }

                }

                foreach (var n in toRemove.OrderByDescending(i => i))
                    foreach (var m in mms)
                        m.attrs.RemoveAt(n);

                var lens = new int[attrs.Count - toRemove.Count];

                foreach (var m in mms)
                {
                    for (var i = 0; i < m.attrs.Count; i++)
                    {
                        var a = m.attrs[i];

                        if (a != null)
                        {
                            var len = a.Name.Length;

                            if (a.Parameters.Count >= 0)
                                len += a.Parameters.Sum(p => 2 + p.Length);

                            lens[i] = Math.Max(lens[i], len);
                        }
                    }
                }

                foreach (var m in allMembers)
                {
                    if (!(m is MemberGroup))
                        m.BeginConditional(tt, IsCompact);

                    foreach (var c in m.Comment)
                        WriteComment(tt, c);

                    if (attrs.Count > 0)
                    {
                        var ma = mms.First(mr => mr.m == m);

                        if (m.Attributes.Count > 0)
                        {
                            tt.Write("[");

                            for (var i = 0; i < ma.attrs.Count; i++)
                            {
                                var a = ma.attrs[i];

                                if (a == null)
                                {
                                    tt.WriteSpaces(lens[i]);
                                    if (i + 1 < ma.attrs.Count)
                                        tt.Write("  ");
                                }
                                else
                                {
                                    var len = tt.GenerationEnvironment.Length;
                                    a.Render(tt);
                                    len = (tt.GenerationEnvironment.Length - len);

                                    var commaAdded = false;

                                    for (var j = i + 1; j < ma.attrs.Count; j++)
                                    {
                                        if (ma.attrs[j] != null)
                                        {
                                            SkipSpacesAndInsert(tt, ", ");
                                            commaAdded = true;
                                            break;
                                        }
                                    }

                                    if (i + 1 < ma.attrs.Count && !commaAdded)
                                        tt.Write("  ");

                                    tt.WriteSpaces(lens[i] - len);
                                }
                            }

                            tt.Write("] ");
                        }
                        else
                        {
                            tt.WriteSpaces(lens.Sum() + ma.attrs.Count * 2 + 1);
                        }
                    }

                    m.Render(tt, true);

                    if (!IsCompact)
                        tt.WriteLine("");

                    if (!(m is MemberGroup))
                        m.EndConditional(tt, IsCompact);
                }
            }
        }
        else
        {
            foreach (var cm in Members)
            {
                if (cm is MemberBase)
                {
                    var m = (MemberBase)cm;

                    if (!(m is MemberGroup))
                        m.BeginConditional(tt, IsCompact);

                    foreach (var c in m.Comment)
                        WriteComment(tt, c);

                    if (m.Attributes.Count > 0)
                    {
                        var q =
                            from a in m.Attributes
                            group a by a.Conditional ?? "";

                        foreach (var g in q)
                        {
                            if (g.Key.Length > 0)
                            {
                                tt.RemoveSpace();
                                tt.WriteLine("#if " + g.Key);
                            }

                            var attrs = g.ToList();

                            var aa = attrs.Where(a => !a.IsSeparated).ToList();

                            if (aa.Count > 0)
                            {
                                tt.Write("[");

                                for (var i = 0; i < aa.Count; i++)
                                {
                                    if (i > 0) tt.Write(", ");
                                    aa[i].Render(tt);
                                }

                                tt.WriteLine("]");
                            }

                            aa = attrs.Where(a => a.IsSeparated).ToList();

                            foreach (var a in aa)
                            {
                                tt.Write("[");
                                a.Render(tt);
                                tt.WriteLine("]");
                            }

                            if (g.Key.Length > 0)
                            {
                                tt.RemoveSpace();
                                tt.WriteLine("#endif");
                            }
                        }
                    }

                    m.Render(tt, false);

                    if (m.InsertBlankLineAfter)
                        tt.WriteLine("");

                    if (!(m is MemberGroup))
                        m.EndConditional(tt, IsCompact);
                }
                else if (cm is TypeBase)
                {
                    var t = (TypeBase)cm;

                    t.Render(tt);
                    tt.WriteLine("");
                }
            }
        }

        tt.Trim();

        EndConditional(tt, isCompact);

        if (!string.IsNullOrEmpty(Region))
        {
            tt.WriteLine("");
            EndRegion(tt);
        }
    }

    public override IEnumerable<ITree> GetNodes() { return Members; }

    public override void SetTree()
    {
        foreach (var ch in GetNodes())
        {
            ch.Parent = this;
            ch.SetTree();
        }
    }
}

static Action<GeneratedTextTransformation,Field> WriteField = (tt,f) =>
{
    var am = f.AccessModifier.ToString().ToLower();
    var mdf =
        (f.IsStatic   ? " static"   : "") +
        (f.IsReadonly ? " readonly" : "") ;

    tt.Write("{0}{1}{2}{3} {4}{5} {6}",
        am,     LenDiff(f.AccessModifierLen, am),
        mdf,    LenDiff(f.ModifierLen,       mdf),
        f.Type, LenDiff(f.TypeLen,           f.Type),
        f.Name);

    if (f.InitValue != null)
    {
        tt.Write(" = {0}", f.InitValue);        
    }

    tt.Write(";");

    if (!string.IsNullOrEmpty(f.EndLineComment))
    {
        tt.WriteSpaces(f.NameLen - f.Name.Length + f.BodyLen + f.ParamLen - 1);
        tt.Write(" ");
        WriteComment(tt, " " + f.EndLineComment);
    }
    else    
        tt.WriteLine("");
};

public partial class Field : MemberBase
{
    public bool   IsStatic;
    public bool   IsReadonly;
    public string InitValue;

    public Field()
    {
    }

    public Field(string type, string name)
    {
        Type = type;
        Name = name;
    }

    public override int CalcModifierLen()
    {
        return
            (IsStatic   ? " static".  Length : 0) +
            (IsReadonly ? " readonly".Length : 0) ;
    }

    public override int  CalcBodyLen() { return InitValue == null ? 1 : 4 + InitValue.Length; }

    public override void Render(GeneratedTextTransformation tt, bool isCompact)
    {
        WriteField(tt, this);
    }
}

static Action<GeneratedTextTransformation,Event> WriteEvent = (tt,m) =>
{
    var am  = m.AccessModifier.ToString().ToLower();
    var mdf =
        (m.IsStatic  ? " static"  : "") +
        (m.IsVirtual ? " virtual" : "") +
        " event";

    tt.Write("{0}{1}{2}{3} {4}{5} {6};",
        am,     LenDiff(m.AccessModifierLen, am),
        mdf,    LenDiff(m.ModifierLen,       mdf),
        m.Type, LenDiff(m.TypeLen,           m.Type),
        m.Name);

    if (!string.IsNullOrEmpty(m.EndLineComment))
    {
        tt.WriteSpaces(m.NameLen - m.Name.Length + m.BodyLen + m.ParamLen - 1);
        tt.Write(" ");
        WriteComment(tt, " " + m.EndLineComment);
    }
    else
        tt.WriteLine("");
};

public partial class Event : MemberBase
{
    public bool IsStatic;
    public bool IsVirtual;

    public Event()
    {
    }

    public Event(string type, string name)
    {
        Type = type;
        Name = name;
    }

    public override int CalcModifierLen()
    {
        return
            (IsStatic  ? " static". Length : 0) +
            (IsVirtual ? " virtual".Length : 0) +
            " event".Length;
    }

    public override int CalcBodyLen() { return 1; }

    public override void Render(GeneratedTextTransformation tt, bool isCompact)
    {
        WriteEvent(tt, this);
    }
}

static Action<GeneratedTextTransformation,Property,bool> WriteProperty = (tt,p,compact) =>
{
    var am  = p.AccessModifier.ToString().ToLower();
//  var mdf = p.IsVirtual ? " virtual" : "";
    var mdf = p.IsAbstract ? " abstract" : p.IsVirtual ? " virtual" : p.IsOverride ? " override" : p.IsStatic ? " static" : "";

    tt.Write("{0}{1}{2}{3} {4}{5} {6}",
        am,     LenDiff(p.AccessModifierLen, am),
        mdf,    LenDiff(p.ModifierLen,       mdf),
        p.Type, LenDiff(p.TypeLen,           p.Type),
        p.Name);

    Action writeComment = () =>
    {
        if (!string.IsNullOrEmpty(p.EndLineComment))
        {
            tt.Write(" ");
            WriteComment(tt, " " + p.EndLineComment);
        }
        else
            tt.WriteLine("");
    };

    if (p.IsAuto)
    {
        tt.Write(LenDiff(p.NameLen + p.ParamLen, p.Name));

        var len = tt.GenerationEnvironment.Length;

        tt.Write(" { ");

        if (!p.HasGetter)
            tt.Write("private ");
        else if (p.GetterLen == 13)
            tt.Write("        ");
        tt.Write("get; ");

        if (!p.HasSetter)
            tt.Write("private ");
        else if (p.SetterLen == 13)
            tt.Write("        ");
        tt.Write("set; ");

        tt.Write("}");

        if (!string.IsNullOrEmpty(p.EndLineComment))
            tt.WriteSpaces(p.BodyLen - (tt.GenerationEnvironment.Length - len));
        writeComment();
    }
    else
    {
        if (compact)
        {
            tt.Write(LenDiff(p.NameLen + p.ParamLen, p.Name));

            var len = tt.GenerationEnvironment.Length;

            tt.Write(" { ");

            if (p.HasGetter)
            {
                tt.Write("get { ");
                foreach (var t in p.GetBody)
                    tt.Write("{0} ", t);
                tt.Write("} ");
            }

            if (p.HasSetter)
            {
                tt.Write("set { ");
                foreach (var t in p.SetBody)
                    tt.Write("{0} ", t);
                tt.Write("} ");
            }

            tt.Write("}");

            if (!string.IsNullOrEmpty(p.EndLineComment))
                tt.WriteSpaces(p.BodyLen - (tt.GenerationEnvironment.Length - len));
            writeComment();
        }
        else
        {
            writeComment();

            tt.WriteLine("{");
            tt.PushIndent("\t");

            if (p.HasGetter)
            {
                if (p.GetBody.Count == 1)
                {
                    tt.WriteLine("get {{ {0} }}", p.GetBody[0]);
                }
                else
                {
                    tt.WriteLine("get");
                    tt.WriteLine("{");
                    tt.PushIndent("\t");

                    foreach (var t in p.GetBody)
                        tt.WriteLine(t);

                    tt.PopIndent();
                    tt.WriteLine("}");
                }
            }

            if (p.HasSetter)
            {
                if (p.SetBody.Count == 1)
                {
                    tt.WriteLine("set {{ {0} }}", p.SetBody[0]);
                }
                else
                {
                    tt.WriteLine("set");
                    tt.WriteLine("{");
                    tt.PushIndent("\t");

                    foreach (var t in p.SetBody)
                        tt.WriteLine(t);

                    tt.PopIndent();
                    tt.WriteLine("}");
                }
            }

            tt.PopIndent();
            tt.WriteLine("}");
        }
    }
};

public partial class Property : MemberBase
{
    public bool         IsAuto    = true;
    public string       InitValue;
    public bool         IsVirtual;
    public bool         IsOverride;
    public bool         IsAbstract;
    public bool         IsStatic;
    public bool         HasGetter = true;
    public bool         HasSetter = true;
    public List<string> GetBody   = new List<string>();
    public List<string> SetBody   = new List<string>();

    public int GetterLen = 5;
    public int SetterLen = 5;

    public Property()
    {
    }

    public Property(string type, string name, IEnumerable<string> getBody = null, IEnumerable<string> setBody = null)
    {
        Type = type;
        Name = name;

        InitBody(getBody, setBody);
    }

    public override int CalcModifierLen()
    {
        return IsVirtual ? " virtual".Length : 0;
    }

    public override int CalcBodyLen()
    {
        if (IsAuto)
            return 4 + GetterLen + SetterLen; // ' { get; set; }'

        var len = " {".Length;

        if (HasGetter)
        {
            len += " get {".Length;
            foreach (var t in GetBody)
                len += 1 + t.Length;
            len += " }".Length;
        }

        if (HasSetter)
        {
            len += " set {".Length;
            foreach (var t in SetBody)
                len += 1 + t.Length;
            len += " }".Length;
        }

        len += " }".Length;

        return len;
    }

    public override void Render(GeneratedTextTransformation tt, bool isCompact)
    {
        if (!IsAuto && HasGetter && GetBody.Count == 1)
        {
            var t = GetBody[GetBody.Count - 1];

            if (!t.StartsWith("return"))
            {
                t = "return " + t;

                if (!t.EndsWith(";"))
                    t += ";";

                GetBody[GetBody.Count - 1] = t;
            }
        }

        WriteProperty(tt, this, isCompact);
    }

    public Property InitBody(IEnumerable<string> getBody = null, IEnumerable<string> setBody = null)
    {
        IsAuto = getBody == null && setBody == null;

        if (getBody != null) GetBody.AddRange(getBody);
        if (setBody != null) SetBody.AddRange(setBody);

        if (!IsAuto)
        {
            HasGetter = getBody != null;
            HasSetter = setBody != null;
        }

        return this;
    }

    public Property InitGetter(params string[] getBody)
    {
        return InitBody(getBody, null);
    }
}

static Action<GeneratedTextTransformation,Method,bool> WriteMethod = (tt,m,compact) =>
{
    var am1  = m.AccessModifier.ToString().ToLower();
    var len1 = m.AccessModifierLen;
    var am2  = "";
    var len2 = 0;
    var mdf  = m.IsAbstract ? " abstract" : m.IsVirtual ? " virtual" : m.IsOverride ? " override" : m.IsStatic ? " static" : "";
    var mlen = m.ModifierLen;

    if (am1 == "partial" && mdf.Length > 0)
    {
        am2 = " " + am1; len2 = len1 + 1;
        am1 = "";        len1 = 0;
        mdf = mdf.Trim();
        mlen--;
    }

    tt.Write("{0}{1}{2}{3}{4}{5}{6}{7}{8} {9}",
        am1,    LenDiff(len1, am1),
        mdf,    LenDiff(mlen, mdf),
        am2,    LenDiff(len2, am2),
        m.Type == null ? "" : " ",
        m.Type, LenDiff(m.TypeLen,     m.Type ?? ""),
        m.Name);

    Action writeComment = () =>
    {
        if (!string.IsNullOrEmpty(m.EndLineComment))
        {
            tt.Write(" ");
            WriteComment(tt, " " + m.EndLineComment);
        }
        else
            tt.WriteLine("");
    };

    Action writeParams = () =>
    {
        tt.Write("(");

        for (int i = 0; i < m.Parameters.Count; i++)
        {
            if (i > 0)
                tt.Write(", ");
            tt.Write(m.Parameters[i]);
        }

        tt.Write(")");
    };

    if (compact)
    {
        tt.Write(LenDiff(m.NameLen, m.Name));

        var len = tt.GenerationEnvironment.Length;

        writeParams();

        foreach (var s in m.AfterSignature)
        {
            tt.Write(" ");
            tt.Write(s);
        }

        len = tt.GenerationEnvironment.Length - len;

        if (m.IsAbstract || m.AccessModifier == AccessModifier.Partial)
        {
            tt.Write(";");
            len = 0;
        }
        else
        {
            tt.WriteSpaces(m.ParamLen - len);

            len = tt.GenerationEnvironment.Length;

            tt.Write(" {");

            foreach (var t in m.Body)
                tt.Write(" {0}", t);

            tt.Write(" }");
        }

        if (!string.IsNullOrEmpty(m.EndLineComment))
            tt.WriteSpaces(m.BodyLen - (tt.GenerationEnvironment.Length - len));
        writeComment();
    }
    else
    {
        writeParams ();
        writeComment();

        tt.PushIndent("\t");
        foreach (var s in m.AfterSignature)
            tt.WriteLine(s);
        tt.PopIndent();

        tt.WriteLine("{");
        tt.PushIndent("\t");

        foreach (var t in m.Body)
        {
            if (t.Length > 1 && t[0] == '#')
            {
                tt.RemoveSpace();
            }

            tt.WriteLine(t);
        }

        tt.PopIndent();
        tt.WriteLine("}");
    }
};

public partial class Method : MemberBase
{
    public bool         IsAbstract;
    public bool         IsVirtual;
    public bool         IsOverride;
    public bool         IsStatic;
    public List<string> AfterSignature = new List<string>();
    public List<string> Parameters     = new List<string>();
    public List<string> Body           = new List<string>();

    public Method()
    {
    }

    public Method(string type, string name, IEnumerable<string> parameters = null, IEnumerable<string> body = null)
    {
        Type = type;
        Name = name;

        if (parameters != null) Parameters.AddRange(parameters);
        if (body       != null) Body.      AddRange(body);
    }

    public override int CalcModifierLen()
    {
        return
            IsAbstract ? " abstract".Length :
            IsVirtual  ? " virtual".Length  :
            IsStatic   ? " static".Length   : 0;
    }

    public override int CalcBodyLen()
    {
        if (IsAbstract || AccessModifier == AccessModifier.Partial)
            return 1;

        var len = " {".Length;

        foreach (var t in Body)
            len += 1 + t.Length;

        len += " }".Length;

        return len;
    }

    public override int CalcParamLen()
    {
        return Parameters.Sum(p => p.Length + 2);
    }

    public override void Render(GeneratedTextTransformation tt, bool isCompact)
    {
        WriteMethod(tt, this, isCompact);
    }
}

static Action<GeneratedTextTransformation,Attribute> WriteAttribute = (tt,a) =>
{
    tt.Write(a.Name);

    if (a.Parameters.Count > 0)
    {
        tt.Write("(");

        for (var i = 0; i < a.Parameters.Count; i++)
        {
            if (i > 0)
                if (a.Parameters[i - 1].All(c => c == ' '))
                    tt.Write("  ");
                else
                    SkipSpacesAndInsert(tt, ", ");
            tt.Write(a.Parameters[i]);
        }

        SkipSpacesAndInsert(tt, ")");
    }
};

public partial class Attribute
{
    public string       Name;
    public List<string> Parameters = new List<string>();
    public string       Conditional;
    public bool         IsSeparated;

    public Attribute()
    {
    }

    public Attribute(string name, params string[] ps)
    {
        Name = name;
        Parameters.AddRange(ps);
    }

    public virtual void Render(GeneratedTextTransformation tt)
    {
        WriteAttribute(tt, this);
    }
}

// Helpers.
//

Func<string,string> ToPlural   = s => s + "s";
Func<string,string> ToSingular = s => s;

static string LenDiff(int max, string str)
{
    var s = "";

    while (max-- > str.Length)
        s += " ";

    return s;
}

public void WriteSpaces(int len)
{
    while (len-- > 0)
        Write(" ");
}

void RemoveSpace()
{
    Write(" ");

    while (GenerationEnvironment.Length > 0 &&
        (GenerationEnvironment[GenerationEnvironment.Length - 1] == ' ' ||
         GenerationEnvironment[GenerationEnvironment.Length - 1] == '\t'))
        GenerationEnvironment.Length--;
}

public static IEnumerable<ITree> GetTreeNodes(ITree parent)
{
    foreach (var node in parent.GetNodes())
    {
        yield return node;

        foreach (var grandNode in GetTreeNodes(node))
            yield return grandNode;
    }
}

public static ITree FindNode(ITree parent, Func<ITree,bool> func)
{
    foreach (var node in parent.GetNodes())
    {
        if (func(node))
            return node;

        var n = FindNode(node, func);

        if (n != null)
            return n;
    }

    return null;
}

static void SkipSpacesAndInsert(GeneratedTextTransformation tt, string value)
{
    var l = tt.GenerationEnvironment.Length;

    for (; l > 0 && tt.GenerationEnvironment[l - 1] == ' '; l--)
    {
    }

    tt.GenerationEnvironment.Insert(l, value);
}

string ToCamelCase(string name)
{
    var n = 0;

    foreach (var c in name)
    {
        if (char.IsUpper(c))
            n++;
        else
            break;
    }

    if (n == 0)
        return name;

    if (n == name.Length)
        return name.ToLower();

    n = Math.Max(1, n - 1);

    return name.Substring(0, n).ToLower() + name.Substring(n);
}

event Action<Property,string,object> SetPropertyValueAction;

void SetPropertyValue(Property propertyObject, string propertyName, object value)
{
    if (SetPropertyValueAction != null)
        SetPropertyValueAction(propertyObject, propertyName, value);
}

        #line default
        #line hidden

        #line 16 "/Users/nikola/projects/blog/LinqToDB.Templates/DataModel.ttinclude"

string   NamespaceName
{
    get { return Model.Namespace.Name;  }
    set { Model.Namespace.Name = value; }
}

string   DatabaseName                  = null;
string   DataContextName               = null;
string   BaseDataContextClass          = null;
string   BaseEntityClass               = null;
string   OneToManyAssociationType      = "IEnumerable<{0}>";

bool     GenerateDatabaseName          = false;
bool     GenerateConstructors          = true;
string   DefaultConfiguration          = null;
bool     GenerateAssociations          = true;
bool     GenerateBackReferences        = true;
bool     GenerateAssociationExtensions = false;
bool     ReplaceSimilarTables          = true;
bool     IncludeDefaultSchema          = true;

Class    DataContextObject;

bool PluralizeClassNames                 = false;
bool SingularizeClassNames               = true;
bool PluralizeDataContextPropertyNames   = true;
bool SingularizeDataContextPropertyNames = false;

bool NormalizeNames                      = true;

private Func<string, bool, string> _toValidName;
Func<string, bool, string> ToValidName
{
    get { return _toValidName ?? ToValidNameDefault; }
    set { _toValidName = value; }
}

private Func<string, bool, string> _convertToCompilabl;
Func<string, bool, string> ConvertToCompilable 
{
    get { return _convertToCompilabl ?? ConvertToCompilableDefault; }
    set { _convertToCompilabl = value; }
}

private Func<ForeignKey, string> _getAssociationExtensionPluralName;
Func<ForeignKey, string> GetAssociationExtensionPluralName
{
    get { return _getAssociationExtensionPluralName ?? GetAssociationExtensionPluralNameDefault; }
    set { _getAssociationExtensionPluralName = value; }
}

private Func<ForeignKey, string> _getAssociationExtensionSingularName;
Func<ForeignKey, string> GetAssociationExtensionSinglularName
{
    get { return _getAssociationExtensionSingularName ?? GetAssociationExtensionSinglularNameDefault; }
    set { _getAssociationExtensionSingularName = value; }
}

LinqToDB.SchemaProvider.GetSchemaOptions GetSchemaOptions =
    new LinqToDB.SchemaProvider.GetSchemaOptions();

LinqToDB.SqlProvider.ISqlBuilder SqlBuilder;

Func<TableSchema,Table> LoadProviderSpecificTable = tableSchema => null;

static Func<ColumnSchema,string>                 ConvertColumnMemberType          = (c) => c.MemberType;
static Func<TableSchema,ColumnSchema,string>     ConvertTableColumnMemberType     = (t,c) => ConvertColumnMemberType(c);
static Func<ProcedureSchema,ColumnSchema,string> ConvertProcedureColumnMemberType = (t,c) => ConvertColumnMemberType(c);

HashSet<string> KeyWords = new HashSet<string>
{
    "abstract", "as",       "base",     "bool",    "break",     "byte",     "case",       "catch",     "char",    "checked",
    "class",    "const",    "continue", "decimal", "default",   "delegate", "do",         "double",    "else",    "enum",
    "event",    "explicit", "extern",   "false",   "finally",   "fixed",    "float",      "for",       "foreach", "goto",
    "if",       "implicit", "in",       "int",     "interface", "internal", "is",         "lock",      "long",    "new",
    "null",     "object",   "operator", "out",     "override",  "params",   "private",    "protected", "public",  "readonly",
    "ref",      "return",   "sbyte",    "sealed",  "short",     "sizeof",   "stackalloc", "static",    "struct",  "switch",
    "this",     "throw",    "true",     "try",     "typeof",    "uint",     "ulong",      "unchecked", "unsafe",  "ushort",
    "using",    "virtual",  "volatile", "void",    "while",     "namespace", "string"
};

void LoadServerMetadata(DataConnection dataConnection)
{
    SqlBuilder = dataConnection.DataProvider.CreateSqlBuilder();

    var sp = dataConnection.DataProvider.GetSchemaProvider();
    var db = sp.GetSchema(dataConnection, GetSchemaOptions);

    if (DatabaseName == null && GenerateDatabaseName)
        DatabaseName = db.Database;

    if (DataContextName == null)
        DataContextObject.Name = DataContextName = (ToValidName(db.Database, true) + "DB");

    DataContextObject.Comment.Add("/ <summary>");
    DataContextObject.Comment.Add("/ Database       : " + db.Database);
    DataContextObject.Comment.Add("/ Data Source    : " + db.DataSource);
    DataContextObject.Comment.Add("/ Server Version : " + db.ServerVersion);
    DataContextObject.Comment.Add("/ </summary>");

    var tables = db.Tables
        .Where(t => !t.IsProviderSpecific)
        .Select(t => new
        {
            t,
            key = t.IsDefaultSchema ? t.TableName : t.SchemaName + "." + t.TableName,
            table = new Table
            {
                TableSchema             = t,
                Schema                  = (t.IsDefaultSchema && !IncludeDefaultSchema) || string.IsNullOrEmpty(t.SchemaName)? null : t.SchemaName,
                BaseClass               = BaseEntityClass,
                TableName               = t.TableName,
                TypeName                = t.TypeName,
                DataContextPropertyName = t.TypeName,
                IsView                  = t.IsView,
                IsProviderSpecific      = false,
                Description             = t.Description,
                Columns                 = t.Columns.ToDictionary(
                    c => c.ColumnName,
                    c => new Column
                    {
                        ColumnName      = c.ColumnName,
                        ColumnType      = c.ColumnType,
                        DataType        = "DataType." + c.DataType,
                        Length          = c.Length,
                        Precision       = c.Precision,
                        Scale           = c.Scale,
                        IsNullable      = c.IsNullable,
                        IsIdentity      = c.IsIdentity,
                        IsPrimaryKey    = c.IsPrimaryKey,
                        PrimaryKeyOrder = c.PrimaryKeyOrder,
                        MemberName      = CheckType(c.SystemType, c.MemberName),
                        Type            = ConvertTableColumnMemberType(t, c),
                        SkipOnInsert    = c.SkipOnInsert,
                        SkipOnUpdate    = c.SkipOnUpdate,
                        Description     = c.Description,
                    })
            }
        })
        .ToList();

    if (PluralizeClassNames || SingularizeClassNames)
    {
        var foundNames = new HashSet<string>(tables.Select(t => t.table.TypeName));
        foreach (var t in tables)
        {
            var newName = t.table.TypeName;
                newName = 
                    PluralizeClassNames   ? ToPlural  (newName) :
                    SingularizeClassNames ? ToSingular(newName) : newName;

            if (newName != t.table.TypeName)
            {
                if (!foundNames.Contains(newName))
                {
                    t.table.TypeName = newName;
                    foundNames.Add(newName);
                }
            }
        }
    }

    if (PluralizeDataContextPropertyNames || SingularizeDataContextPropertyNames)
    {
        var foundNames = new HashSet<string>(tables.Select(t => t.table.DataContextPropertyName));
        foreach (var t in tables)
        {
            var newName = t.table.DataContextPropertyName;
                newName = 
                    PluralizeDataContextPropertyNames   ? ToPlural  (newName) :
                    SingularizeDataContextPropertyNames ? ToSingular(newName) : newName;

            if (newName != t.table.TypeName)
            {
                if (!foundNames.Contains(newName))
                {
                    t.table.DataContextPropertyName = newName;
                    foundNames.Add(newName);
                }
            }
        }
    }

    tables.AddRange(db.Tables
        .Where(t => t.IsProviderSpecific)
        .Select(t => new
        {
            t,
            key = t.IsDefaultSchema ? t.TableName : t.SchemaName + "." + t.TableName,
            table = LoadProviderSpecificTable(t)
        })
        .Where(t => t.table != null));

    foreach (var t in tables)
        Tables.Add(t.key, t.table);

    var keys =
    (
        from t in tables
        from k in t.t.ForeignKeys
        let otherTable = tables.Where(tbl => tbl.t == k.OtherTable).Select(tbl => tbl.table).Single()
        select new
        {
            k,
            k.KeyName,
            t,
            key = new ForeignKey
            {
                KeyName         = k.KeyName,
                OtherTable      = otherTable,
                OtherColumns    = k.OtherColumns.Select(c => otherTable.Columns[c.ColumnName]).ToList(),
                ThisColumns     = k.ThisColumns. Select(c => t.table.   Columns[c.ColumnName]).ToList(),
                CanBeNull       = k.CanBeNull,
                MemberName      = k.MemberName,
                AssociationType = (AssociationType)(int)k.AssociationType,
            }
        }
    ).ToList();

    foreach (var key in keys)
    {
        var keyName = key.k.OtherTable.IsDefaultSchema ? key.KeyName : key.k.OtherTable.SchemaName + "." + key.KeyName;
        ForeignKey foundKey;
        if (key.t.table.ForeignKeys.TryGetValue(keyName, out foundKey))
        {
            key.t.table.ForeignKeys.Remove(keyName);
            key.t.table.ForeignKeys.Add(foundKey.OtherTable.TableName + "." + keyName, foundKey);
            keyName = key.k.OtherTable.TableName + "." + keyName;
        }

        key.t.table.ForeignKeys.Add(
            key.k.OtherTable.IsDefaultSchema ? key.KeyName : key.k.OtherTable.SchemaName + "." + key.KeyName,
            key.key);

        if (key.k.BackReference != null)
            key.key.BackReference = keys.First(k => k.k == key.k.BackReference).key;
    }

    var procedures = db.Procedures
        .Select(p => new
        {
            p,
            key = p.IsDefaultSchema ? p.ProcedureName : p.SchemaName + "." + p.ProcedureName,
            proc = new Procedure
            {
                Schema          = (p.IsDefaultSchema && !IncludeDefaultSchema) || string.IsNullOrEmpty(p.SchemaName)? null : p.SchemaName,
                ProcedureName   = p.ProcedureName,
                Name            = ToValidName(p.MemberName, true),
                IsFunction      = p.IsFunction,
                IsTableFunction = p.IsTableFunction,
                IsDefaultSchema = p.IsDefaultSchema,
                IsLoaded        = p.IsLoaded,
                ResultTable     = p.ResultTable == null ? null :
                    new Table
                    {
                        TypeName = ToValidName(
                            PluralizeClassNames   ? ToPlural  (p.ResultTable.TypeName) :
                            SingularizeClassNames ? ToSingular(p.ResultTable.TypeName) : p.ResultTable.TypeName, true),
                        Columns  = ToDictionary(
                            p.ResultTable.Columns,
                            c => c.ColumnName,
                            c => new Column
                            {
                                ColumnName      = c.ColumnName,
                                ColumnType      = c.ColumnType,
                                IsNullable      = c.IsNullable,
                                IsIdentity      = c.IsIdentity,
                                IsPrimaryKey    = c.IsPrimaryKey,
                                PrimaryKeyOrder = c.PrimaryKeyOrder,
                                MemberName      = CheckColumnName(CheckType(c.SystemType, c.MemberName)),
                                Type            = ConvertProcedureColumnMemberType(p, c),
                                SkipOnInsert    = c.SkipOnInsert,
                                SkipOnUpdate    = c.SkipOnUpdate,
                                Description     = c.Description,
                            },
                            (c,n) =>
                            {
                                c.IsDuplicateOrEmpty = true;
                                return "$" + (c.MemberName = "Column" + n);
                            })
                    },
                ResultException = p.ResultException,
                SimilarTables   = p.SimilarTables == null ? new List<Table>() :
                    p.SimilarTables
                        .Select(t => tables.Single(tbl => tbl.t == t).table)
                        .ToList(),
                ProcParameters  = p.Parameters
                    .Select(pr => new Parameter
                    {
                        SchemaName    = pr.SchemaName,
                        SchemaType    = pr.SchemaType,
                        IsIn          = pr.IsIn,
                        IsOut         = pr.IsOut,
                        IsResult      = pr.IsResult,
                        Size          = pr.Size,
                        ParameterName = CheckParameterName(pr.ParameterName),
                        ParameterType = pr.ParameterType,
                        SystemType    = pr.SystemType,
                        DataType      = pr.DataType.ToString(),
                    })
                    .ToList(),
            }
        })
        .ToList();

    foreach (var p in procedures)
    {
        if (ReplaceSimilarTables)
            if (p.proc.SimilarTables.Count() == 1 || p.proc.SimilarTables.Count(t => !t.IsView) == 1)
                p.proc.ResultTable = p.proc.SimilarTables.Count() == 1 ?
                    p.proc.SimilarTables[0] :
                    p.proc.SimilarTables.First(t => !t.IsView);

        Procedures[p.key] = p.proc;
    }
}

Dictionary<string,TR> ToDictionary<T,TR>(IEnumerable<T> source, Func<T,string> keyGetter, Func<T,TR> objGetter, Func<TR,int,string> getKeyName)
{
    var dic     = new Dictionary<string,TR>();
    var current = 1;

    foreach (var item in source)
    {
        var key = keyGetter(item);
        var obj = objGetter(item);

        if (string.IsNullOrEmpty(key) || dic.ContainsKey(key))
            key = getKeyName(obj, current);

        dic.Add(key, obj);

        current++;
    }

    return dic;
}

string CheckType(Type type, string typeName)
{
    if (!Model.Usings.Contains(type.Namespace))
        Model.Usings.Add(type.Namespace);
    return typeName;
}

string CheckColumnName(string memberName)
{
    if (string.IsNullOrEmpty(memberName))
        memberName = "Empty";
    else
    {
        memberName = memberName
            .Replace("%", "Percent")
            .Replace(">", "Greater")
            .Replace("<", "Lower")
            .Replace("+", "Plus")
            .Replace('(', '_')
            .Replace(')', '_')
            .Replace('-', '_')
            .Replace('|', '_')
            .Replace(',', '_')
            .Replace('"', '_')
            .Replace("'", "_")
            .Replace(".", "_")
            .Replace("\u00A3", "Pound");

        if (KeyWords.Contains(memberName))
            memberName = "@" + memberName;
    }
    return memberName;
}

string CheckParameterName(string parameterName)
{
    var invalidParameterNames = new List<string>
    {
        "@DataType"
    };

    var result = parameterName;
    while (invalidParameterNames.Contains(result))
    {
        result = result + "_";
    }
    return result;
}

Action AfterLoadMetadata = () => {};

void LoadMetadata(DataConnection dataConnection)
{
    if (DataContextObject == null)
    {
        DataContextObject = new Class(DataContextName) { BaseClass = BaseDataContextClass, };

        Model.Types.Add(DataContextObject);
    }

    LoadServerMetadata(dataConnection);

    if (Tables.Values.SelectMany(_ => _.ForeignKeys.Values).Any(_ => _.AssociationType == AssociationType.OneToMany))
        Model.Usings.Add("System.Collections.Generic");

    foreach (var t in Tables.Values)
    {
        if (KeyWords.Contains(t.TypeName))
            t.TypeName = "@" + t.TypeName;

        if (KeyWords.Contains(t.DataContextPropertyName))
            t.DataContextPropertyName = "@" + t.DataContextPropertyName;

        t.TypeName                = ConvertToCompilable(t.TypeName,                true);
        t.DataContextPropertyName = ConvertToCompilable(t.DataContextPropertyName, true);

        foreach (var col in t.Columns.Values)
        {
            if (KeyWords.Contains(col.MemberName))
                col.MemberName = "@" + col.MemberName;

            col.MemberName = ConvertToCompilable(col.MemberName, true);

            if (col.MemberName == t.TypeName)
                col.MemberName += "_Column";
        }
    }

    foreach (var t in Tables.Values)
    {
        foreach (var fk in t.ForeignKeys.Values)
        {
            var memberName = fk.OtherTable.TypeName;

            memberName = fk.AssociationType == AssociationType.OneToMany ?
                ToPlural(memberName) : ToSingular(memberName);

            fk.MemberName = memberName;
        }
    }

    foreach (var t in Tables.Values)
    {
        var forbidden = KeyWords.Concat(new [] {t.TypeName});

        var hasDuplicates = t.Columns.Values
                .Select(c => c.MemberName)
                .Concat(t.ForeignKeys.Values.Select(f => f.MemberName))
                .Concat(forbidden)
                .ToLookup(n => n)
                .Any(g => g.Count() > 1);

        if (hasDuplicates)
        {
            foreach (var fk in t.ForeignKeys.Values)
            {
                var mayDuplicate = t.Columns.Values
                    .Select(c => c.MemberName)
                    .Concat(forbidden)
                    .Concat(t.ForeignKeys.Values.Where(f => f != fk).Select(f => f.MemberName));

                fk.MemberName = SuggestNoDuplicate(mayDuplicate, fk.MemberName, "FK");
            }

            foreach (var col in t.Columns.Values)
            {
                var mayDuplicate = t.Columns.Values
                    .Where(c => c != col)
                    .Select(c => c.MemberName)
                    .Concat(forbidden)
                    .Concat(t.ForeignKeys.Values.Select(fk => fk.MemberName));

                col.MemberName = SuggestNoDuplicate(mayDuplicate, col.MemberName, null);
            }
        }
    }

    AfterLoadMetadata();
}

string SuggestNoDuplicate(IEnumerable<string> currentNames, string newName, string prefix)
{
    var names = new HashSet<string>(currentNames);
    var result = newName;
    if (names.Contains(result))
    {
        if (!string.IsNullOrEmpty(prefix))
            result = prefix + result;
        if (names.Contains(result))
        {
            var counter = 0;
            var number = string.Concat(result.Reverse().Take(6).TakeWhile(c => Char.IsDigit(c)).Reverse());
            if (!string.IsNullOrEmpty(number))
            {
                if (int.TryParse(number, out counter))
                {
                    result = result.Remove(result.Length - number.Length);
                }
            }

            do
            {
                ++counter;
                if (!names.Contains(result + counter))
                {
                    result = result + counter;
                    break;
                }
            }
            while(true);
        }
    }

    return result;
}

string ConvertToCompilableDefault(string name, bool mayRemoveUnderscore)
{
    var query =
        from c in name
        select char.IsLetterOrDigit(c) || c == '@' ? c : '_';

    return ToValidName(new string(query.ToArray()), mayRemoveUnderscore);
}

Table GetTable(string name)
{
    Table tbl;

    if (Tables.TryGetValue(name, out tbl))
        return tbl;

    WriteLine("#error Table '" + name +  "' not found.");
    WriteLine("/*");
    WriteLine("\tExisting tables:");
    WriteLine("");

    foreach (var key in Tables.Keys)
        WriteLine("\t" + key);

    WriteLine(" */");

    throw new ArgumentException("Table '" + name +  "' not found.");
}

Procedure GetProcedure(string name)
{
    Procedure proc;

    if (Procedures.TryGetValue(name, out proc))
        return proc;

    WriteLine("#error Procedure '" + name +  "' not found.");
    WriteLine("");
    WriteLine("/*");
    WriteLine("\tExisting procedures:");
    WriteLine("");

    foreach (var key in Procedures.Keys)
        WriteLine("\t" + key);

    WriteLine(" */");

    throw new ArgumentException("Procedure '" + name +  "' not found.");
}

Column GetColumn(string tableName, string columnName)
{
    var tbl = GetTable(tableName);

    Column col;

    if (tbl.Columns.TryGetValue(columnName, out col))
        return col;

    WriteLine("#error Column '" + tableName + "'.'" + columnName +  "' not found.");
    WriteLine("");
    WriteLine("/*");
    WriteLine("\tExisting '" + tableName + "'columns:");
    WriteLine("");

    foreach (var key in tbl.Columns.Keys)
        WriteLine("\t" + key);

    WriteLine(" */");

    throw new ArgumentException("Column '" + tableName + "'.'" + columnName +  "' not found.");
}

ForeignKey GetFK(string tableName, string fkName)
{
    return GetForeignKey(tableName, fkName);
}

ForeignKey GetForeignKey(string tableName, string fkName)
{
    var tbl = GetTable(tableName);

    ForeignKey col;

    if (tbl.ForeignKeys.TryGetValue(fkName, out col))
        return col;

    WriteLine("#error FK '" + tableName + "'.'" + fkName +  "' not found.");
    WriteLine("");
    WriteLine("/*");
    WriteLine("\tExisting '" + tableName + "'FKs:");
    WriteLine("");

    foreach (var key in tbl.ForeignKeys.Keys)
        WriteLine("\t" + key);

    WriteLine(" */");

    throw new ArgumentException("FK '" + tableName + "'.'" + fkName +  "' not found.");
}

public TableContext SetTable(string tableName,
    string TypeName                = null,
    string DataContextPropertyName = null)
{
    var ctx = new TableContext { Transformation = this, TableName = tableName };

    if (TypeName != null || DataContextPropertyName != null)
    {
        var t = GetTable(tableName);

        if (TypeName                != null) t.TypeName                = TypeName;
        if (DataContextPropertyName != null) t.DataContextPropertyName = DataContextPropertyName;
    }

    return ctx;
}

public class TableContext
{
    public GeneratedTextTransformation Transformation;
    public string                      TableName;

    public TableContext Column(string columnName,
        string MemberName  = null,
        string Type        = null,
        bool?  IsNullable  = null,
        string Conditional = null)
    {
        var c = Transformation.GetColumn(TableName, columnName);

        if (MemberName  != null) c.MemberName  = MemberName;
        if (Type        != null) c.Type        = Type;
        if (IsNullable  != null) c.IsNullable  = IsNullable.Value;
        if (Conditional != null) c.Conditional = Conditional;

        return this;
    }

    public TableContext FK(string fkName,
        string           MemberName      = null,
        AssociationType? AssociationType = null,
        bool?            CanBeNull       = null)
    {
        var c = Transformation.GetFK(TableName, fkName);

        if (MemberName      != null) c.MemberName      = MemberName;
        if (AssociationType != null) c.AssociationType = AssociationType.Value;
        if (CanBeNull       != null) c.CanBeNull       = CanBeNull.Value;

        return this;
    }
}

Dictionary<string,Table>     Tables     = new Dictionary<string,Table>    ();
Dictionary<string,Procedure> Procedures = new Dictionary<string,Procedure>();

public partial class Table : Class
{
    public TableSchema TableSchema             { get; set; }
    public string      Schema                  { get; set; }
    public string      TableName               { get; set; }
    public string      DataContextPropertyName { get; set; }
    public MemberBase  DataContextProperty     { get; set; }
    public bool        IsView                  { get; set; }
    public bool        IsProviderSpecific      { get; set; }
    public string      Description             { get; set; }
    public string      AliasPropertyName       { get; set; }
    public string      AliasTypeName           { get; set; }
    public string      TypePrefix              { get; set; }

    public string TypeName
    {
        get { return Name;  }
        set { Name = value; }
    }

    public Dictionary<string,Column>     Columns;
    public Dictionary<string,ForeignKey> ForeignKeys = new Dictionary<string,ForeignKey>();
}

public partial class Column : Property
{
    public string    ColumnName; // Column name in database
    public bool      IsNullable;
    public bool      IsIdentity;
    public string    ColumnType; // Type of the column in database
    public string    DataType;
    public long?     Length;
    public int?      Precision;
    public int?      Scale;
    public DbType    DbType;
    public string    Description;
    public bool      IsPrimaryKey;
    public int       PrimaryKeyOrder;
    public bool      SkipOnUpdate;
    public bool      SkipOnInsert;
    public bool      IsDuplicateOrEmpty;
    public bool      IsDiscriminator;
    public string    AliasName;

    public string MemberName
    {
        get { return Name;  }
        set { Name = value; }
    }
}

public enum AssociationType
{
    Auto,
    OneToOne,
    OneToMany,
    ManyToOne,
}

public partial class ForeignKey : Property
{
    public string       KeyName;
    public Table        OtherTable;
    public List<Column> ThisColumns;
    public List<Column> OtherColumns;
    public bool         CanBeNull;
    public ForeignKey   BackReference;

    public string MemberName
    {
        get { return Name;  }
        set { Name = value; }
    }

    private AssociationType _associationType = AssociationType.Auto;
    public  AssociationType  AssociationType
    {
        get { return _associationType; }
        set
        {
            _associationType = value;

            if (BackReference != null)
            {
                switch (value)
                {
                    case AssociationType.Auto      : BackReference.AssociationType = AssociationType.Auto;      break;
                    case AssociationType.OneToOne  : BackReference.AssociationType = AssociationType.OneToOne;  break;
                    case AssociationType.OneToMany : BackReference.AssociationType = AssociationType.ManyToOne; break;
                    case AssociationType.ManyToOne : BackReference.AssociationType = AssociationType.OneToMany; break;
                }
            }
        }
    }
}

public partial class Procedure : Method
{
    public string Schema          { get; set; }
    public string ProcedureName   { get; set; }
    public bool   IsFunction      { get; set; }
    public bool   IsTableFunction { get; set; }
    public bool   IsDefaultSchema { get; set; }
    public bool   IsLoaded        { get; set; }

    public Table           ResultTable     { get; set; }
    public Exception       ResultException { get; set; }
    public List<Table>     SimilarTables   { get; set; }
    public List<Parameter> ProcParameters  { get; set; }
}

public class Parameter
{
    public string   SchemaName    { get; set; }
    public string   SchemaType    { get; set; }
    public bool     IsIn          { get; set; }
    public bool     IsOut         { get; set; }
    public bool     IsResult      { get; set; }
    public long?    Size          { get; set; }

    public string   ParameterName { get; set; }
    public string   ParameterType { get; set; }
    public Type     SystemType    { get; set; }
    public string   DataType      { get; set; }
}

private int _counter = 0;

string ToValidNameDefault(string name, bool mayRemoveUnderscore)
{
    if (NormalizeNames && mayRemoveUnderscore && name.Contains("_"))
    {
        name = SplitAndJoin(name, "", '_');
    }   

    if (name.Contains("."))
    {
        name = SplitAndJoin(name, "", '.');
    }

    if (name.Length > 0 && char.IsDigit(name[0]))
        name = "_" + name;

    if (string.IsNullOrEmpty(name))
        name = "_" + _counter++;

    if (NormalizeNames)
    {
        name = char.ToUpper(name[0]) + name.Substring(1);
    }

    return name;
}

static string SplitAndJoin(string value, string join, params char[] split)
{
    var ss = value.Split(split, StringSplitOptions.RemoveEmptyEntries)
        .Select(s => char.ToUpper(s[0]) + s.Substring(1));

    return string.Join(join, ss.ToArray());
}

private string GetAssociationExtensionSinglularNameDefault(ForeignKey key)
{
    return ToSingular(key.Name);
}

private string GetAssociationExtensionPluralNameDefault(ForeignKey key)
{
    return ToPlural(ToSingular(key.Name));
}

        #line default
        #line hidden

        #line 10 "/Users/nikola/projects/blog/LinqToDB.Templates/LinqToDB.ttinclude"

Action BeforeGenerateLinqToDBModel = () => {};
Action AfterGenerateLinqToDBModel  = () => {};

Func<Table,MemberBase> GenerateProviderSpecificTable = t => null;

bool   GenerateObsoleteAttributeForAliases = false;
bool   GenerateFindExtensions    = true;
bool   IsCompactColumns          = true;
bool   IsCompactColumnAliases    = true;
bool   GenerateDataTypes         = false;
bool?  GenerateLengthProperty    = null;
bool?  GeneratePrecisionProperty = null;
bool?  GenerateScaleProperty     = null;
bool   GenerateDbTypes           = false;
bool   GenerateSchemaAsType      = false;
bool   GenerateViews             = true;
string SchemaNameSuffix          = "Schema";
string SchemaDataContextTypeName = "DataContext";

Dictionary<string,string> SchemaNameMapping = new Dictionary<string,string>();

Func<string,string,IEnumerable<Method>> GetConstructors =  (conf, name) => GetConstructorsImpl(conf, name);

static IEnumerable<Method> GetConstructorsImpl(string defaultConfiguration, string name)
{
    if (defaultConfiguration == null)
        yield return new Method(null, name);
    else
        yield return new Method(null, name) { AfterSignature = { ": base(\"" + defaultConfiguration + "\")" } };
    yield return new Method(null, name, new[] { "string configuration" }) { AfterSignature = { ": base(configuration)" } };
}

void GenerateTypesFromMetadata()
{
    BeforeGenerateLinqToDBModel();

    Model.Usings.Add("LinqToDB");
    Model.Usings.Add("LinqToDB.Mapping");

    if (NamespaceName == null)
        NamespaceName = "DataModel";

    string schemaName;

    var schemas =
    (
        from t in Tables.Values
        where GenerateSchemaAsType && t.Schema != null && !t.TableSchema.IsDefaultSchema
        group t by t.Schema into gr
        orderby gr.Key
        let typeName = SchemaNameMapping.TryGetValue(gr.Key, out schemaName) ? schemaName : gr.Key
        select new
        {
            Name            = gr.Key,
            TypeName        = typeName + SchemaNameSuffix,
            PropertyName    = typeName,
            Props           = new MemberGroup { IsCompact = true },
            Aliases         = new MemberGroup { IsCompact = true, Region = "Alias members" },
            TableExtensions = new MemberGroup { Region = "Table Extensions" },
            Type            = new Class(typeName + SchemaNameSuffix) { IsStatic = true },
            Tables          = gr.ToList(),
            DataContext     = new Class(SchemaDataContextTypeName),
            Procedures      = new MemberGroup(),
            Functions       = new MemberGroup(),
            TableFunctions  = new MemberGroup { Region = "Table Functions" },
        }
    ).ToDictionary(t => t.Name);

    var defProps           = new MemberGroup { IsCompact = true };
    var defAliases         = new MemberGroup { IsCompact = true, Region = "Alias members" };
    var defTableExtensions = new MemberGroup { };

    if (schemas.Count > 0)
    {
        var body = new List<string>();

        var schemaGroup   = new MemberGroup { Region = "Schemas" };
        var schemaMembers = new MemberGroup { IsCompact = true   };

        var maxLen1 = schemas.Values.Max(schema => schema.PropertyName.Trim().Length);
        var maxLen2 = schemas.Values.Max(schema => schema.TypeName.    Trim().Length);

        foreach (var schema in schemas.Values)
        {
            schemaMembers.Members.Add(new Property(schema.TypeName + "." + SchemaDataContextTypeName, schema.PropertyName));
            body.Add(
                schema.PropertyName + LenDiff(maxLen1, schema.PropertyName) +
                " = new " + schema.TypeName + "." + LenDiff(maxLen2, schema.TypeName) +
                SchemaDataContextTypeName + "(this);");
        }

        schemaGroup.Members.Add(schemaMembers);
        schemaGroup.Members.Add(new Method("void", "InitSchemas", new string[0], body));

        DataContextObject.Members.Add(schemaGroup);
    }

    if (GenerateConstructors)
    {
        foreach (var c in GetConstructors(DefaultConfiguration, DataContextObject.Name))
        {
            if (c.Body.Count > 0)
                c.Body.Add("");

            if (schemas.Count > 0)
                c.Body.Add("InitSchemas();");

            c.Body.Add("InitDataContext();");

            DataContextObject.Members.Add(c);
        }
    }

    DataContextObject.Members.Add(new MemberGroup
    {
        IsCompact = true,
        Members   = { new Method("void", "InitDataContext") { AccessModifier = AccessModifier.Partial } }
    });

    if (Tables.Count > 0)
        DataContextObject.Members.Insert(0, defProps);

    foreach (var schema in schemas.Values)
    {
        schema.Type.Members.Add(schema.DataContext);
        schema.DataContext.Members.Insert(0, schema.Props);

        schema.DataContext.Members.Add(new Field ("IDataContext", "_dataContext") { AccessModifier = AccessModifier.Private, IsReadonly = true });
        schema.DataContext.Members.Add(new Method(null, schema.DataContext.Name, new[] { "IDataContext dataContext" }, new[] { "_dataContext = dataContext;" }));

        foreach (var t in schema.Tables)
        {
            t.TypePrefix = schema.TypeName + ".";
        }
    }

    var associationExtensions = new MemberGroup() {Region = "Associations"};

    foreach (var t in Tables.Values.OrderBy(tbl => tbl.IsProviderSpecific).ThenBy(tbl => tbl.TypeName))
    {
        Action<Class> addType     = tp => Model.Types.Add(tp);
        var props                 = defProps;
        var aliases               = defAliases;
        var tableExtensions       = defTableExtensions;

        if (t.IsView && !GenerateViews){
            continue;
        }

        var schema = t.Schema != null && schemas.ContainsKey(t.Schema) ? schemas[t.Schema] : null;

        if (schema != null)
        {
            var si = schemas[t.Schema];

            addType         = tp => si.Type.Members.Add(tp);
            props           = si.Props;
            aliases         = si.Aliases;
            tableExtensions = si.TableExtensions;
        }

        MemberBase dcProp = t.IsProviderSpecific ?
            GenerateProviderSpecificTable(t) :
            new Property(
                string.Format("ITable<{0}>", t.TypeName),
                t.DataContextPropertyName,
                new[] { string.Format((schema == null ? "this" : "_dataContext") + ".GetTable<{0}>()", t.TypeName) },
                null);

        if (dcProp == null) continue;

        t.DataContextProperty = dcProp;

        props.Members.Add(dcProp);

        Property aProp = null;

        if (t.AliasPropertyName != null && t.AliasPropertyName != t.DataContextPropertyName)
        {
            aProp = new Property(
                string.Format("ITable<{0}>", t.TypeName),
                t.AliasPropertyName,
                new[] { t.DataContextPropertyName },
                null);

            if (GenerateObsoleteAttributeForAliases)
                aProp.Attributes.Add(new Attribute("Obsolete", "\"Use " + t.DataContextPropertyName + " instead.\""));

            aliases.Members.Add(aProp);
        }

        var tableAttrs = new List<string>();

        if (DatabaseName != null) tableAttrs.Add("Database=" + '"' + DatabaseName + '"');
        if (t.Schema     != null) tableAttrs.Add("Schema="   + '"' + t.Schema     + '"');

        tableAttrs.Add((tableAttrs.Count == 0 ? "" : "Name=") + '"' + t.TableName + '"');

        if (t.IsView)
            tableAttrs.Add("IsView=true");

        t.Attributes.Add(new Attribute("Table", tableAttrs.ToArray()) { IsSeparated = true } );

        var comments = new List<string>();

        if (!string.IsNullOrWhiteSpace(t.Description))
        {
            comments.Add("/ <summary>");
            foreach (var line in t.Description.Split('\n'))
                comments.Add("/ " + line.TrimEnd());
            comments.Add("/ </summary>");
        }

        if (comments.Count > 0)
        {
            t.     Comment.AddRange(comments);
            dcProp.Comment.AddRange(comments);

            if (aProp != null)
                aProp.Comment.AddRange(comments);
        }

        var columns        = new MemberGroup { IsCompact = IsCompactColumns };
        var columnAliases  = new MemberGroup { IsCompact = IsCompactColumnAliases, Region = "Alias members" };
        var nPKs           = t.Columns.Values.Count(c => c.IsPrimaryKey);
        var allNullable    = t.Columns.Values.All  (c => c.IsNullable || c.IsIdentity);
        var nameMaxLen     = t.Columns.Values.Max  (c => (int?)(c.MemberName == c.ColumnName 
            ? 0 
            : NormalizeStringName(c.ColumnName).Length)) ?? 0;
        var dbTypeMaxLen   = t.Columns.Values.Max  (c => (int?)(c.ColumnType.Length)) ?? 0;
        var dataTypeMaxLen = t.Columns.Values.Where(c => c.DataType != null).Max  (c => (int?)(c.DataType.Length)) ?? 0;
        var dataTypePrefix = t.Columns.Values.Any  (c => c.MemberName == "DataType") ? "LinqToDB." : "";

        foreach (var c in t.Columns.Values)
        {
            // Column.
            //
            var ca = new Attribute("Column");
            var canBeReplaced = true;

            if (c.MemberName != c.ColumnName)
            {
                var columnNameInAttr = NormalizeStringName(c.ColumnName);

                var space = new string(' ', nameMaxLen - columnNameInAttr.Length);

                ca.Parameters.Add(columnNameInAttr + space);
                canBeReplaced = false;
            }
            else if (nameMaxLen > 0)
            {
                ca.Parameters.Add(new string(' ', nameMaxLen));
                canBeReplaced = false;
            }

            if (GenerateDbTypes)
            {
                var space = new string(' ', dbTypeMaxLen - c.ColumnType.Length);

                ca.Parameters.Add("DbType=\"" + c.ColumnType + '"' + space);
                canBeReplaced = false;
            }

            if (GenerateDataTypes)
            {
                var space = new string(' ', dataTypeMaxLen - c.DataType.Length);
                ca.Parameters.Add("DataType=" + dataTypePrefix + c.DataType + space);
                canBeReplaced = false;
            }

            if (GenerateDataTypes && !GenerateLengthProperty.HasValue || GenerateLengthProperty == true)
            {
                if (c.Length    != null) ca.Parameters.Add("Length="    + (c.Length == int.MaxValue ? "int.MaxValue" : c.Length.ToString()));
                canBeReplaced = false;
            }

            if (GenerateDataTypes && !GeneratePrecisionProperty.HasValue || GeneratePrecisionProperty == true)
            {
                if (c.Precision != null) ca.Parameters.Add("Precision=" + c.Precision);
                canBeReplaced = false;
            }

            if (GenerateDataTypes && !GenerateScaleProperty.HasValue || GenerateScaleProperty == true)
            {
                if (c.Scale     != null) ca.Parameters.Add("Scale=" + c.Scale);
                canBeReplaced = false;
            }

            if (c.SkipOnInsert && !c.IsIdentity)
            {
                ca.Parameters.Add("SkipOnInsert=true");
                canBeReplaced = false;
            }

            if (c.SkipOnUpdate && !c.IsIdentity)
            {
                ca.Parameters.Add("SkipOnUpdate=true");
                canBeReplaced = false;
            }

            if (c.IsDiscriminator)
            {
                ca.Parameters.Add("IsDiscriminator=true");
                canBeReplaced = false;
            }

            c.Attributes.Add(ca);

            // PK.
            //
            if (c.IsPrimaryKey)
            {
                var pka = new Attribute("PrimaryKey");

                if (nPKs > 1)
                    pka.Parameters.Add(c.PrimaryKeyOrder.ToString());

                if (canBeReplaced)
                    c.Attributes[0] = pka;
                else
                    c.Attributes.Add(pka);

                canBeReplaced = false;
            }

            // Identity.
            //
            if (c.IsIdentity)
            {
                var ida = new Attribute("Identity");

                if (canBeReplaced)
                    c.Attributes[0] = ida;
                else
                    c.Attributes.Add(ida);

                canBeReplaced = false;
            }

            // Nullable.
            //
            if (c.IsNullable)
                c.Attributes.Add(new Attribute((allNullable ? "" : "   ") + "Nullable"));
            else if (!c.IsIdentity)
                c.Attributes.Add(new Attribute("NotNull"));

            var columnComments = new List<string>();

            if (!string.IsNullOrWhiteSpace(c.Description))
            {
                columnComments.Add("/ <summary>");
                foreach (var line in c.Description.Split('\n'))
                    columnComments.Add("/ " + line.TrimEnd());
                columnComments.Add("/ </summary>");
            }

            if (columnComments.Count > 0)
                c.Comment.AddRange(columnComments);

            // End line comment.
            //
            c.EndLineComment = c.ColumnType;

            SetPropertyValue(c, "IsNotifying", true);
            SetPropertyValue(c, "IsEditable",  true);

            columns.Members.Add(c);

            // Alias.
            //
            if (c.AliasName != null && c.AliasName != c.MemberName)
            {
                var caProp = new Property(
                    c.Type,
                    c.AliasName,
                    new[] { c.MemberName },
                    new[] { c.MemberName + " = value;"});

                caProp.Comment.AddRange(columnComments);

                if (GenerateObsoleteAttributeForAliases)
                    caProp.Attributes.Add(new Attribute("Obsolete", "\"Use " + c.MemberName + " instead.\""));

                caProp.Attributes.Add(new Attribute("ColumnAlias", "\"" + c.MemberName + "\""));

                columnAliases.Members.Add(caProp);
            }
        }

        t.Members.Add(columns);

        if (columnAliases.Members.Count > 0)
            t.Members.Add(columnAliases);

        if (GenerateAssociations || GenerateAssociationExtensions)
        {
            var keys = t.ForeignKeys.Values.ToList();

            if (!GenerateBackReferences)
                keys = keys.Where(k => k.BackReference != null).ToList();

            if (keys.Count > 0)
            {
                var associations          = new MemberGroup { Region = "Associations" };
                var extensionAssociations = new MemberGroup { Region = t.Name + " Associations"};

                foreach (var key in keys.OrderBy(k => k.MemberName))
                {
                    key.Comment.Add("/ <summary>");
                    key.Comment.Add("/ " + key.KeyName);
                    key.Comment.Add("/ </summary>");

                    if (key.AssociationType == AssociationType.OneToMany)
                        key.Type = string.Format(OneToManyAssociationType, key.OtherTable.TypePrefix + key.OtherTable.TypeName);
                    else
                        key.Type = key.OtherTable.TypePrefix + key.OtherTable.TypeName;

                    var aa = new Attribute("Association");

                    aa.Parameters.Add("ThisKey=\""   + string.Join(", ", (from c in key.ThisColumns  select c.MemberName).ToArray()) + "\"");
                    aa.Parameters.Add("OtherKey=\""  + string.Join(", ", (from c in key.OtherColumns select c.MemberName).ToArray()) + "\"");
                    aa.Parameters.Add("CanBeNull=" + (key.CanBeNull ? "true" : "false"));

                    switch (key.AssociationType)
                    {
                        case AssociationType.OneToOne  : aa.Parameters.Add("Relationship=Relationship.OneToOne");  break;
                        case AssociationType.OneToMany : aa.Parameters.Add("Relationship=Relationship.OneToMany"); break;
                        case AssociationType.ManyToOne : aa.Parameters.Add("Relationship=Relationship.ManyToOne"); break;
                    }

                    if (key.BackReference != null)
                    {
                        if (!string.IsNullOrEmpty(key.KeyName))
                            aa.Parameters.Add("KeyName=\"" + key.KeyName + "\"");
                        if (GenerateBackReferences && !string.IsNullOrEmpty(key.BackReference.MemberName))
                            aa.Parameters.Add("BackReferenceName=\"" + key.BackReference.MemberName + "\"");
                    }
                    else
                    {
                        aa.Parameters.Add("IsBackReference=true");
                    }

                    key.Attributes.Add(aa);

                    SetPropertyValue(key, "IsNotifying", true);
                    SetPropertyValue(key, "IsEditable",  true);

                    associations.Members.Add(key);

                    var extension = new Method(string.Format("IQueryable<{0}>", key.OtherTable.TypePrefix + key.OtherTable.TypeName), GetAssociationExtensionPluralName(key));
                    extension.Parameters.Add(string.Format("this {0} obj", t.TypeName));
                    extension.Parameters.Add("IDataContext db");
                    extension.Attributes.Add(aa);
                    extension.IsStatic = true;

                    extension.Comment.Add("/ <summary>");
                    extension.Comment.Add("/ " + key.KeyName);
                    extension.Comment.Add("/ </summary>");

                    var sb = new StringBuilder();
                    sb
                        .Append("return db.GetTable<")
                        .Append(key.OtherTable.TypePrefix + key.OtherTable.TypeName)
                        .Append(">().Where(c => ");
                    for (var i = 0; i < key.OtherColumns.Count; i++)
                    {
                        sb.Append("c.")
                            .Append(key.OtherColumns[i].MemberName)
                            .Append(" == obj.")
                            .Append(key.ThisColumns[i].MemberName)
                            .Append(" && ");
                    }
                    sb.Length -= 4;
                    sb.Append(");");

                    extension.Body.Add(sb.ToString());

                    extensionAssociations.Members.Add(extension);

                    if (key.AssociationType != AssociationType.OneToMany)
                    {
                        extension.Name = GetAssociationExtensionPluralName(key);

                        var single = new Method(key.OtherTable.TypePrefix + key.OtherTable.TypeName, GetAssociationExtensionSinglularName(key));
                        single.Parameters.Add(string.Format("this {0} obj", t.TypeName));
                        single.Parameters.Add("IDataContext db");
                        single.Attributes.Add(aa);
                        single.IsStatic = true;

                        single.Comment.Add("/ <summary>");
                        single.Comment.Add("/ " + key.KeyName);
                        single.Comment.Add("/ </summary>");

                        sb.Length -= 1;

                        if (key.CanBeNull)
                            sb.Append(".FirstOrDefault();");
                        else 
                            sb.Append(".First();");

                        single.Body.Add(sb.ToString());

                        extensionAssociations.Members.Add(single);
                    }

                }

                if (GenerateAssociations)
                    t.Members.Add(associations);
                if (GenerateAssociationExtensions)
                    associationExtensions.Members.Add(extensionAssociations);
            }
        }

        if (GenerateFindExtensions && nPKs > 0)
        {
            var PKs         = t.Columns.Values.Where(c => c.IsPrimaryKey).ToList();
            var maxNameLen1 = PKs.Max(c => (int?)c.MemberName.Length) ?? 0;
            var maxNameLen2 = PKs.Take(nPKs - 1).Max(c => (int?)c.MemberName.Length) ?? 0;

            tableExtensions.Members.Add(
                new Method(
                    t.TypeName,
                    "Find",
                    new[] { (string.Format("this ITable<{0}> table", t.TypeName)) }
                        .Union(PKs.Select(c => c.Type + " " + c.MemberName)),
                    new[] { "return table.FirstOrDefault(t =>" }
                        .Union(PKs.SelectMany((c,i) =>
                        {
                            var ss = new List<string>();

                            if (c.Conditional != null)
                                ss.Add("#if " + c.Conditional);

                            ss.Add(string.Format("\tt.{0}{1} == {0}{3}{2}",
                                c.MemberName, LenDiff(maxNameLen1, c.MemberName), i == nPKs - 1 ? ");" : " &&", i == nPKs - 1 ? "" : LenDiff(maxNameLen2, c.MemberName)));

                            if (c.Conditional != null)
                            {
                                if (ss[1].EndsWith(");"))
                                {
                                    ss[1] = ss[1].Substring(0, ss[1].Length - 2);
                                    ss.Add("#endif");
                                    ss.Add("\t\t);");
                                }
                                else
                                {
                                    ss.Add("#endif");
                                }
                            }

                            return ss;
                        })))
                {
                    IsStatic = true
                });
        }

        addType(t);

        if (!string.IsNullOrWhiteSpace(t.AliasTypeName))
        {
            var aClass = new Class(t.AliasTypeName)
            {
                BaseClass = t.TypeName
            };

            if (comments.Count > 0)
                aClass.Comment.AddRange(comments);

            if (GenerateObsoleteAttributeForAliases)
                aClass.Attributes.Add(new Attribute("Obsolete", "\"Use " + t.TypeName + " instead.\""));

            Model.Types.Add(aClass);
        }

    }

    if (associationExtensions.Members.Count > 0)
        defTableExtensions.Members.Add(associationExtensions);

    if (defAliases.Members.Count > 0)
        DataContextObject.Members.Add(defAliases);

    foreach (var schema in schemas.Values)
        if (schema.Aliases.Members.Count > 0)
            schema.Type.Members.Add(defAliases);

    if (Procedures.Count > 0)
    {
        Model.Usings.Add("System.Collections.Generic");
        Model.Usings.Add("System.Data");
        Model.Usings.Add("LinqToDB.Data");
        Model.Usings.Add("LinqToDB.Common");

        if (Procedures.Values.Any(p => p.IsTableFunction))
            Model.Usings.Add("System.Reflection");

        var procs = new MemberGroup();
        var funcs = new MemberGroup();
        var tabfs = new MemberGroup { Region = "Table Functions" };

        foreach (var p in Procedures.Values.Where(
            proc => proc.IsLoaded || proc.IsFunction && !proc.IsTableFunction ||
            proc.IsTableFunction && proc.ResultException != null
            ))
        {
            Action<MemberGroup> addProcs = tp => procs.Members.Add(tp);
            Action<MemberGroup> addFuncs = tp => funcs.Members.Add(tp);
            Action<MemberGroup> addTabfs = tp => tabfs.Members.Add(tp);

            var thisDataContext = "this";

            var schema = p.Schema != null && schemas.ContainsKey(p.Schema) ? schemas[p.Schema] : null;

            if (schema != null)
            {
                var si = schemas[p.Schema];

                addProcs        = tp => si.Procedures.    Members.Add(tp);
                addFuncs        = tp => si.Functions.     Members.Add(tp);
                addTabfs        = tp => si.TableFunctions.Members.Add(tp);
                thisDataContext = "_dataContext";
            }

            var proc = new MemberGroup { Region = p.Name };

                 if (!p.IsFunction)     addProcs(proc);
            else if (p.IsTableFunction) addTabfs(proc);
            else                        addFuncs(proc);

            if (p.ResultException != null)
            {
                proc.Errors.Add(p.ResultException.Message);
                continue;
            }

            proc.Members.Add(p);

            if (p.IsTableFunction)
            {
                var tableAttrs = new List<string>();

                if (DatabaseName != null) tableAttrs.Add("Database=" + '"' + DatabaseName + '"');
                if (p.Schema     != null) tableAttrs.Add("Schema="   + '"' + p.Schema     + '"');

                tableAttrs.Add("Name=" + '"' + p.ProcedureName + '"');

                p.Attributes.Add(new Attribute("Sql.TableFunction", tableAttrs.ToArray()));

                p.Type = "ITable<" + p.ResultTable.TypeName + ">";
            }
            else if (p.IsFunction)
            {
                p.IsStatic = true;
                p.Type = p.ProcParameters.Single(pr => pr.IsResult).ParameterType;
                p.Attributes.Add(new Attribute("Sql.Function", "Name=\"" + p.Schema + "."  + p.ProcedureName + "\"", "ServerSideOnly=true"));
            }
            else
            {
                p.IsStatic = true;
                p.Type     = p.ResultTable == null ? "int" : "IEnumerable<" + p.ResultTable.TypeName + ">";
                p.Parameters.Add("this DataConnection dataConnection");
            }

            foreach (var pr in p.ProcParameters.Where(par => !par.IsResult))
                p.Parameters.Add(string.Format("{0}{1} {2}",
                    pr.IsOut ? pr.IsIn ? "ref " : "out " : "", pr.ParameterType, pr.ParameterName));

            if (p.IsTableFunction)
            {
                var body = string.Format("return " + thisDataContext + ".GetTable<{0}>(this, (MethodInfo)MethodBase.GetCurrentMethod()", p.ResultTable.TypeName);

                body += p.ProcParameters.Count == 0 ? ");" : ",";

                p.Body.Add(body);

                for (var i = 0; i < p.ProcParameters.Count; i++)
                    p.Body.Add("\t" + p.ProcParameters[i].ParameterName + (i + 1 == p.ProcParameters.Count ? ");" : ","));
            }
            else if (p.IsFunction)
            {
                p.Body.Add("throw new InvalidOperationException();");
            }
            else
            {
                var spName = 
                    SqlBuilder.BuildTableName(
                        new System.Text.StringBuilder(),
                        (string)SqlBuilder.Convert(DatabaseName,    LinqToDB.SqlProvider.ConvertType.NameToDatabase),
                        (string)SqlBuilder.Convert(p.Schema,        LinqToDB.SqlProvider.ConvertType.NameToOwner),
                        (string)SqlBuilder.Convert(p.ProcedureName, LinqToDB.SqlProvider.ConvertType.NameToQueryTable)
                    ).ToString();

                spName = "\"" + spName.Replace("\"", "\\\"") + "\"";

                var inputParameters  = p.ProcParameters.Where(pp => pp.IsIn). ToList();
                var outputParameters = p.ProcParameters.Where(pp => pp.IsOut).ToList();

                spName += inputParameters.Count == 0 ? ");" : ",";

                var retName = "ret";
                var retNo   = 0;

                while (p.ProcParameters.Any(pp => pp.ParameterName == retName))
                    retName = "ret" + ++retNo;

                var hasOut = outputParameters.Any(pr => pr.IsOut);
                var prefix = hasOut ? "var " + retName + " = " : "return ";

                if (p.ResultTable == null)
                    p.Body.Add(prefix + "dataConnection.ExecuteProc(" + spName);
                else
                {
                    if (p.ResultTable.Columns.Values.Any(c => c.IsDuplicateOrEmpty))
                    {
                        p.Body.Add("var ms = dataConnection.MappingSchema;");
                        p.Body.Add("");
                        p.Body.Add(prefix + "dataConnection.QueryProc(dataReader =>");
                        p.Body.Add("\tnew " + p.ResultTable.TypeName);
                        p.Body.Add("\t{");

                        var n          = 0;
                        var maxNameLen = p.ResultTable.Columns.Values.Max(c => (int?)c.MemberName.Length) ?? 0;
                        var maxTypeLen = p.ResultTable.Columns.Values.Max(c => (int?)c.Type.      Length) ?? 0;

                        foreach (var c in p.ResultTable.Columns.Values)
                        {
                            p.Body.Add(string.Format("\t\t{0}{1} = Converter.ChangeTypeTo<{2}>{3}(dataReader.GetValue({4}), ms),",
                                c.MemberName, LenDiff(maxNameLen, c.MemberName), c.Type, LenDiff(maxTypeLen, c.Type), n++));
                        }

                        p.Body.Add("\t},");
                        p.Body.Add("\t" + spName);
                    }
                    else
                    {
                        p.Body.Add(prefix + "dataConnection.QueryProc<" + p.ResultTable.TypeName + ">(" + spName);
                    }
                }

                var maxLenSchema = inputParameters.Max(pr => (int?)pr.SchemaName.   Length) ?? 0;
                var maxLenParam  = inputParameters.Max(pr => (int?)pr.ParameterName.Length) ?? 0;
                var maxLenType   = inputParameters.Max(pr => (int?)("DataType." + pr.DataType).Length) ?? 0;

                for (var i = 0; i < inputParameters.Count; i++)
                {
                    var pr = inputParameters[i];

                    var str = string.Format("\tnew DataParameter(\"{0}\", {1}{2}, {3}{4})",
                        pr.SchemaName,
                        LenDiff(maxLenSchema, pr.SchemaName),
                        pr.ParameterName,
                        LenDiff(maxLenParam, pr.ParameterName),
                        "DataType." + pr.DataType);

                    if (pr.IsOut)
                    {
                        str += LenDiff(maxLenType, "DataType." + pr.DataType);
                        str += " { Direction = " + (pr.IsIn ? "ParameterDirection.InputOutput" : "ParameterDirection.Output");

                        if (pr.Size != null && pr.Size.Value != 0)
                            str += ", Size = " + pr.Size.Value;

                        str += " }";
                    }

                    str += i + 1 == inputParameters.Count ? ");" : ",";

                    p.Body.Add(str);
                }

                if (hasOut)
                {
                    maxLenSchema = outputParameters.Max(pr => (int?)pr.SchemaName.   Length) ?? 0;
                    maxLenParam  = outputParameters.Max(pr => (int?)pr.ParameterName.Length) ?? 0;
                    maxLenType   = outputParameters.Max(pr => (int?)pr.ParameterType.Length) ?? 0;

                    p.Body.Add("");

                    foreach (var pr in p.ProcParameters.Where(_ => _.IsOut))
                    {
                        var str = string.Format("{0} {1}= Converter.ChangeTypeTo<{2}>{3}(((IDbDataParameter)dataConnection.Command.Parameters[\"{4}\"]).{5}Value);",
                            pr.ParameterName,
                            LenDiff(maxLenParam,  pr.ParameterName),
                            pr.ParameterType,
                            LenDiff(maxLenType,   pr.ParameterType),
                            pr.SchemaName,
                            LenDiff(maxLenSchema, pr.SchemaName));

                        p.Body.Add(str);
                    }

                    p.Body.Add("");
                    p.Body.Add("return " + retName + ";");
                }
            }

            if (p.ResultTable != null && p.ResultTable.DataContextPropertyName == null)
            {
                var columns = new MemberGroup { IsCompact = true };

                foreach (var c in p.ResultTable.Columns.Values)
                {
                    if (c.MemberName != c.ColumnName)
                        c.Attributes.Add(new Attribute("Column") { Parameters = { NormalizeStringName(c.ColumnName) } });
                    columns.Members.Add(c);
                }

                p.ResultTable.Members.Add(columns);
                proc.Members.Add(p.ResultTable);
            }
        }

        if (procs.Members.Count > 0)
            Model.Types.Add(new Class(DataContextObject.Name + "StoredProcedures", procs) { IsStatic = true });

        if (funcs.Members.Count > 0)
            Model.Types.Add(new Class("SqlFunctions", funcs) { IsStatic = true });

        if (tabfs.Members.Count > 0)
            DataContextObject.Members.Add(tabfs);

        foreach (var schema in schemas.Values)
        {
            if (schema.Procedures.Members.Count > 0)
                schema.Type.Members.Add(new Class(DataContextObject.Name + "StoredProcedures", schema.Procedures) { IsStatic = true });

            if (schema.Functions.Members.Count > 0)
                schema.Type.Members.Add(new Class("SqlFunctions", schema.Functions) { IsStatic = true });

            if (schema.TableFunctions.Members.Count > 0)
                schema.DataContext.Members.Add(schema.TableFunctions);
        }
    }

    if (defTableExtensions.Members.Count > 0)
    {
        Model.Usings.Add("System.Linq");
        Model.Types.Add(new Class("TableExtensions", defTableExtensions) { IsStatic = true });
    }

    foreach (var schema in schemas.Values)
    {
        Model.Types.Add(schema.Type);

        if (schema.TableExtensions.Members.Count > 0)
        {
            Model.Usings.Add("System.Linq");
            schema.Type.Members.Add(schema.TableExtensions);
        }
    }

    Tables.    Clear();
    Procedures.Clear();

    Model.SetTree();

    AfterGenerateLinqToDBModel();
}

string NormalizeStringName(string name)
{
    if (name.Contains("\"") || name.Contains("\\"))
        return @"@""" + name.Replace(@"""", @"""""") + @"""";
    return "\"" + name + "\"";
}

        #line default
        #line hidden

        #line 6 "/Users/nikola/projects/blog/LinqToDB.Templates/LinqToDB.MySql.ttinclude"

LinqToDB.Data.DataConnection GetMySqlConnection(string connectionString)
{
    return LinqToDB.DataProvider.MySql.MySqlTools.CreateDataConnection(connectionString);
}

LinqToDB.Data.DataConnection GetMySqlConnection(string server, string database, string uid, string password, int port=3306)
{
    return GetMySqlConnection(string.Format(
        "Server={0};Port={4};Database={1};Uid={2};Pwd={3};charset=utf8;",
        server, database, uid, password,port));
}

void LoadMySqlMetadata(string connectionString)
{
    var dataConnection = GetMySqlConnection(connectionString);
    LoadMetadata(dataConnection);
}

void LoadMySqlMetadata(string server, string database, string uid, string password,int port=3306)
{
    var dataConnection = GetMySqlConnection(server, database, uid, password,port);
    LoadMetadata(dataConnection);
}

        #line default
        #line hidden

        public global::Microsoft.VisualStudio.TextTemplating.ITextTemplatingEngineHost Host {
            get {
                return this.hostValue;
            }
            set {
                this.hostValue = value;
            }
        }

        public override string TransformText() {
            this.GenerationEnvironment = null;

            #line 6 "/Users/nikola/projects/blog/LinqToDB.Templates/DataModel.ttinclude"

    {
        var beforeGenerateModel = BeforeGenerateModel;
        BeforeGenerateModel = () =>
        {
            GenerateTypesFromMetadata();
            beforeGenerateModel();
        };
    }

            #line default
            #line hidden

            #line 6 "/Users/nikola/projects/blog/LinqToDB.Templates/LinqToDB.ttinclude"

    if (BaseDataContextClass == null)
        BaseDataContextClass = "LinqToDB.Data.DataConnection";

            #line default
            #line hidden

            #line 2 "/Users/nikola/projects/blog/LinqToDB.Templates/LinqToDB.MySql.ttinclude"

    LinqToDB.DataProvider.MySql.MySqlTools.ResolveMySql(
        typeof(MySql.Data.MySqlClient.MySqlConnection).Assembly);

            #line default
            #line hidden

            #line 4 "/Users/nikola/projects/blog/Models/db.tt"

    /*
        1. Copy this file to a folder where you would like to generate your data model,
           rename it, and delete .txt extension. For example:

            MyProject
                DataModels
                    MyDatabase.tt

        2. Modify the connection settings below to connect to your database.

        3. Add connection string to the web/app.config file:

            <connectionStrings>
                <add name="MyDatabase" providerName="System.Data.SqlClient"
                    connectionString="Data Source=.;Database=MyDatabase;User Id=User;Password=TestPassword;" />
            </connectionStrings>

        4. To access your database use the following code:

            using (var db = new MyDatabaseDB())
            {
                var q =
                    from c in db.Customers
                    select c;

                foreach (var c in q)
                    Console.WriteLine(c.ContactName);
            }

        5. See more at https://github.com/linq2db/t4models

        If you need to use the Microsoft.SqlServer.Types namespace, install the Microsoft.SqlServer.Types nuget,
        and replace the following include at the top of this file:

            "$(ProjectDir)LinqToDB.Templates\LinqToDB.SqlServer.Tools.ttinclude"

        with

            "$(ProjectDir)LinqToDB.Templates\LinqToDB.SqlServer.SqlTypes.Tools.ttinclude"
    */

    NamespaceName = "blog.db";
    DataContextName = "BlogDb";
    GenerateConstructors = false;

    LoadMySqlMetadata("sql7.freemysqlhosting.net", "", "", "");

            #line default
            #line hidden

            #line 57 "/Users/nikola/projects/blog/Models/db.tt"
            this.Write("\n// Disable documentation warnings\n#pragma warning disable 1591\n\n");

            #line default
            #line hidden

            #line 61 "/Users/nikola/projects/blog/Models/db.tt"

    GenerateModel();

            #line default
            #line hidden
            return this.GenerationEnvironment.ToString();
        }

        public override void Initialize() {
            base.Initialize();
        }
    }
}
MaceWindu commented 5 years ago

nicely done ~Microsoft~ apparently it could be MonoDevelop authors .... random names public partial class GeneratedTextTransformation776afa5f and this is how it should be: public class GeneratedTextTransformation : Microsoft.VisualStudio.TextTemplating.TextTransformation

MaceWindu commented 5 years ago

Ok, now it is clear what is going on, I think we can workaround it somehow. Maybe we can remove use of GeneratedTextTransformation type or use it in some other way.

goors commented 5 years ago

As far as I can see, nothing we can do about this. Right? GeneratedTextTransformation is deeply used so I don't see how can we quickly fix this for just one osx user :) You might just close the issue.

MaceWindu commented 5 years ago

No-no-no, leave it open :) I don't have time right now to work on it, but it definitely fixable, so I will get back to it later

MaceWindu commented 5 years ago

probably easiest fix will be using Microsoft.VisualStudio.TextTemplating.TextTransformation istead of GeneratedTextTransformation, as both T4 hosts use it as base class and methods we need declared there

MaceWindu commented 5 years ago

you can actually replace and try it

goors commented 5 years ago

tried that one but now I am getting a bunch of other errors like:

LinqToDB.Templates/T4Model.ttinclude(87,6) : error CS1061: Type Microsoft.VisualStudio.TextTemplating.TextTransformation does not contain a definition for RenderUsings'and no extension method RenderUsings'of type Microsoft.VisualStudio.TextTemplating.TextTransformation' could be found. Are you missing an assembly reference?

Error list is huge as hell :)

MaceWindu commented 5 years ago

I see, we call our methods using this type, so it's a bit more tricky. Will require additional refactorings, but doable

MaceWindu commented 5 years ago

Renaming issue into feature to support MonoDevelop t4 host as vs for mac is just a mondevelop variation. Also I see Rider has T4 support too, so we can also try to support it too if anybody interested.

sdanyliv commented 5 years ago

This one: https://github.com/Tak/monodevelop/blob/master/main/src/addins/TextTemplating/Mono.TextTemplating/Mono.TextTemplating/TemplatingEngine.cs#L184-L186 Looks like it is configurable

goors commented 4 years ago

Hi guys, no update on this?

MaceWindu commented 4 years ago

Not yet. I'm currently busy with providers refactoring for v3, so probably I can work on it in January.

soracoder commented 4 years ago

Hi! any update on this?

MaceWindu commented 4 years ago

Sorry, still nothing. First we need to release preview2, which is almost ready.

MaceWindu commented 4 years ago

Started looking into it and found out that monodevelop's implementation doesn't support project properties, that's why $(LinqToDBT4SqlServerTemplatesDirectory) (and other directives like that) doesn't work.

Not sure what to do actually. Without project properties we cannot locate required libraries. Probably it is time for us to move from T4 templates to more universal scaffolding tool implementation.

goors commented 4 years ago

@MaceWindu this would be awesome to have. My development looks really funny now. I do stuff on osx and then when i want to do db sync i fire up other laptop with windows :) click and send file via email. Funny right :) Or i ask some of colleges windows hackers :) to sync and share me the file :)

MaceWindu commented 4 years ago

I actually have some ideas how to fix it for T4. Will probably require small manual work for monodevelop/vs4mac users.

soracoder commented 3 years ago

Still not news here? My workflow for this is also a bit of a mess having to recreate the model from a separated machine 😅

MaceWindu commented 3 years ago

Plan to work on it next month full-time. Hope nothing will mess my plans :)

isaacfi commented 2 years ago

Hi @MaceWindu

Do you have any news about this integration? Sometimes I don't have a PC with windows to generate the model :(

BR

MaceWindu commented 2 years ago

Almost done, but I don't have right now to finish it...

ericvergnaud commented 2 years ago

It'd be great to have this. After hard coding the paths, and removing unsupported attribute once="true", latest version crashes VS when used. Very frustrating, although it's a VS issue rather than a linq2db one.

isaacfi commented 2 years ago

Hi @ericvergnaud

I understand that the issue is in Visual Studio, but is there any way to run the T4 code generation by Windows command line or terminal in Mac or Linux?

ericvergnaud commented 2 years ago

@isaacfi sure on Mac I can run MSBuild ..., or .net... but how would the cmd line look like ?

MaceWindu commented 2 years ago

FYI https://github.com/linq2db/linq2db/pull/3098#issuecomment-1022184862

goors commented 1 year ago

This still does not work. But you https://linq2db.github.io/articles/CLI.html works as expected.