diff options
30 files changed, 3943 insertions, 75 deletions
diff --git a/ComicRackWebViewer/BCRDatabase.cs b/ComicRackWebViewer/BCRDatabase.cs index cc524b0..3de88d8 100644 --- a/ComicRackWebViewer/BCRDatabase.cs +++ b/ComicRackWebViewer/BCRDatabase.cs @@ -19,6 +19,7 @@ namespace BCR { private const int COMIC_DB_VERSION = 1; + private BcrDB bcrDB; private SQLiteConnection mConnection; private string mFolder; private const string DIRECTORY = "ComicRack BCR"; @@ -31,15 +32,17 @@ namespace BCR private Guid bcrGuid = Guid.Empty; public GlobalSettings globalSettings { get { return _globalSettings; } } - - + + public string Filename { get { return mFolder + "\\bcr.s3db"; } } + public static Database Instance { get { return instance; } } public static string ConfigurationFolder { get { return DIRECTORY; } } - + public BcrDB DB { get { return bcrDB; } } + public Database() { mFolder = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), DIRECTORY); @@ -47,7 +50,10 @@ namespace BCR { Directory.CreateDirectory(mFolder); } - + + LinqToDB.Data.DataConnection.AddConfiguration("bcr", "Data Source=" + Filename, LinqToDB.DataProvider.SQLite.SQLiteTools.GetDataProvider()); + bcrDB = new BcrDB("bcr"); + string s = "cYo.Projects.ComicRack.Engine.Database.ComicLibraryListItem"; ComicListItem item = Program.Database.ComicLists.GetItems<ComicListItem>(false).FirstOrDefault((ComicListItem cli) => cli.GetType().ToString() == s); if (item != null) diff --git a/ComicRackWebViewer/BCREntities.cs b/ComicRackWebViewer/BCREntities.cs index 07a5d45..15b5f3a 100644 --- a/ComicRackWebViewer/BCREntities.cs +++ b/ComicRackWebViewer/BCREntities.cs @@ -1,5 +1,6 @@ using cYo.Projects.ComicRack.Engine; using cYo.Projects.ComicRack.Engine.Database; +using LinqToDB.Mapping; using System; using System.Collections; using System.Collections.Generic; @@ -68,7 +69,7 @@ namespace BCR private string _caption = null; private ComicBook book; - private ComicProgress progress; + private comic_progress progress; private bool useComicrackProgress = false; // Cache expensive properties @@ -85,7 +86,7 @@ namespace BCR public Comic(ComicBook source, BCRUser user) { book = source; - useComicrackProgress = user.settings.use_comicrack_progress; + useComicrackProgress = user.settings2.use_comicrack_progress != 0; progress = useComicrackProgress ? null : user.GetComicProgress(source.Id); //_caption = new Lazy<string>(() => { return book.Caption; }); @@ -199,11 +200,11 @@ namespace BCR #region User specific properties - public int UserCurrentPage { get { return useComicrackProgress ? book.CurrentPage : (progress == null ? 0 : progress.CurrentPage); } } + public int UserCurrentPage { get { return useComicrackProgress ? book.CurrentPage : (progress == null ? 0 : (int)progress.current_page.GetValueOrDefault()); } } - public int UserLastPageRead { get { return useComicrackProgress ? book.LastPageRead : (progress == null ? 0 : progress.LastPageRead); } } + public int UserLastPageRead { get { return useComicrackProgress ? book.LastPageRead : (progress == null ? 0 : (int)progress.last_page_read.GetValueOrDefault()); } } - public string UserOpenedTimeAsText { get { return useComicrackProgress ? book.OpenedTimeAsText : (progress == null ? "" : progress.DateLastRead); } } + public string UserOpenedTimeAsText { get { return useComicrackProgress ? book.OpenedTimeAsText : (progress == null ? "" : progress.date_last_read); } } #endregion User specific properties } @@ -256,18 +257,7 @@ namespace BCR public string Type { get; set; } } - public class ComicProgress - { - public int CurrentPage { get; set; } - - public int DatabaseId { get; set; } - - public string DateLastRead { get; set; } - - public Guid Id { get; set; } - public int LastPageRead { get; set; } - } - + public class Publisher { public string Imprint { get; set; } diff --git a/ComicRackWebViewer/BCRUser.cs b/ComicRackWebViewer/BCRUser.cs index f3802fc..b5eb6d3 100644 --- a/ComicRackWebViewer/BCRUser.cs +++ b/ComicRackWebViewer/BCRUser.cs @@ -2,6 +2,9 @@ using Nancy.Security; using System; using System.Collections.Generic; using System.Data.SQLite; +using System.Linq; +using ComicRackWebViewer; +using LinqToDB; namespace BCR { @@ -9,11 +12,11 @@ namespace BCR { // Cache the entire list of comic progress, so we only need to perform one SQL lookup instead of // one per comic. This speeds up the displaying of a list of comics. - public Dictionary<Guid, ComicProgress> comicProgress = new Dictionary<Guid, ComicProgress>(); + public Dictionary<Guid, comic_progress> comicProgress2 = new Dictionary<Guid, comic_progress>(); public Guid homeListId; - public UserSettings settings = new UserSettings(); + public user_settings settings2 = new user_settings(); public IEnumerable<string> Claims { get; set; } @@ -23,10 +26,10 @@ namespace BCR public string UserName { get; set; } - public ComicProgress GetComicProgress(Guid comicId) + public comic_progress GetComicProgress(Guid comicId) { - ComicProgress progress; - if (comicProgress.TryGetValue(comicId, out progress)) + comic_progress progress; + if (comicProgress2.TryGetValue(comicId, out progress)) { return progress; } @@ -34,35 +37,34 @@ namespace BCR return null; } - public UserSettings GetSettings() + public user_settings GetSettings() { - return settings; + return settings2; } public void Initialize() { - settings.Load(this); + settings2 = (from s in Database.Instance.DB.user_settings + where s.user_id == UserId + select s).Single(); - comicProgress.Clear(); - using (SQLiteDataReader reader = Database.Instance.ExecuteReader("SELECT id, comic_id, current_page, last_page_read, date_last_read FROM comic_progress WHERE user_id = " + UserId + ";")) - { - while (reader.Read()) - { - ComicProgress progress = new ComicProgress(); - progress.DatabaseId = reader.GetInt32(0); - progress.Id = new Guid(reader.GetString(1)); - progress.CurrentPage = reader.GetInt32(2); - progress.LastPageRead = reader.GetInt32(3); - progress.DateLastRead = reader.GetString(4); - comicProgress.Add(progress.Id, progress); - } - } + comicProgress2.Clear(); + + var result = from comic in Database.Instance.DB.comic_progress + where comic.user_id == UserId + select comic; + + comicProgress2 = result.ToDictionary<comic_progress, Guid>((p) => { return Guid.Parse(p.comic_id); }); } public void ResetComicProgress(Guid comicId) { - comicProgress.Remove(comicId); - Database.Instance.ExecuteNonQuery("DELETE FROM comic_progress WHERE comic_id = '" + comicId + "' AND user_id = " + UserId + ";"); + comicProgress2.Remove(comicId); + string comic_id = comicId.ToString(); + + Database.Instance.DB.comic_progress + .Where(p => p.comic_id == comic_id && p.user_id == UserId) + .Delete(); } public bool SetAccessLevel() @@ -77,35 +79,37 @@ namespace BCR public void UpdateComicProgress(Guid comicId, int currentPage) { - ComicProgress progress; - if (comicProgress.TryGetValue(comicId, out progress)) + comic_progress progress2; + if (comicProgress2.TryGetValue(comicId, out progress2)) { - if (currentPage > progress.LastPageRead) - progress.LastPageRead = currentPage; + if (currentPage > progress2.last_page_read) + progress2.last_page_read = currentPage; + + progress2.current_page = currentPage; + progress2.date_last_read = System.DateTime.Now.ToString("s"); - progress.CurrentPage = currentPage; - progress.DateLastRead = System.DateTime.Now.ToString("s"); - Database.Instance.ExecuteNonQuery("UPDATE comic_progress SET current_page = " + progress.CurrentPage + ", last_page_read = " + progress.LastPageRead + ", date_last_read = '" + progress.DateLastRead + "' WHERE id = " + progress.DatabaseId); + Database.Instance.DB.Update(progress2); } else { - progress = new ComicProgress(); - progress.Id = comicId; - progress.LastPageRead = currentPage; - progress.CurrentPage = currentPage; - progress.DateLastRead = System.DateTime.Now.ToString("s"); + progress2 = new comic_progress(); + progress2.comic_id = comicId.ToString(); + progress2.last_page_read = currentPage; + progress2.current_page = currentPage; + progress2.date_last_read = System.DateTime.Now.ToString("s"); - Database.Instance.ExecuteNonQuery("INSERT INTO comic_progress (user_id, comic_id, current_page, last_page_read, date_last_read) VALUES(" + UserId + ", '" + progress.Id.ToString() + "', " + progress.CurrentPage + ", " + progress.LastPageRead + ", '" + progress.DateLastRead + "');"); + comicProgress2.Add(comicId, progress2); - progress.DatabaseId = (int)Database.Instance.GetLastInsertRowId(); - comicProgress.Add(comicId, progress); + progress2.id = Convert.ToInt64(Database.Instance.DB.InsertWithIdentity(progress2)); + comicProgress2.Add(comicId, progress2); + } } - public void UpdateSettings(UserSettings settings) + public void UpdateSettings(user_settings settings) { - this.settings = settings; - settings.Save(this); + settings2 = settings; + Database.Instance.DB.Update(settings2); } /* public Guid GetHomeList() diff --git a/ComicRackWebViewer/Bcr.s3db b/ComicRackWebViewer/Bcr.s3db Binary files differnew file mode 100644 index 0000000..1845ffd --- /dev/null +++ b/ComicRackWebViewer/Bcr.s3db diff --git a/ComicRackWebViewer/BcrDB.tt b/ComicRackWebViewer/BcrDB.tt new file mode 100644 index 0000000..e7b83d0 --- /dev/null +++ b/ComicRackWebViewer/BcrDB.tt @@ -0,0 +1,10 @@ +<#@ template language="C#" debug="True" hostSpecific="True" #> +<#@ output extension=".generated.cs" #> +<#@ include file="$(ProjectDir)LinqToDB.Templates\LinqToDB.SQLite.Tools.ttinclude" #> +<#@ include file="$(ProjectDir)LinqToDB.Templates\PluralizationService.ttinclude" #> +<# + NamespaceName = "BCR"; + + LoadSQLiteMetadata(Host.ResolvePath("."), "Bcr.s3db"); + GenerateModel(); +#>
\ No newline at end of file diff --git a/ComicRackWebViewer/ComicRackWebViewer.csproj b/ComicRackWebViewer/ComicRackWebViewer.csproj index ca49ccf..aa5e7e7 100644 --- a/ComicRackWebViewer/ComicRackWebViewer.csproj +++ b/ComicRackWebViewer/ComicRackWebViewer.csproj @@ -92,6 +92,9 @@ <Reference Include="ICSharpCode.SharpZipLib">
<HintPath>C:\Program Files\ComicRack\ICSharpCode.SharpZipLib.dll</HintPath>
</Reference>
+ <Reference Include="linq2db">
+ <HintPath>..\packages\linq2db.1.0.1.1\lib\net40\linq2db.dll</HintPath>
+ </Reference>
<Reference Include="Linq2Rest, Version=3.3.2.0, Culture=neutral, PublicKeyToken=73a6c4cf8c87a30e, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion>
<HintPath>..\packages\Linq2Rest.3.3.2.0\lib\Net40\Linq2Rest.dll</HintPath>
@@ -136,6 +139,11 @@ </None>
<Compile Include="BCRAPI.cs" />
<Compile Include="BCRDatabase.cs" />
+ <Compile Include="BcrDB.generated.cs">
+ <AutoGen>True</AutoGen>
+ <DesignTime>True</DesignTime>
+ <DependentUpon>BcrDB.tt</DependentUpon>
+ </Compile>
<Compile Include="BCREntities.cs" />
<Compile Include="BCRExtensions.cs" />
<Compile Include="BCRInstaller.cs" />
@@ -160,12 +168,36 @@ <Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="SaltedHash.cs" />
<Compile Include="UserDatabase.cs" />
- <Compile Include="UserSettings.cs" />
<Compile Include="WebHost.cs" />
</ItemGroup>
<ItemGroup>
<None Include="app.config" />
<None Include="badaap_icon.png" />
+ <None Include="BcrDB.tt">
+ <Generator>TextTemplatingFileGenerator</Generator>
+ <LastGenOutput>BcrDB.generated.cs</LastGenOutput>
+ </None>
+ <None Include="LinqToDB.Templates\DataAnnotations.ttinclude" />
+ <None Include="LinqToDB.Templates\DataModel.ttinclude" />
+ <None Include="LinqToDB.Templates\EditableObject.ttinclude" />
+ <None Include="LinqToDB.Templates\LinqToDB.Access.ttinclude" />
+ <None Include="LinqToDB.Templates\LinqToDB.DB2.ttinclude" />
+ <None Include="LinqToDB.Templates\LinqToDB.Firebird.ttinclude" />
+ <None Include="LinqToDB.Templates\LinqToDB.Informix.ttinclude" />
+ <None Include="LinqToDB.Templates\LinqToDB.MySql.ttinclude" />
+ <None Include="LinqToDB.Templates\LinqToDB.Oracle.ttinclude" />
+ <None Include="LinqToDB.Templates\LinqToDB.PostgreSQL.ttinclude" />
+ <None Include="LinqToDB.Templates\LinqToDB.SqlCe.ttinclude" />
+ <None Include="LinqToDB.Templates\LinqToDB.SQLite.Tools.ttinclude" />
+ <None Include="LinqToDB.Templates\LinqToDB.SQLite.ttinclude" />
+ <None Include="LinqToDB.Templates\LinqToDB.SqlServer.ttinclude" />
+ <None Include="LinqToDB.Templates\LinqToDB.Sybase.ttinclude" />
+ <None Include="LinqToDB.Templates\LinqToDB.Tools.ttinclude" />
+ <None Include="LinqToDB.Templates\LinqToDB.ttinclude" />
+ <None Include="LinqToDB.Templates\NotifyPropertyChanged.ttinclude" />
+ <None Include="LinqToDB.Templates\PluralizationService.ttinclude" />
+ <None Include="LinqToDB.Templates\T4Model.ttinclude" />
+ <None Include="LinqToDB.Templates\Validation.ttinclude" />
<None Include="Package.ini">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</None>
@@ -1541,6 +1573,9 @@ <DependentUpon>MainForm.cs</DependentUpon>
</EmbeddedResource>
</ItemGroup>
+ <ItemGroup>
+ <Service Include="{508349B6-6B84-4DF5-91F0-309BEEBAD82D}" />
+ </ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<PropertyGroup>
<PostBuildEvent>$(ProjectDir)postbuild.bat "$(ProjectDir)" "$(TargetDir)"
diff --git a/ComicRackWebViewer/LinqToDB.Templates/DataAnnotations.ttinclude b/ComicRackWebViewer/LinqToDB.Templates/DataAnnotations.ttinclude new file mode 100644 index 0000000..3fac32a --- /dev/null +++ b/ComicRackWebViewer/LinqToDB.Templates/DataAnnotations.ttinclude @@ -0,0 +1,65 @@ +<# + { + var beforeGenerateModel = BeforeGenerateModel; + BeforeGenerateModel = () => + { + beforeGenerateModel(); + DataAnnotationsImpl(); + }; + } +#> +<#+ +void DataAnnotationsImpl() +{ + foreach (Class cl in GetTreeNodes(Model).OfType<Class>()) + { + foreach (var p in GetTreeNodes(cl).OfType<Property>()) + { + if (p.DisplayName != null) + { + p.Attributes.Add(new Attribute("Display", "Name=\"" + p.DisplayName + "\"") { IsSeparated = true }); + } + + if (p.IsRequired) + { + var attr = new Attribute("Required") { IsSeparated = true }; + + if (p.IsRequiredMessage != null) + attr.Parameters.Add(string.Format("ErrorMessage=\"" + p.IsRequiredMessage + "\"", p.DisplayName ?? p.Name)); + + p.Attributes.Add(attr); + } + + if (p.StringLength > 0) + { + var attr = new Attribute("StringLength", p.StringLength.ToString()) { IsSeparated = true }; + + if (p.StringLengthMessage != null) + attr.Parameters.Add(string.Format("ErrorMessage=\"" + p.StringLengthMessage + "\"", p.DisplayName ?? p.Name)); + + p.Attributes.Add(attr); + +// p.Attributes.Add( +// new Attribute("StringLength", +// p.StringLength.ToString(), +// string.Format( +// "ErrorMessage=\"The {0} must be a string with a maximum lenfth of {1}.\"", +// p.DisplayName ?? "field", +// p.StringLength)) +// { +// IsSeparated = true +// }); + } + } + } +} + +partial class Property +{ + public string DisplayName; + public bool IsRequired; + public string IsRequiredMessage; + public int StringLength; + public string StringLengthMessage; +} +#> diff --git a/ComicRackWebViewer/LinqToDB.Templates/DataModel.ttinclude b/ComicRackWebViewer/LinqToDB.Templates/DataModel.ttinclude new file mode 100644 index 0000000..6295538 --- /dev/null +++ b/ComicRackWebViewer/LinqToDB.Templates/DataModel.ttinclude @@ -0,0 +1,502 @@ +<#@ assembly name="System.Data" #> +<#@ import namespace="System.Data" #> +<#@ import namespace="LinqToDB.Data" #> +<#@ include file="T4Model.ttinclude" #> +<# + { + var beforeGenerateModel = BeforeGenerateModel; + BeforeGenerateModel = () => + { + GenerateTypesFromMetadata(); + beforeGenerateModel(); + }; + } +#><#+ + +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}>"; + +string[] DatabaseQuote = null; + +bool GenerateDatabaseName = false; +bool GenerateConstructors = true; +string DefaultConfiguration = null; +bool GenerateAssociations = true; +bool GenerateBackReferences = true; +bool ReplaceSimilarTables = true; +bool IncludeDefaultSchema = true; + +Class DataContextObject; + +bool PluralizeClassNames = false; +bool SingularizeClassNames = true; +bool PluralizeDataContextPropertyNames = true; +bool SingularizeDataContextPropertyNames = false; + +LinqToDB.SchemaProvider.GetSchemaOptions GetSchemaOptions = + new LinqToDB.SchemaProvider.GetSchemaOptions(); + +LinqToDB.SqlProvider.ISqlBuilder SqlBuilder; + +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 = db.Database + "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 + .Select(t => new + { + t, + key = t.IsDefaultSchema ? t.TableName : t.SchemaName + "." + t.TableName, + table = new Table + { + Schema = (t.IsDefaultSchema && !IncludeDefaultSchema) || string.IsNullOrEmpty(t.SchemaName)? null : t.SchemaName, + BaseClass = BaseEntityClass, + TableName = t.TableName, + TypeName = + PluralizeClassNames ? ToPlural (t.TypeName) : + SingularizeClassNames ? ToSingular(t.TypeName) : t.TypeName, + DataContextPropertyName = + PluralizeDataContextPropertyNames ? ToPlural (t.TypeName) : + SingularizeDataContextPropertyNames ? ToSingular(t.TypeName) : t.TypeName, + IsView = t.IsView, + Description = t.Description, + Columns = t.Columns.ToDictionary( + 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 = CheckType(c.SystemType, c.MemberName), + Type = c.MemberType, + SkipOnInsert = c.SkipOnInsert, + SkipOnUpdate = c.SkipOnUpdate, + Description = c.Description, + }) + } + }) + .ToList(); + + 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) + { + key.t.table.ForeignKeys.Add(key.KeyName, key.key); + + if (key.k.BackReference != null) + key.key.BackReference = keys.First(k => k.k == key.k.BackReference).key; + + key.key.MemberName = key.key.AssociationType == AssociationType.OneToMany ? + ToPlural(key.key.MemberName) : ToSingular(key.key.MemberName); + } + + 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 = p.MemberName, + IsFunction = p.IsFunction, + IsTableFunction = p.IsTableFunction, + IsDefaultSchema = p.IsDefaultSchema, + ResultTable = p.ResultTable == null ? null : + new Table + { + TypeName = + PluralizeClassNames ? ToPlural (p.ResultTable.TypeName) : + SingularizeClassNames ? ToSingular(p.ResultTable.TypeName) : p.ResultTable.TypeName, + 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 = CheckType(c.SystemType, c.MemberName), + Type = c.MemberType, + 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 = 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; +} + +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"); + + var 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" + }; + + foreach (var t in Tables.Values) + { + if (keyWords.Contains(t.TypeName)) + t.TypeName = "@" + t.TypeName; + + if (keyWords.Contains(t.DataContextPropertyName)) + t.DataContextPropertyName = "@" + t.DataContextPropertyName; + + foreach (var col in t.Columns.Values) + if (keyWords.Contains(col.MemberName)) + col.MemberName = "@" + col.MemberName; + } +} + +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."); +} + + +Dictionary<string,Table> Tables = new Dictionary<string,Table> (); +Dictionary<string,Procedure> Procedures = new Dictionary<string,Procedure>(); + +public partial class Table : Class +{ + public string Schema { get; set; } + public string TableName { get; set; } + public string DataContextPropertyName { get; set; } + public bool IsView { get; set; } + public string Description { get; set; } + public string AliasPropertyName { get; set; } + public string AliasTypeName { 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 DbType DbType; + public string Description; + public bool IsPrimaryKey; + public int PrimaryKeyOrder; + public bool SkipOnUpdate; + public bool SkipOnInsert; + public bool IsDuplicateOrEmpty; + 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 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 int? Size { get; set; } + + public string ParameterName { get; set; } + public string ParameterType { get; set; } + public Type SystemType { get; set; } + public string DataType { get; set; } +} + +#> diff --git a/ComicRackWebViewer/LinqToDB.Templates/EditableObject.ttinclude b/ComicRackWebViewer/LinqToDB.Templates/EditableObject.ttinclude new file mode 100644 index 0000000..c4ad9f5 --- /dev/null +++ b/ComicRackWebViewer/LinqToDB.Templates/EditableObject.ttinclude @@ -0,0 +1,235 @@ +<# + { + var beforeGenerateModel = BeforeGenerateModel; + BeforeGenerateModel = () => + { + EditableObjectImpl(); + beforeGenerateModel(); + }; + + SetPropertyValueAction += (obj,prop,val) => + { + if (prop == "IsEditable") + obj.IsEditable = (bool)val; + }; + } +#> +<#+ +void EditableObjectImpl() +{ + foreach (Property prop in GetTreeNodes(Model).OfType<Property>().Where(p => p.IsEditable).ToList()) + { + SetPropertyValue(prop, "IsNotifying", true); + + List<IClassMember> parentMembers; + + MemberGroup gr = null; + + if (prop.Parent is Class) + { + var parent = (Class)prop.Parent; + parentMembers = parent.Members; + } + else + { + var parent = (MemberGroup)prop.Parent; + parentMembers = parent.Members; + + parent.IsCompact = false; + } + + var name = prop.Name.Trim(); + var type = prop.Type.Trim(); + + if (gr == null) + { + gr = new MemberGroup + { + Region = name + " : " + type, + Members = { prop }, + IsPropertyGroup = true, + }; + + var index = parentMembers.IndexOf(prop); + + parentMembers.RemoveAt(index); + parentMembers.Insert (index, gr); + } + + var originalField = new Field(type, "_original" + name) + { + AccessModifier = AccessModifier.Private, + InsertBlankLineAfter = false, + }; + + gr.Members.Insert(0, originalField); + + var currentField = new Field(type, " _current" + name) + { + AccessModifier = AccessModifier.Private, + InsertBlankLineAfter = false, + }; + + if (prop.InitValue != null) + currentField.InitValue = prop.InitValue; + + gr.Members.Insert(0, currentField); + + prop.Name = " " + name; + prop.Type = " " + type; + prop.IsAuto = false; + + if (prop.HasGetter) prop.GetBody.Add("return " + currentField.Name.Trim() + ";"); + if (prop.HasSetter) prop.SetBody.Add(currentField.Name.Trim() + " = value;"); + + var ac = new Method ("void", "Accept" + name + "Changes", null, new[] { string.Format("_original{0} = _current{0};", name) }); + var rc = new Method ("void", "Reject" + name + "Changes", null, new[] { string.Format("{0} = _original{0};", name) }); + var id = new Property("bool", "Is" + name + "Dirty") + .InitGetter(string.Format(prop.IsDirtyText, "_current" + name, "_original" + name)); + + gr.Members.Add(new MemberGroup + { + Region = "EditableObject support", + Members = { ac, rc, id }, + }); + + prop.Parent.SetTree(); + } + + foreach (Class cl in GetTreeNodes(Model).OfType<Class>()) + { + var props = GetTreeNodes(cl).OfType<Property>().Where(p => p.IsEditable).ToList(); + + if (props.Count > 0) + { + if (props.Any(p => p.IsEditable)) + { + var ctor = GetTreeNodes(cl) + .OfType<Method>() + .FirstOrDefault(m => m.Name == cl.Name && m.Parameters.Count == 0); + + if (ctor == null) + { + ctor = new Method(null, cl.Name); + cl.Members.Insert(0, ctor); + } + + ctor.Body.Add("AcceptChanges();"); + } + + var maxLen = props.Max(p => p.Name.Trim().Length); + + var ac = new Method("void", "AcceptChanges") { IsVirtual = true }; + var rc = new Method("void", "RejectChanges") { IsVirtual = true }; + var id = new Property("bool", "IsDirty") { IsAuto = false, HasSetter = false, IsVirtual = true }; + + ac.Body.Add("BeforeAcceptChanges();"); + ac.Body.Add(""); + rc.Body.Add("BeforeRejectChanges();"); + rc.Body.Add(""); + id.GetBody.Add("return"); + + foreach (var p in props) + { + var name = p.Name.Trim(); + + ac.Body.Add(string.Format("Accept{0}Changes();", name)); + rc.Body.Add(string.Format("Reject{0}Changes();", name)); + id.GetBody.Add(string.Format("\tIs{0}Dirty{1} ||", name, LenDiff(maxLen, name))); + } + + ac.Body.Add(""); + ac.Body.Add("AfterAcceptChanges();"); + rc.Body.Add(""); + rc.Body.Add("AfterRejectChanges();"); + id.GetBody[id.GetBody.Count - 1] = id.GetBody[id.GetBody.Count - 1].Trim(' ' , '|') + ";"; + + cl.Members.Add(new MemberGroup + { + Region = "EditableObject support", + Members = + { + new MemberGroup + { + IsCompact = true, + Members = + { + new Method("void", "BeforeAcceptChanges") { AccessModifier = AccessModifier.Partial }, + new Method("void", "AfterAcceptChanges") { AccessModifier = AccessModifier.Partial }, + } + }, + ac, + new MemberGroup + { + IsCompact = true, + Members = + { + new Method("void", "BeforeRejectChanges") { AccessModifier = AccessModifier.Partial }, + new Method("void", "AfterRejectChanges") { AccessModifier = AccessModifier.Partial }, + } + }, + rc, + id + }, + }); + + if (!cl.Interfaces.Contains("IEditableObject")) + { + if (!Model.Usings.Contains("System.ComponentModel")) + Model.Usings.Add("System.ComponentModel"); + + cl.Interfaces.Add("IEditableObject"); + + cl.Members.Add(new MemberGroup + { + Region = "IEditableObject support", + Members = + { + new MemberGroup + { + IsCompact = true, + Members = + { + new Field ("bool", "_isEditing") { AccessModifier = AccessModifier.Private }, + new Property("bool", " IsEditing").InitGetter("_isEditing"), + } + }, + new MemberGroup + { + IsCompact = true, + Members = + { + new Method("void", "BeginEdit", null, new[] { "AcceptChanges();", "_isEditing = true;" }) { IsVirtual = true }, + new Method("void", "CancelEdit", null, new[] { "_isEditing = false;", "RejectChanges();", }) { IsVirtual = true }, + new Method("void", "EndEdit", null, new[] { "_isEditing = false;", "AcceptChanges();", }) { IsVirtual = true }, + } + }, + } + }); + } + } + + cl.SetTree(); + } +} + +partial class Property +{ + public bool IsEditable; + public string IsDirtyText = "{0} != {1}"; +} + +class EditableProperty : Property +{ + public EditableProperty() + { + IsEditable = true; + } + + public EditableProperty(string type, string name) + : base(type, name, null, null) + { + IsEditable = true; + } +} +#> diff --git a/ComicRackWebViewer/LinqToDB.Templates/LinqToDB.Access.ttinclude b/ComicRackWebViewer/LinqToDB.Templates/LinqToDB.Access.ttinclude new file mode 100644 index 0000000..31d009f --- /dev/null +++ b/ComicRackWebViewer/LinqToDB.Templates/LinqToDB.Access.ttinclude @@ -0,0 +1,26 @@ +<#@ include file="LinqToDB.ttinclude" #> +<#+ +LinqToDB.Data.DataConnection GetAccessConnection(string connectionString) +{ + return LinqToDB.DataProvider.Access.AccessTools.CreateDataConnection(connectionString); +} + +LinqToDB.Data.DataConnection GetAccessConnection(string path, string database) +{ + return GetAccessConnection(string.Format( + "Provider=Microsoft.Jet.OLEDB.4.0;Data Source={0};Locale Identifier=1033;Jet OLEDB:Engine Type=5;Persist Security Info=True", + System.IO.Path.Combine(path, database))); +} + +void LoadAccessMetadata(string connectionString) +{ + var dataConnection = GetAccessConnection(connectionString); + LoadMetadata(dataConnection); +} + +void LoadAccessMetadata(string path, string database) +{ + var dataConnection = GetAccessConnection(path, database); + LoadMetadata(dataConnection); +} +#> diff --git a/ComicRackWebViewer/LinqToDB.Templates/LinqToDB.DB2.ttinclude b/ComicRackWebViewer/LinqToDB.Templates/LinqToDB.DB2.ttinclude new file mode 100644 index 0000000..1b064b9 --- /dev/null +++ b/ComicRackWebViewer/LinqToDB.Templates/LinqToDB.DB2.ttinclude @@ -0,0 +1,28 @@ +<#@ include file="LinqToDB.ttinclude" #> +<# + LinqToDB.DataProvider.DB2.DB2Tools.ResolveDB2( + typeof(IBM.Data.DB2.DB2Connection).Assembly); +#><#+ +LinqToDB.Data.DataConnection GetDB2Connection(string connectionString) +{ + var conn = new IBM.Data.DB2.DB2Connection(connectionString); + return LinqToDB.DataProvider.DB2.DB2Tools.CreateDataConnection(conn); +} + +LinqToDB.Data.DataConnection GetDB2Connection(string server, string port, string database, string uid, string password) +{ + return GetDB2Connection(string.Format("Server={0}:{1};Database={2};UID={3};PWD={4};", server, port, database, uid, password)); +} + +void LoadDB2Metadata(string connectionString) +{ + var dataConnection = GetDB2Connection(connectionString); + LoadMetadata(dataConnection); +} + +void LoadDB2Metadata(string server, string port, string database, string uid, string password) +{ + var dataConnection = GetDB2Connection(server, port, database, uid, password); + LoadMetadata(dataConnection); +} +#> diff --git a/ComicRackWebViewer/LinqToDB.Templates/LinqToDB.Firebird.ttinclude b/ComicRackWebViewer/LinqToDB.Templates/LinqToDB.Firebird.ttinclude new file mode 100644 index 0000000..97aec35 --- /dev/null +++ b/ComicRackWebViewer/LinqToDB.Templates/LinqToDB.Firebird.ttinclude @@ -0,0 +1,41 @@ +<#@ include file="LinqToDB.ttinclude" #> +<# + LinqToDB.DataProvider.Firebird.FirebirdTools.ResolveFirebird( + typeof(FirebirdSql.Data.FirebirdClient.FbConnection).Assembly); +#> +<#+ +LinqToDB.Data.DataConnection GetFirebirdConnection(string connectionString) +{ + return LinqToDB.DataProvider.Firebird.FirebirdTools.CreateDataConnection(connectionString); +} + +LinqToDB.Data.DataConnection GetFirebirdConnection(string server, string database) +{ + return GetFirebirdConnection(server, database, "SYSDBA", "masterkey"); +} + +LinqToDB.Data.DataConnection GetFirebirdConnection(string server, string database, string uid, string password) +{ + return GetFirebirdConnection(string.Format( + "DataSource={0};Database={1};User Id={2};Password={3}", + server, database, uid, password)); +} + +void LoadFirebirdMetadata(string connectionString) +{ + var dataConnection = GetFirebirdConnection(connectionString); + LoadMetadata(dataConnection); +} + +void LoadFirebirdMetadata(string server, string database, string uid, string password) +{ + var dataConnection = GetFirebirdConnection(server, database, uid, password); + LoadMetadata(dataConnection); +} + +void LoadFirebirdMetadata(string server, string database) +{ + var dataConnection = GetFirebirdConnection(server, database); + LoadMetadata(dataConnection); +} +#> diff --git a/ComicRackWebViewer/LinqToDB.Templates/LinqToDB.Informix.ttinclude b/ComicRackWebViewer/LinqToDB.Templates/LinqToDB.Informix.ttinclude new file mode 100644 index 0000000..3beaa0b --- /dev/null +++ b/ComicRackWebViewer/LinqToDB.Templates/LinqToDB.Informix.ttinclude @@ -0,0 +1,28 @@ +<#@ include file="LinqToDB.ttinclude" #> +<# + LinqToDB.DataProvider.Informix.InformixTools.ResolveInformix( + typeof(IBM.Data.Informix.IfxConnection).Assembly); +#><#+ +LinqToDB.Data.DataConnection GetInformixConnection(string connectionString) +{ + var conn = new IBM.Data.Informix.IfxConnection(connectionString); + return LinqToDB.DataProvider.Informix.InformixTools.CreateDataConnection(conn); +} + +LinqToDB.Data.DataConnection GetInformixConnection(string host, string port, string server, string database, string uid, string password) +{ + return GetInformixConnection(string.Format("Host={0};Service={1};Server={2};Protocol=onsoctcp;Database={3};UID={4};PWD={5}", host, port, server, database, uid, password)); +} + +void LoadInformixMetadata(string connectionString) +{ + var dataConnection = GetInformixConnection(connectionString); + LoadMetadata(dataConnection); +} + +void LoadInformixMetadata(string host, string port, string server, string database, string uid, string password) +{ + var dataConnection = GetInformixConnection(host, port, server, database, uid, password); + LoadMetadata(dataConnection); +} +#> diff --git a/ComicRackWebViewer/LinqToDB.Templates/LinqToDB.MySql.ttinclude b/ComicRackWebViewer/LinqToDB.Templates/LinqToDB.MySql.ttinclude new file mode 100644 index 0000000..9b2ac55 --- /dev/null +++ b/ComicRackWebViewer/LinqToDB.Templates/LinqToDB.MySql.ttinclude @@ -0,0 +1,30 @@ +<#@ include file="LinqToDB.ttinclude" #> +<# + LinqToDB.DataProvider.MySql.MySqlTools.ResolveMySql( + typeof(MySql.Data.MySqlClient.MySqlConnection).Assembly); +#> +<#+ +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) +{ + return GetMySqlConnection(string.Format( + "Server={0};Port=3306;Database={1};Uid={2};Pwd={3};charset=utf8;", + server, database, uid, password)); +} + +void LoadMySqlMetadata(string connectionString) +{ + var dataConnection = GetMySqlConnection(connectionString); + LoadMetadata(dataConnection); +} + +void LoadMySqlMetadata(string server, string database, string uid, string password) +{ + var dataConnection = GetMySqlConnection(server, database, uid, password); + LoadMetadata(dataConnection); +} +#> diff --git a/ComicRackWebViewer/LinqToDB.Templates/LinqToDB.Oracle.ttinclude b/ComicRackWebViewer/LinqToDB.Templates/LinqToDB.Oracle.ttinclude new file mode 100644 index 0000000..7014697 --- /dev/null +++ b/ComicRackWebViewer/LinqToDB.Templates/LinqToDB.Oracle.ttinclude @@ -0,0 +1,27 @@ +<#@ include file="LinqToDB.ttinclude" #> +<# + LinqToDB.DataProvider.Oracle.OracleTools.ResolveOracle( + typeof(Oracle.DataAccess.Client.OracleConnection).Assembly); +#><#+ +LinqToDB.Data.DataConnection GetOracleConnection(string connectionString) +{ + return LinqToDB.DataProvider.Oracle.OracleTools.CreateDataConnection(connectionString); +} + +LinqToDB.Data.DataConnection GetOracleConnection(string server, string port, string database, string uid, string password) +{ + return GetOracleConnection(string.Format("Data Source=(DESCRIPTION=(ADDRESS=(PROTOCOL=TCP)(HOST={0})(PORT={1}))(CONNECT_DATA=(SERVICE_NAME={2})));User Id={3};Password={4};", server, port, database, uid, password)); +} + +void LoadOracleMetadata(string connectionString) +{ + var dataConnection = GetOracleConnection(connectionString); + LoadMetadata(dataConnection); +} + +void LoadOracleMetadata(string server, string port, string database, string uid, string password) +{ + var dataConnection = GetOracleConnection(server, port, database, uid, password); + LoadMetadata(dataConnection); +} +#> diff --git a/ComicRackWebViewer/LinqToDB.Templates/LinqToDB.PostgreSQL.ttinclude b/ComicRackWebViewer/LinqToDB.Templates/LinqToDB.PostgreSQL.ttinclude new file mode 100644 index 0000000..1c079da --- /dev/null +++ b/ComicRackWebViewer/LinqToDB.Templates/LinqToDB.PostgreSQL.ttinclude @@ -0,0 +1,57 @@ +<#@ include file="LinqToDB.ttinclude" #> +<# + LinqToDB.DataProvider.PostgreSQL.PostgreSQLTools.ResolvePostgreSQL( + typeof(Npgsql.NpgsqlConnection).Assembly); + + { + var beforeGenerateLinqToDBModel = BeforeGenerateLinqToDBModel; + BeforeGenerateLinqToDBModel = () => + { + beforeGenerateLinqToDBModel(); + SetCaseSensitiveNames(); + }; + } +#><#+ + +bool GenerateCaseSensitiveNames = false; + +void SetCaseSensitiveNames() +{ + if (GenerateCaseSensitiveNames) + { + foreach (var t in Tables.Values) + { + if (t.TableName.Any(char.IsUpper)) + t.TableName = "\\\"" + t.TableName + "\\\""; + + foreach (var c in t.Columns.Values) + { + if (c.ColumnName.Any(char.IsUpper)) + c.ColumnName = "\\\"" + c.ColumnName + "\\\""; + } + } + } +} + +LinqToDB.Data.DataConnection GetPostgreSQLConnection(string connectionString) +{ + return LinqToDB.DataProvider.PostgreSQL.PostgreSQLTools.CreateDataConnection(connectionString); +} + +LinqToDB.Data.DataConnection GetPostgreSQLConnection(string server, string port, string database, string uid, string password) +{ + return GetPostgreSQLConnection(string.Format(@"Server={0};Port={1};Database={2};User Id={3};Password={4};Pooling=true;MinPoolSize=10;MaxPoolSize=100;Protocol=3;", server, port, database, uid, password)); +} + +void LoadPostgreSQLMetadata(string connectionString) +{ + var dataConnection = GetPostgreSQLConnection(connectionString); + LoadMetadata(dataConnection); +} + +void LoadPostgreSQLMetadata(string server, string port, string database, string uid, string password) +{ + var dataConnection = GetPostgreSQLConnection(server, port, database, uid, password); + LoadMetadata(dataConnection); +} +#> diff --git a/ComicRackWebViewer/LinqToDB.Templates/LinqToDB.SQLite.Tools.ttinclude b/ComicRackWebViewer/LinqToDB.Templates/LinqToDB.SQLite.Tools.ttinclude new file mode 100644 index 0000000..d90434d --- /dev/null +++ b/ComicRackWebViewer/LinqToDB.Templates/LinqToDB.SQLite.Tools.ttinclude @@ -0,0 +1,3 @@ +<#@ assembly name="$(SolutionDir)packages\linq2db.SQLite.1.0.0\tools\System.Data.SQLite.dll" #> +<#@ include file="LinqToDB.Tools.ttinclude" #> +<#@ include file="LinqToDB.SQLite.ttinclude" #> diff --git a/ComicRackWebViewer/LinqToDB.Templates/LinqToDB.SQLite.ttinclude b/ComicRackWebViewer/LinqToDB.Templates/LinqToDB.SQLite.ttinclude new file mode 100644 index 0000000..3c2703e --- /dev/null +++ b/ComicRackWebViewer/LinqToDB.Templates/LinqToDB.SQLite.ttinclude @@ -0,0 +1,44 @@ +<#@ include file="LinqToDB.ttinclude" #> +<# + LinqToDB.DataProvider.SQLite.SQLiteTools.ResolveSQLite(typeof(System.Data.SQLite.SQLiteConnection).Assembly); + + { + var beforeGenerateLinqToDBModel = BeforeGenerateLinqToDBModel; + BeforeGenerateLinqToDBModel = () => + { + ConvertSQLiteMetadata(); + beforeGenerateLinqToDBModel(); + }; + } +#> +<#+ +void ConvertSQLiteMetadata() +{ + foreach (var t in Tables.Values) + foreach (var fk in t.ForeignKeys.Values) + if (fk.MemberName.Length == 0 || char.IsDigit(fk.MemberName[0])) + fk.MemberName = "FK_" + fk.MemberName; +} + +LinqToDB.Data.DataConnection GetSQLiteConnection(string connectionString) +{ + return LinqToDB.DataProvider.SQLite.SQLiteTools.CreateDataConnection(connectionString); +} + +LinqToDB.Data.DataConnection GetSQLiteConnection(string path, string database) +{ + return GetSQLiteConnection(string.Format("Data Source={0}", System.IO.Path.Combine(path, database))); +} + +void LoadSQLiteMetadata(string connectionString) +{ + var dataConnection = GetSQLiteConnection(connectionString); + LoadMetadata(dataConnection); +} + +void LoadSQLiteMetadata(string path, string database) +{ + var dataConnection = GetSQLiteConnection(path, database); + LoadMetadata(dataConnection); +} +#> diff --git a/ComicRackWebViewer/LinqToDB.Templates/LinqToDB.SqlCe.ttinclude b/ComicRackWebViewer/LinqToDB.Templates/LinqToDB.SqlCe.ttinclude new file mode 100644 index 0000000..d327cbb --- /dev/null +++ b/ComicRackWebViewer/LinqToDB.Templates/LinqToDB.SqlCe.ttinclude @@ -0,0 +1,28 @@ +<#@ include file="LinqToDB.ttinclude" #> +<# + LinqToDB.DataProvider.SqlCe.SqlCeTools.ResolveSqlCe( + typeof(System.Data.SqlServerCe.SqlCeConnection).Assembly); +#> +<#+ +LinqToDB.Data.DataConnection GetSqlCeConnection(string connectionString) +{ + return LinqToDB.DataProvider.SqlCe.SqlCeTools.CreateDataConnection(connectionString); +} + +LinqToDB.Data.DataConnection GetSqlCeConnection(string path, string database) +{ + return GetSqlCeConnection(string.Format("Data Source={0}", System.IO.Path.Combine(path, database))); +} + +void LoadSqlCeMetadata(string connectionString) +{ + var dataConnection = GetSqlCeConnection(connectionString); + LoadMetadata(dataConnection); +} + +void LoadSqlCeMetadata(string path, string database) +{ + var dataConnection = GetSqlCeConnection(path, database); + LoadMetadata(dataConnection); +} +#> diff --git a/ComicRackWebViewer/LinqToDB.Templates/LinqToDB.SqlServer.ttinclude b/ComicRackWebViewer/LinqToDB.Templates/LinqToDB.SqlServer.ttinclude new file mode 100644 index 0000000..04354c5 --- /dev/null +++ b/ComicRackWebViewer/LinqToDB.Templates/LinqToDB.SqlServer.ttinclude @@ -0,0 +1,107 @@ +<#@ include file="LinqToDB.ttinclude" #> +<# + LinqToDB.DataProvider.SqlServer.SqlServerTools.ResolveSqlTypes( + typeof(Microsoft.SqlServer.Types.SqlGeography).Assembly); + + { + var afterGenerateLinqToDBModel = AfterGenerateLinqToDBModel; + AfterGenerateLinqToDBModel = () => + { + afterGenerateLinqToDBModel(); + GenerateSqlServerTypes(); + }; + } +#> +<#+ +void GenerateSqlServerTypes() +{ + Model.Usings.Add("System.Collections.Generic"); + Model.Usings.Add("System.Linq.Expressions"); + Model.Usings.Add("System.Reflection"); + Model.Usings.Add("LinqToDB"); + Model.Usings.Add("LinqToDB.DataProvider.SqlServer"); + + DataContextObject.Members.Add( + new MemberGroup + { + Region = "FreeTextTable", + Members = + { + new Class("FreeTextKey<T>", + new MemberGroup + { + IsCompact = true, + Members = + { + new Field("T", "Key"), + new Field("int", "Rank") + } + }) + { + IsPartial = false + }, + + new Method("ITable<FreeTextKey<TKey>>", "FreeTextTable<TTable,TKey>", + new[] { "string field", "string text" }, + new[] + { + "return this.GetTable<FreeTextKey<TKey>>(", + " this,", + " ((MethodInfo)(MethodBase.GetCurrentMethod())).MakeGenericMethod(typeof(TTable), typeof(TKey)),", + " field,", + " text);", + }) + { + Attributes = { new Attribute("FreeTextTableExpression") } + }, + new Method("ITable<FreeTextKey<TKey>>", "FreeTextTable<TTable,TKey>", + new[] { "Expression<Func<TTable,string>> fieldSelector", "string text" }, + new[] + { + "return this.GetTable<FreeTextKey<TKey>>(", + " this,", + " ((MethodInfo)(MethodBase.GetCurrentMethod())).MakeGenericMethod(typeof(TTable), typeof(TKey)),", + " fieldSelector,", + " text);", + }) + { + Attributes = { new Attribute("FreeTextTableExpression") } + }, + } + } + ); +} + +LinqToDB.Data.DataConnection GetSqlServerConnection(string connectionString) +{ + return LinqToDB.DataProvider.SqlServer.SqlServerTools.CreateDataConnection(connectionString); +} + +LinqToDB.Data.DataConnection GetSqlServerConnection(string server, string database) +{ + return GetSqlServerConnection(string.Format("Data Source={0};Database={1};Integrated Security=SSPI", server, database)); +} + +LinqToDB.Data.DataConnection GetSqlServerConnection(string server, string database, string user, string password) +{ + return GetSqlServerConnection(string.Format("Server={0};Database={1};User Id={2};Password={3};", server, database, user, password)); +} + +void LoadSqlServerMetadata(string connectionString) +{ + var dataConnection = GetSqlServerConnection(connectionString); + LoadMetadata(dataConnection); +} + +void LoadSqlServerMetadata(string server, string database) +{ + var dataConnection = GetSqlServerConnection(server, database); + LoadMetadata(dataConnection); +} + +void LoadSqlServerMetadata(string server, string database, string user, string password) +{ + var dataConnection = GetSqlServerConnection(server, database, user, password); + LoadMetadata(dataConnection); +} +#> diff --git a/ComicRackWebViewer/LinqToDB.Templates/LinqToDB.Sybase.ttinclude b/ComicRackWebViewer/LinqToDB.Templates/LinqToDB.Sybase.ttinclude new file mode 100644 index 0000000..80ca407 --- /dev/null +++ b/ComicRackWebViewer/LinqToDB.Templates/LinqToDB.Sybase.ttinclude @@ -0,0 +1,82 @@ +<#@ include file="LinqToDB.ttinclude" #> +<# + LinqToDB.DataProvider.Sybase.SybaseTools.ResolveSybase( + typeof(Sybase.Data.AseClient.AseConnection).Assembly); + + { + var beforeGenerateLinqToDBModel = BeforeGenerateLinqToDBModel; + BeforeGenerateLinqToDBModel = () => + { + beforeGenerateLinqToDBModel(); + GenerateSybaseTypes(); + }; + } +#><#+ +bool GenerateSybaseSystemTables = false; + +void GenerateSybaseTypes() +{ + if (GenerateSybaseSystemTables) + { + Tables.Add("sysobjects", new Table + { + Name = "sysobjects", + TableName = "sysobjects", + TypeName = "SysObject", + DataContextPropertyName = "SysObjects", + BaseClass = BaseEntityClass, + Columns = new Dictionary<string,Column>() + { + { "name", new Column { Name = "name", ColumnName = "name", ColumnType = "varchar", Type = "string", DbType = DbType.AnsiString, /*Length = 255*/ }}, + { "id", new Column { Name = "id", ColumnName = "id", ColumnType = "int", Type = "int", DbType = DbType.Int32, }}, + { "uid", new Column { Name = "uid", ColumnName = "uid", ColumnType = "int", Type = "int", DbType = DbType.Int32, }}, + { "type", new Column { Name = "type", ColumnName = "type", ColumnType = "char", Type = "string", DbType = DbType.AnsiStringFixedLength, /*Length = 2,*/ }}, + { "userstat", new Column { Name = "userstat", ColumnName = "userstat", ColumnType = "smallint", Type = "short", DbType = DbType.Int16, }}, + { "sysstat", new Column { Name = "sysstat", ColumnName = "sysstat", ColumnType = "smallint", Type = "short", DbType = DbType.Int16, }}, + { "indexdel", new Column { Name = "indexdel", ColumnName = "indexdel", ColumnType = "smallint", Type = "short", DbType = DbType.Int16, }}, + { "schemacnt", new Column { Name = "schemacnt", ColumnName = "schemacnt", ColumnType = "smallint", Type = "short", DbType = DbType.Int16, }}, + { "sysstat2", new Column { Name = "sysstat2", ColumnName = "sysstat2", ColumnType = "int", Type = "int", DbType = DbType.Int32, }}, + { "crdate", new Column { Name = "crdate", ColumnName = "crdate", ColumnType = "datetime", Type = "DateTime", DbType = DbType.DateTime }}, + { "expdate", new Column { Name = "expdate", ColumnName = "expdate", ColumnType = "datetime", Type = "DateTime", DbType = DbType.DateTime }}, + { "deltrig", new Column { Name = "deltrig", ColumnName = "deltrig", ColumnType = "int", Type = "int", DbType = DbType.Int32, }}, + { "instrig", new Column { Name = "instrig", ColumnName = "instrig", ColumnType = "int", Type = "int", DbType = DbType.Int32, }}, + { "updtrig", new Column { Name = "updtrig", ColumnName = "updtrig", ColumnType = "int", Type = "int", DbType = DbType.Int32, }}, + { "seltrig", new Column { Name = "seltrig", ColumnName = "seltrig", ColumnType = "int", Type = "int", DbType = DbType.Int32, }}, + { "ckfirst", new Column { Name = "ckfirst", ColumnName = "ckfirst", ColumnType = "int", Type = "int", DbType = DbType.Int32, }}, + { "cache", new Column { Name = "cache", ColumnName = "cache", ColumnType = "smallint", Type = "short", DbType = DbType.Int16 }}, + { "audflags", new Column { Name = "audflags", ColumnName = "audflags", ColumnType = "int", Type = "int", DbType = DbType.Int32, IsNullable = true }}, + { "objspare", new Column { Name = "objspare", ColumnName = "objspare", ColumnType = "int", Type = "int", DbType = DbType.Int32, }}, + { "versionts", new Column { Name = "versionts", ColumnName = "versionts", ColumnType = "binary", Type = "byte[]", DbType = DbType.Binary, IsNullable = true, /*Length = 6*/ }}, + { "loginame", new Column { Name = "loginame", ColumnName = "loginame", ColumnType = "varchar", Type = "string", DbType = DbType.AnsiString, /*Length = 30*/ }}, + } + }); + } +} + +LinqToDB.Data.DataConnection GetSybaseConnection(string connectionString) +{ + return LinqToDB.DataProvider.Sybase.SybaseTools.CreateDataConnection(connectionString); +} + +LinqToDB.Data.DataConnection GetSybaseConnection(string server, string database) +{ + return GetSybaseConnection(string.Format("Data Source={0};Database={1};Integrated Security=SSPI", server, database)); +} + +LinqToDB.Data.DataConnection GetSybaseConnection(string server, string port, string database, string uid, string password) +{ + return GetSybaseConnection(string.Format("Data Source={0};Port={1};Database={2};Uid={3};Password={4};Charset=utf8;", server, port, database, uid, password)); +} + +void LoadSybaseMetadata(string connectionString) +{ + var dataConnection = GetSybaseConnection(connectionString); + LoadMetadata(dataConnection); +} + +void LoadSybaseMetadata(string server, string port, string database, string uid, string password) +{ + var dataConnection = GetSybaseConnection(server, port, database, uid, password); + LoadMetadata(dataConnection); +} +#> diff --git a/ComicRackWebViewer/LinqToDB.Templates/LinqToDB.Tools.ttinclude b/ComicRackWebViewer/LinqToDB.Templates/LinqToDB.Tools.ttinclude new file mode 100644 index 0000000..d1fa105 --- /dev/null +++ b/ComicRackWebViewer/LinqToDB.Templates/LinqToDB.Tools.ttinclude @@ -0,0 +1 @@ +<#@ assembly name="$(SolutionDir)packages\linq2db.t4models.1.0.2\tools\linq2db.dll" #> diff --git a/ComicRackWebViewer/LinqToDB.Templates/LinqToDB.ttinclude b/ComicRackWebViewer/LinqToDB.Templates/LinqToDB.ttinclude new file mode 100644 index 0000000..798421f --- /dev/null +++ b/ComicRackWebViewer/LinqToDB.Templates/LinqToDB.ttinclude @@ -0,0 +1,477 @@ +<#@ assembly name="System.Data" #> +<#@ import namespace="System.Data" #> +<#@ import namespace="LinqToDB.Data" #> +<#@ include file="DataModel.ttinclude" #> +<# + if (BaseDataContextClass == null) + BaseDataContextClass = "LinqToDB.Data.DataConnection"; +#> +<#+ +Action BeforeGenerateLinqToDBModel = () => {}; +Action AfterGenerateLinqToDBModel = () => {}; + +bool GenerateObsoleteAttributeForAliases; +bool IsCompactColumns = true; +bool IsCompactColumnAliases = true; + +void GenerateTypesFromMetadata() +{ + BeforeGenerateLinqToDBModel(); + + Model.Usings.Add("LinqToDB"); + Model.Usings.Add("LinqToDB.Mapping"); + + if (NamespaceName == null) + NamespaceName = "DataModel"; + + var props = new MemberGroup { IsCompact = true }; + var aliases = new MemberGroup { IsCompact = true, Region = "Alias members" }; + + if (GenerateConstructors) + { + if (DefaultConfiguration == null) + DataContextObject.Members.Add(new Method(null, DataContextObject.Name)); + else + DataContextObject.Members.Add(new Method(null, DataContextObject.Name) { AfterSignature = { ": base(\"" + DefaultConfiguration + "\")" } }); + DataContextObject.Members.Add(new Method(null, DataContextObject.Name, new[] { "string configuration" }) { AfterSignature = { ": base(configuration)" } }); + } + + if (Tables.Count > 0) + DataContextObject.Members.Insert(0, props); + + foreach (var t in Tables.Values.OrderBy(tbl => tbl.TypeName)) + { + var dcProp = new Property( + string.Format("ITable<{0}>", t.TypeName), + t.DataContextPropertyName, + new[] { string.Format("this.GetTable<{0}>()", t.TypeName) }, + null); + + 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 + '"'); + + t.Attributes.Add(new Attribute("Table", tableAttrs.ToArray()) { IsSeparated = true } ); + + if (t.IsView) + t.Comment.Add(" View"); + + 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); + + foreach (var c in t.Columns.Values) + { + // Column. + // + var ca = new Attribute("Column"); + var canBeReplaced = true; + + if (c.MemberName != c.ColumnName) + { + ca.Parameters.Add('"' + c.ColumnName + '"'); + 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; + } + + 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) + { + 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" }; + + foreach (var key in keys) + { + 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.TypeName); + else + key.Type = 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")); + + key.Attributes.Add(aa); + + SetPropertyValue(key, "IsNotifying", true); + SetPropertyValue(key, "IsEditable", true); + + associations.Members.Add(key); + } + + t.Members.Add(associations); + } + } + + Model.Types.Add(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 (aliases.Members.Count > 0) + DataContextObject.Members.Add(aliases); + + 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"); + + var procs = new MemberGroup(); + var funcs = new MemberGroup(); + var tabfs = new MemberGroup { Region = "Table Functions" }; + + foreach (var p in Procedures.Values) + { + if (p.ResultTable == null && p.ResultException != null) + continue; + + var proc = new MemberGroup { Region = p.Name }; + + if (p.IsTableFunction) + { + p.Attributes.Add(new Attribute("Sql.TableFunction", "Name=\"" + p.ProcedureName + "\"")); + 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.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 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() + + "\""; + + var inputParameters = p.ProcParameters.Where(pp => pp.IsIn). ToList(); + var outputParameters = p.ProcParameters.Where(pp => pp.IsOut).ToList(); + + spName += inputParameters.Count == 0 ? ");" : ","; + + var hasOut = outputParameters.Any(pr => pr.IsOut); + var prefix = hasOut ? "var ret = " : "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?)pr.ParameterType.Length) ?? 0; + + for (var i = 0; i < inputParameters.Count; i++) + { + var pr = inputParameters[i]; + + var str = string.Format("\tnew DataParameter(\"{0}\", {1}{2})", + pr.SchemaName, + LenDiff(maxLenSchema, pr.SchemaName), + pr.ParameterName); + + if (pr.IsOut) + { + str += LenDiff(maxLenParam, pr.ParameterName); + 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 ret;"); + } + } + + 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 = { '"' + c.ColumnName + '"' } }); + columns.Members.Add(c); + } + + p.ResultTable.Members.Add(columns); + proc.Members.Add(p.ResultTable); + } + + proc.Members.Add(p); + + if (!p.IsFunction) procs.Members.Add(proc); + else if (p.IsTableFunction) tabfs.Members.Add(proc); + else funcs.Members.Add(proc); + } + + 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); + } + + Tables. Clear(); + Procedures.Clear(); + + Model.SetTree(); + + AfterGenerateLinqToDBModel(); +} +#> diff --git a/ComicRackWebViewer/LinqToDB.Templates/NotifyPropertyChanged.ttinclude b/ComicRackWebViewer/LinqToDB.Templates/NotifyPropertyChanged.ttinclude new file mode 100644 index 0000000..3d4f738 --- /dev/null +++ b/ComicRackWebViewer/LinqToDB.Templates/NotifyPropertyChanged.ttinclude @@ -0,0 +1,379 @@ +<# + { + var beforeGenerateModel = BeforeGenerateModel; + BeforeGenerateModel = () => + { + beforeGenerateModel(); + NotifyPropertyChangedImpl(); + }; + + SetPropertyValueAction += (obj,prop,val) => + { + if (prop == "IsNotifying") + obj.IsNotifying = (bool)val; + }; + } +#><#+ +public bool ImplementNotifyPropertyChanging; + +void NotifyPropertyChangedImpl() +{ + foreach (Property prop in GetTreeNodes(Model).OfType<Property>().Where(p => p.IsNotifying).ToList()) + { + List<IClassMember> parentMembers; + + MemberGroup gr = null; + + if (prop.Parent is Class) + { + var parent = (Class)prop.Parent; + parentMembers = parent.Members; + } + else + { + var parent = (MemberGroup)prop.Parent; + + parent.IsCompact = false; + + parentMembers = parent.Members; + + if (parent.IsPropertyGroup) + gr = parent; + } + + var name = prop.Name.Trim(); + var type = prop.Type.Trim(); + + if (gr == null) + { + gr = new MemberGroup + { + Region = name + " : " + type, + Members = { prop }, + IsPropertyGroup = true, + }; + + var index = parentMembers.IndexOf(prop); + + parentMembers.RemoveAt(index); + parentMembers.Insert (index, gr); + } + + if (prop.IsAuto) + { + var field = new Field(type, "_" + ToCamelCase(name)) + { + AccessModifier = AccessModifier.Private, + InsertBlankLineAfter = false, + }; + + if (prop.InitValue != null) + field.InitValue = prop.InitValue; + + gr.Members.Insert(0, field); + + prop.Name = " " + name; + prop.Type = " " + type; + prop.IsAuto = false; + + if (prop.HasGetter) prop.GetBody.Add("return " + field.Name + ";"); + if (prop.HasSetter) prop.SetBody.Add(field.Name + " = value;"); + } + + var methods = new MemberGroup + { + Region = "INotifyPropertyChanged support", + Members = + { + new Field("const string", "NameOf" + name) + { + InitValue = "\"" + name + "\"", + AccessModifier = AccessModifier.Public, + }, + new Field("PropertyChangedEventArgs", "_" + ToCamelCase(name) + "ChangedEventArgs") + { + InitValue = "new PropertyChangedEventArgs(NameOf" + name + ")", + AccessModifier = AccessModifier.Private, + IsStatic = true, + IsReadonly = true, + }, + new Method("void", "On" + name + "Changed", null, + new[] { "OnPropertyChanged(_" + ToCamelCase(name) + "ChangedEventArgs);" }) + { + AccessModifier = AccessModifier.Private + } + } + }; + + gr.Members.Add(methods); + + if (prop.Dependents.Count == 0) + prop.Dependents.Add(name); + + if (ImplementNotifyPropertyChanging) + { + gr.Members.Add(new MemberGroup + { + Region = "INotifyPropertyChanging support", + Members = + { + new Field("PropertyChangingEventArgs", "_" + ToCamelCase(name) + "ChangingEventArgs") + { + InitValue = "new PropertyChangingEventArgs(NameOf" + name + ")", + AccessModifier = AccessModifier.Private, + IsStatic = true, + IsReadonly = true, + }, + new Method("void", "On" + name + "Changing", null, + new[] { "OnPropertyChanging(_" + ToCamelCase(name) + "ChangingEventArgs);" }) + { + AccessModifier = AccessModifier.Private + } + } + }); + } + + if (prop.HasSetter) + { + prop.SetBody = prop.SetBody.Select(s => "\t" + s).ToList(); + + string getValue; + + if (prop.GetBody.Count == 1 && prop.GetBody[0].StartsWith("return")) + { + getValue = prop.GetBody[0].Substring("return".Length).Trim(' ', '\t', ';'); + } + else + { + getValue = name; + } + + var insSpaces = prop.SetBody.Count > 1; + var n = 0; + + prop.SetBody.Insert(n++, "if (" + getValue + " != value)"); + prop.SetBody.Insert(n++, "{"); + + if (ImplementNotifyPropertyChanging) + { + foreach (var dp in prop.Dependents) + prop.SetBody.Insert(n++, "\tOn" + dp + "Changing();"); + prop.SetBody.Insert(n++, ""); + } + + prop.SetBody.Insert(n++, "\tBefore" + name + "Changed(value);"); + + if (insSpaces) + { + prop.SetBody.Insert(3, ""); + prop.SetBody.Add(""); + } + + prop.SetBody.Add("\tAfter" + name + "Changed();"); + prop.SetBody.Add(""); + + foreach (var dp in prop.Dependents) + prop.SetBody.Add("\tOn" + dp + "Changed();"); + + prop.SetBody.Add("}"); + + methods.Members.Insert(0, new MemberGroup + { + IsCompact = true, + Members = + { + new Method("void", "Before" + name + "Changed", new[] { type + " newValue" }) { AccessModifier = AccessModifier.Partial }, + new Method("void", "After" + name + "Changed") { AccessModifier = AccessModifier.Partial }, + } + }); + } + + prop.Parent.SetTree(); + + ITree p = prop.Parent; + + while (!(p is Class) && p != null) + p = p.Parent; + + if (p != null) + { + var cl = (Class)p; + + if (!cl.Interfaces.Contains("INotifyPropertyChanged")) + { + if (!Model.Usings.Contains("System.ComponentModel")) + Model.Usings.Add("System.ComponentModel"); + + cl.Interfaces.Add("INotifyPropertyChanged"); + + cl.Members.Add(new MemberGroup + { + Region = "INotifyPropertyChanged support", + Members = + { + new Event("PropertyChangedEventHandler", "PropertyChanged") + { + IsVirtual = true, + Attributes = { new Attribute("field : NonSerialized") { Conditional = "!SILVERLIGHT" } } + }, + new Method("void", "OnPropertyChanged", new[] { "string propertyName" }, OnPropertyChangedBody) + { + AccessModifier = AccessModifier.Protected + }, + new Method("void", "OnPropertyChanged", new[] { "PropertyChangedEventArgs arg" }, OnPropertyChangedArgBody) + { + AccessModifier = AccessModifier.Protected + }, + } + }); + } + + if (ImplementNotifyPropertyChanging && !cl.Interfaces.Contains("INotifyPropertyChanging")) + { + if (!Model.Usings.Contains("System.ComponentModel")) + Model.Usings.Add("System.ComponentModel"); + + cl.Interfaces.Add("INotifyPropertyChanging"); + + cl.Members.Add(new MemberGroup + { + Region = "INotifyPropertyChanging support", + Members = + { + new Event("PropertyChangingEventHandler", "PropertyChanging") + { + IsVirtual = true, + Attributes = { new Attribute("field : NonSerialized") { Conditional = "!SILVERLIGHT" } } + }, + new Method("void", "OnPropertyChanging", new[] { "string propertyName" }, OnPropertyChangingBody) + { + AccessModifier = AccessModifier.Protected + }, + new Method("void", "OnPropertyChanging", new[] { "PropertyChangingEventArgs arg" }, OnPropertyChangingArgBody) + { + AccessModifier = AccessModifier.Protected + }, + } + }); + } + } + } +} + +public string[] OnPropertyChangedBody = new[] +{ + "var propertyChanged = PropertyChanged;", + "", + "if (propertyChanged != null)", + "{", + "#if SILVERLIGHT", + "\tif (System.Windows.Deployment.Current.Dispatcher.CheckAccess())", + "\t\tpropertyChanged(this, new PropertyChangedEventArgs(propertyName));", + "\telse", + "\t\tSystem.Windows.Deployment.Current.Dispatcher.BeginInvoke(", + "\t\t\t() =>", + "\t\t\t{", + "\t\t\t\tvar pc = PropertyChanged;", + "\t\t\t\tif (pc != null)", + "\t\t\t\t\tpc(this, new PropertyChangedEventArgs(propertyName));", + "\t\t\t});", + "#else", + "\tpropertyChanged(this, new PropertyChangedEventArgs(propertyName));", + "#endif", + "}", +}; + +public string[] OnPropertyChangedArgBody = new[] +{ + "var propertyChanged = PropertyChanged;", + "", + "if (propertyChanged != null)", + "{", + "#if SILVERLIGHT", + "\tif (System.Windows.Deployment.Current.Dispatcher.CheckAccess())", + "\t\tpropertyChanged(this, arg);", + "\telse", + "\t\tSystem.Windows.Deployment.Current.Dispatcher.BeginInvoke(", + "\t\t\t() =>", + "\t\t\t{", + "\t\t\t\tvar pc = PropertyChanged;", + "\t\t\t\tif (pc != null)", + "\t\t\t\t\tpc(this, arg);", + "\t\t\t});", + "#else", + "\tpropertyChanged(this, arg);", + "#endif", + "}", +}; + +public string[] OnPropertyChangingBody = new[] +{ + "var propertyChanging = PropertyChanging;", + "", + "if (propertyChanging != null)", + "{", + "#if SILVERLIGHT", + "\tif (System.Windows.Deployment.Current.Dispatcher.CheckAccess())", + "\t\tpropertyChanging(this, new PropertyChangingEventArgs(propertyName));", + "\telse", + "\t\tSystem.Windows.Deployment.Current.Dispatcher.BeginInvoke(", + "\t\t\t() =>", + "\t\t\t{", + "\t\t\t\tvar pc = PropertyChanging;", + "\t\t\t\tif (pc != null)", + "\t\t\t\t\tpc(this, new PropertyChangingEventArgs(propertyName));", + "\t\t\t});", + "#else", + "\tpropertyChanging(this, new PropertyChangingEventArgs(propertyName));", + "#endif", + "}", +}; + +public string[] OnPropertyChangingArgBody = new[] +{ + "var propertyChanging = PropertyChanging;", + "", + "if (propertyChanging != null)", + "{", + "#if SILVERLIGHT", + "\tif (System.Windows.Deployment.Current.Dispatcher.CheckAccess())", + "\t\tpropertyChanging(this, arg);", + "\telse", + "\t\tSystem.Windows.Deployment.Current.Dispatcher.BeginInvoke(", + "\t\t\t() =>", + "\t\t\t{", + "\t\t\t\tvar pc = PropertyChanging;", + "\t\t\t\tif (pc != null)", + "\t\t\t\t\tpc(this, arg);", + "\t\t\t});", + "#else", + "\tpropertyChanging(this, arg);", + "#endif", + "}", +}; + +partial class Property +{ + public bool IsNotifying; + public List<string> Dependents = new List<string>(); +} + +class NotifyingProperty : Property +{ + public NotifyingProperty() + { + IsNotifying = true; + } + + public NotifyingProperty(string type, string name, params string[] dependents) + : base(type, name, null, null) + { + IsNotifying = true; + + if (dependents.Length == 0) + Dependents.Add(name); + else + Dependents.AddRange(dependents); + } +} +#> diff --git a/ComicRackWebViewer/LinqToDB.Templates/PluralizationService.ttinclude b/ComicRackWebViewer/LinqToDB.Templates/PluralizationService.ttinclude new file mode 100644 index 0000000..fd1bb4e --- /dev/null +++ b/ComicRackWebViewer/LinqToDB.Templates/PluralizationService.ttinclude @@ -0,0 +1,121 @@ +<#@ assembly name="System.Data.Entity.Design" #> +<#@ import namespace="System.Data.Entity.Design.PluralizationServices" #> +<# + { + ToPlural = Pluralization.ToPlural; + ToSingular = Pluralization.ToSingular; + } +#> +<#+ +static class Pluralization +{ + public static string CultureInfo = "en"; + + static PluralizationService _service; + + public static Dictionary<string,string> Dictionary = new Dictionary<string,string> + { + { "access", "accesses" }, { "afterlife", "afterlives" }, { "alga", "algae" }, + { "alumna", "alumnae" }, { "alumnus", "alumni" }, { "analysis", "analyses" }, + { "antenna", "antennae" }, { "appendix", "appendices" }, { "axis", "axes" }, + { "bacillus", "bacilli" }, { "basis", "bases" }, { "Bedouin", "Bedouin" }, + { "cactus", "cacti" }, { "calf", "calves" }, { "cherub", "cherubim" }, + { "child", "children" }, { "cod", "cod" }, { "cookie", "cookies" }, + { "criterion", "criteria" }, { "curriculum", "curricula" }, { "data", "data" }, + { "deer", "deer" }, { "diagnosis", "diagnoses" }, { "die", "dice" }, + { "dormouse", "dormice" }, { "elf", "elves" }, { "elk", "elk" }, + { "erratum", "errata" }, { "esophagus", "esophagi" }, { "fauna", "faunae" }, + { "fish", "fish" }, { "flora", "florae" }, { "focus", "foci" }, + { "foot", "feet" }, { "formula", "formulae" }, { "fundus", "fundi" }, + { "fungus", "fungi" }, { "genie", "genii" }, { "genus", "genera" }, + { "goose", "geese" }, { "grouse", "grouse" }, { "hake", "hake" }, + { "half", "halves" }, { "headquarters", "headquarters" }, { "hippo", "hippos" }, + { "hippopotamus", "hippopotami" }, { "hoof", "hooves" }, { "housewife", "housewives" }, + { "hypothesis", "hypotheses" }, { "index", "indices" }, { "jackknife", "jackknives" }, + { "knife", "knives" }, { "labium", "labia" }, { "larva", "larvae" }, + { "leaf", "leaves" }, { "life", "lives" }, { "loaf", "loaves" }, + { "louse", "lice" }, { "magus", "magi" }, { "man", "men" }, + { "memorandum", "memoranda" }, { "midwife", "midwives" }, { "millennium", "millennia" }, + { "moose", "moose" }, { "mouse", "mice" }, { "nebula", "nebulae" }, + { "neurosis", "neuroses" }, { "nova", "novas" }, { "nucleus", "nuclei" }, + { "oesophagus", "oesophagi" }, { "offspring", "offspring" }, { "ovum", "ova" }, + { "ox", "oxen" }, { "papyrus", "papyri" }, { "passerby", "passersby" }, + { "penknife", "penknives" }, { "person", "people" }, { "phenomenon", "phenomena" }, + { "placenta", "placentae" }, { "pocketknife", "pocketknives" }, { "process", "processes" }, + { "pupa", "pupae" }, { "radius", "radii" }, { "reindeer", "reindeer" }, + { "retina", "retinae" }, { "rhinoceros", "rhinoceros" }, { "roe", "roe" }, + { "salmon", "salmon" }, { "scarf", "scarves" }, { "self", "selves" }, + { "seraph", "seraphim" }, { "series", "series" }, { "sheaf", "sheaves" }, + { "sheep", "sheep" }, { "shelf", "shelves" }, { "species", "species" }, + { "spectrum", "spectra" }, { "status", "status" }, { "stimulus", "stimuli" }, + { "stratum", "strata" }, { "supernova", "supernovas" }, { "swine", "swine" }, + { "terminus", "termini" }, { "thesaurus", "thesauri" }, { "thesis", "theses" }, + { "thief", "thieves" }, { "trout", "trout" }, { "vulva", "vulvae" }, + { "wife", "wives" }, { "wildebeest", "wildebeest" }, { "wolf", "wolves" }, + { "woman", "women" }, { "yen", "yen" }, + }; + + static string GetLastWord(string str) + { + if (string.IsNullOrWhiteSpace(str)) + return string.Empty; + + var i = str.Length - 1; + var isLower = char.IsLower(str[i]); + + while (i > 0 && char.IsLower(str[i-1]) == isLower) + i--; + + return str.Substring(isLower && i > 0 ? i - 1 : i); + } + + public static string ToPlural(string str) + { + if (_service == null) + _service = PluralizationService.CreateService(System.Globalization.CultureInfo.GetCultureInfo(CultureInfo)); + + var word = GetLastWord(str); + + string newWord; + + if (!Dictionary.TryGetValue(word.ToLower(), out newWord)) + newWord = _service.IsPlural(word) ? word : _service.Pluralize(word); + + if (string.Compare(word, newWord, true) != 0) + { + if (char.IsUpper(word[0])) + newWord = char.ToUpper(newWord[0]) + newWord.Substring(1, newWord.Length - 1); + + return word == str ? newWord : str.Substring(0, str.Length - word.Length) + newWord; + } + + return str; + } + + public static string ToSingular(string str) + { + if (_service == null) + _service = PluralizationService.CreateService(System.Globalization.CultureInfo.GetCultureInfo(CultureInfo)); + + var word = GetLastWord(str); + + var newWord = + Dictionary + .Where(dic => string.Compare(dic.Value, word, true) == 0) + .Select(dic => dic.Key) + .FirstOrDefault() + ?? + (_service.IsSingular(word) ? word : _service.Singularize(word)); + + if (string.Compare(word, newWord, true) != 0) + { + if (char.IsUpper(word[0])) + newWord = char.ToUpper(newWord[0]) + newWord.Substring(1, newWord.Length - 1); + + return word == str ? newWord : str.Substring(0, str.Length - word.Length) + newWord; + } + + return str; + } +} +#> diff --git a/ComicRackWebViewer/LinqToDB.Templates/T4Model.ttinclude b/ComicRackWebViewer/LinqToDB.Templates/T4Model.ttinclude new file mode 100644 index 0000000..a634431 --- /dev/null +++ b/ComicRackWebViewer/LinqToDB.Templates/T4Model.ttinclude @@ -0,0 +1,1361 @@ +<#@ assembly name="System.Core" #> +<#@ import namespace="System" #> +<#@ import namespace="System.Collections.Generic" #> +<#@ import namespace="System.Linq" #> +<#+ +static Action<GeneratedTextTransformation,string> WriteComment = (tt,s) => tt.WriteLine("//{0}", s); + +Action BeforeGenerateModel = () => {}; + +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 virtual void Render(GeneratedTextTransformation tt) + { + if (!string.IsNullOrEmpty(Name)) + { + WriteBeginNamespace(tt, Name); + tt.PushIndent("\t"); + } + + 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 abstract void Render(GeneratedTextTransformation tt); + + 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) + { + 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) tt.Write(", "); + 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; + + 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) tt.Write(", "); + 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(""); + } + else if (cm is TypeBase) + { + var t = (TypeBase)cm; + + t.Render(tt); + tt.WriteLine(""); + } + } + + tt.Trim(); + + tt.PopIndent(); + WriteEndClass(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 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 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 override int CalcBodyLen() { return 0; } + + public override void Render(GeneratedTextTransformation tt, bool isCompact) + { + if (!string.IsNullOrEmpty(Region)) + { + BeginRegion(tt, Region); + 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) + { + 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) + { + tt.Write(", "); + 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(""); + } + } + } + else + { + foreach (var cm in Members) + { + if (cm is MemberBase) + { + var m = (MemberBase)cm; + + 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(""); + } + else if (cm is TypeBase) + { + var t = (TypeBase)cm; + + t.Render(tt); + tt.WriteLine(""); + } + } + } + + tt.Trim(); + + 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" : ""; + + 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 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("("); + tt.Write(string.Join(", ", a.Parameters.ToArray())); + tt.Write(")"); + } +}; + +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; +} + +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); +} +#> diff --git a/ComicRackWebViewer/LinqToDB.Templates/Validation.ttinclude b/ComicRackWebViewer/LinqToDB.Templates/Validation.ttinclude new file mode 100644 index 0000000..b3edf40 --- /dev/null +++ b/ComicRackWebViewer/LinqToDB.Templates/Validation.ttinclude @@ -0,0 +1,175 @@ +<# + { + var beforeGenerateModel = BeforeGenerateModel; + BeforeGenerateModel = () => + { + beforeGenerateModel(); + ValidationImpl(); + }; + } +#> +<#+ +void ValidationImpl() +{ + foreach (Class cl in GetTreeNodes(Model).OfType<Class>()) + { + var validationGroup = new MemberGroup + { + Region = "Validation", + }; + + var props = GetTreeNodes(cl).OfType<Property>().Where(p => p.CustomValidation).ToList(); + + if (props.Count > 0) + { + if (!Model.Usings.Contains("System.Collections.Generic")) + Model.Usings.Add("System.Collections.Generic"); + + var isValid = new Method("bool", "IsValid", new[] { cl.Name + " obj" }) { IsStatic = true }; + var validator = new Class("CustomValidator", isValid) { IsStatic = true, }; + var partialGroup = new MemberGroup { IsCompact = true }; + + validationGroup.Members.Add(new Field("int", "_isValidCounter") { Attributes = { new Attribute("field : NonSerialized") { Conditional = "!SILVERLIGHT" } } }); + validationGroup.Members.Add(validator); + validationGroup.Members.Add(partialGroup); + + isValid.Body.Add("try"); + isValid.Body.Add("{"); + isValid.Body.Add("\tobj._isValidCounter++;"); + isValid.Body.Add(""); + + var ret = "\treturn "; + + for (var i = 0; i < props.Count; i++) + { + var p = props[i]; + + var name = p.Name.Trim(); + var mname = "Validate" + name; + + cl.Attributes.Add( + new Attribute("CustomValidation", + "typeof(" + cl.Name + ".CustomValidator)", + "\"" + mname + "\"") + { + IsSeparated = true + }); + + isValid.Body.Add( + "\tvar flag" + i + " = ValidationResult.Success == " + mname + "(obj, obj." + name + ");"); + + ret += (i == 0 ? "" : " || ") + "flag" + i; + + var validate = new Method("ValidationResult", mname, + new[] { cl.Name + " obj", p.Type.Trim() + " value" }) { IsStatic = true }; + + validate.Body.Add("var list = new List<ValidationResult>();"); + validate.Body.Add(""); + validate.Body.Add("Validator.TryValidateProperty("); + validate.Body.Add("\tvalue,"); + validate.Body.Add("\tnew ValidationContext(obj, null, null) { MemberName = NameOf" + name + " }, list);"); + validate.Body.Add(""); + validate.Body.Add("obj." + mname + "(value, list);"); + validate.Body.Add(""); + validate.Body.Add("if (list.Count > 0)"); + validate.Body.Add("{"); + validate.Body.Add("\tforeach (var result in list)"); + validate.Body.Add("\t\tforeach (var name in result.MemberNames)"); + validate.Body.Add("\t\t\tobj.AddError(name, result.ErrorMessage);"); + validate.Body.Add(""); + validate.Body.Add("\treturn list[0];"); + validate.Body.Add("}"); + validate.Body.Add(""); + validate.Body.Add("obj.RemoveError(NameOf" + name + ");"); + validate.Body.Add(""); + validate.Body.Add("return ValidationResult.Success;"); + + validator.Members.Add(validate); + + partialGroup.Members.Add(new Method( + "void", + mname, + new[] + { + p.Type.Trim() + " value", + "List<ValidationResult> validationResults", + }) + { + AccessModifier = AccessModifier.Partial, + }); + } + + isValid.Body.Add(""); + isValid.Body.Add(ret + ";"); + isValid.Body.Add("}"); + isValid.Body.Add("finally"); + isValid.Body.Add("{"); + isValid.Body.Add("\tobj._isValidCounter--;"); + isValid.Body.Add("}"); + } + + props = GetTreeNodes(cl).OfType<Property>().Where(p => p.ValidateProperty && p.HasSetter).ToList(); + + if (props.Count > 0) + { + foreach (var p in props) + { + if (p.SetBody.Count > 0) + p.SetBody.Insert(0, ""); + + p.SetBody.Insert(0, "if (_validationLockCounter == 0)"); + p.SetBody.Insert(1, "{"); + + if (p.CustomValidation) + { + p.SetBody.Insert(2, "\tvar validationResult = CustomValidator.Validate" + p.Name.Trim() + "(this, value);"); + p.SetBody.Insert(3, "\tif (validationResult != ValidationResult.Success)"); + p.SetBody.Insert(4, "\t\tthrow new ValidationException(validationResult, null, null);"); + p.SetBody.Insert(5, "}"); + } + else + { + p.SetBody.Insert(2, "\tValidator.ValidateProperty("); + p.SetBody.Insert(3, "\t\tvalue,"); + p.SetBody.Insert(4, string.Format("\t\tnew ValidationContext(this, null, null) {{ MemberName = NameOf{0} }});", p.Name.Trim())); + p.SetBody.Insert(5, "}"); + } + } + + validationGroup.Members.Add(new Field("int", "_validationLockCounter") + { + AccessModifier = AccessModifier.Private, + InitValue = "0", + Attributes = { new Attribute("field : NonSerialized") { Conditional = "!SILVERLIGHT" } } + }); + + validationGroup.Members.Add(new Method ("void", "LockValidation", null, new[] { "_validationLockCounter++;" })); + validationGroup.Members.Add(new Method ("void", "UnlockValidation", null, new[] { "_validationLockCounter--;" })); + } + + if (validationGroup.Members.Count > 0) + { + if (!Model.Usings.Contains("System.ComponentModel.DataAnnotations")) + Model.Usings.Add("System.ComponentModel.DataAnnotations"); + + cl.Members.Add(validationGroup); + cl.SetTree(); + } + } +} + +partial class Property +{ + public bool CustomValidation; + public bool ValidateProperty; + + public bool Validate + { + set + { + CustomValidation = value; + ValidateProperty = value; + } + } +} +#> diff --git a/ComicRackWebViewer/Modules/BCRModule.cs b/ComicRackWebViewer/Modules/BCRModule.cs index 8a341c7..c872021 100644 --- a/ComicRackWebViewer/Modules/BCRModule.cs +++ b/ComicRackWebViewer/Modules/BCRModule.cs @@ -308,7 +308,7 @@ namespace BCR try { var user = (BCRUser)this.Context.CurrentUser; - user.UpdateSettings(this.Bind<UserSettings>()); + user.UpdateSettings(this.Bind<user_settings>()); return HttpStatusCode.OK; } diff --git a/ComicRackWebViewer/packages.config b/ComicRackWebViewer/packages.config index 6f31d74..de0f962 100644 --- a/ComicRackWebViewer/packages.config +++ b/ComicRackWebViewer/packages.config @@ -1,5 +1,8 @@ <?xml version="1.0" encoding="utf-8"?>
<packages>
+ <package id="linq2db" version="1.0.1.1" targetFramework="net40" />
+ <package id="linq2db.SQLite" version="1.0.0" targetFramework="net40" />
+ <package id="linq2db.t4models" version="1.0.2" targetFramework="net40" />
<package id="Linq2Rest" version="3.3.2.0" targetFramework="net40" />
<package id="Nancy" version="0.21.1" targetFramework="net40" />
<package id="Nancy.Authentication.Stateless" version="0.21.1" targetFramework="net40" />
diff --git a/ComicRackWebViewer/tablet/app/controller/ComicSettings.js b/ComicRackWebViewer/tablet/app/controller/ComicSettings.js index 29a4d6f..0ed7784 100644 --- a/ComicRackWebViewer/tablet/app/controller/ComicSettings.js +++ b/ComicRackWebViewer/tablet/app/controller/ComicSettings.js @@ -87,15 +87,15 @@ Ext.define('Comic.controller.ComicSettings', { pageChangeAreaWidth = me.getPageChangeAreaWidth(); comicsettingsview.setValues({ - open_current_comic_at_launch: Comic.settings.open_current_comic_at_launch, - open_next_comic: Comic.settings.open_next_comic, + open_current_comic_at_launch: (Comic.settings.open_current_comic_at_launch > 0), + open_next_comic: (Comic.settings.open_next_comic > 0), zoom_on_tap: Comic.settings.zoom_on_tap, page_fit_mode: Comic.settings.page_fit_mode, toggle_paging_bar: Comic.settings.toggle_paging_bar, - use_page_turn_drag: (Comic.settings.page_turn_drag_threshold < 1000), - page_turn_drag_threshold: (Comic.settings.page_turn_drag_threshold < 1000) ? Comic.settings.page_turn_drag_threshold : 50, - use_page_change_area: (Comic.settings.page_change_area_width > 0), - page_change_area_width: (Comic.settings.page_change_area_width > 0) ? Comic.settings.page_change_area_width : 75 + use_page_turn_drag: (Comic.settings.use_page_turn_drag > 0), + page_turn_drag_threshold: Comic.settings.page_turn_drag_threshold, + use_page_change_area: (Comic.use_page_change_area > 0), + page_change_area_width: Comic.settings.page_change_area_width }); @@ -125,13 +125,16 @@ Ext.define('Comic.controller.ComicSettings', { values = comicsettingsview.getValues(), comicview = me.getComicview(); - Comic.settings.open_current_comic_at_launch = values.open_current_comic_at_launch ? true : false; - Comic.settings.open_next_comic = values.open_next_comic ? true : false;; + Comic.settings.open_current_comic_at_launch = values.open_current_comic_at_launch ? 1 : 0; + Comic.settings.open_next_comic = values.open_next_comic ? 1 : 0; Comic.settings.zoom_on_tap = values.zoom_on_tap; Comic.settings.page_fit_mode = values.page_fit_mode; Comic.settings.toggle_paging_bar = values.toggle_paging_bar; - Comic.settings.page_turn_drag_threshold = values.use_page_turn_drag ? values.page_turn_drag_threshold : 1000; - Comic.settings.page_change_area_width = values.use_page_change_area ? values.page_change_area_width : 0; + Comic.settings.page_turn_drag_threshold = values.page_turn_drag_threshold; + Comic.settings.page_change_area_width = values.page_change_area_width; + Comic.settings.use_page_change_area = values.use_page_change_area ? 1 : 0; + Comic.settings.use_page_turn_drag = values.use_page_turn_drag ? 1 : 0; + Comic.RemoteApi.SetSettings(Comic.settings, function(provider, response) { |