summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--ComicRackWebViewer/BCRDatabase.cs14
-rw-r--r--ComicRackWebViewer/BCREntities.cs24
-rw-r--r--ComicRackWebViewer/BCRUser.cs88
-rw-r--r--ComicRackWebViewer/Bcr.s3dbbin0 -> 17408 bytes
-rw-r--r--ComicRackWebViewer/BcrDB.tt10
-rw-r--r--ComicRackWebViewer/ComicRackWebViewer.csproj37
-rw-r--r--ComicRackWebViewer/LinqToDB.Templates/DataAnnotations.ttinclude65
-rw-r--r--ComicRackWebViewer/LinqToDB.Templates/DataModel.ttinclude502
-rw-r--r--ComicRackWebViewer/LinqToDB.Templates/EditableObject.ttinclude235
-rw-r--r--ComicRackWebViewer/LinqToDB.Templates/LinqToDB.Access.ttinclude26
-rw-r--r--ComicRackWebViewer/LinqToDB.Templates/LinqToDB.DB2.ttinclude28
-rw-r--r--ComicRackWebViewer/LinqToDB.Templates/LinqToDB.Firebird.ttinclude41
-rw-r--r--ComicRackWebViewer/LinqToDB.Templates/LinqToDB.Informix.ttinclude28
-rw-r--r--ComicRackWebViewer/LinqToDB.Templates/LinqToDB.MySql.ttinclude30
-rw-r--r--ComicRackWebViewer/LinqToDB.Templates/LinqToDB.Oracle.ttinclude27
-rw-r--r--ComicRackWebViewer/LinqToDB.Templates/LinqToDB.PostgreSQL.ttinclude57
-rw-r--r--ComicRackWebViewer/LinqToDB.Templates/LinqToDB.SQLite.Tools.ttinclude3
-rw-r--r--ComicRackWebViewer/LinqToDB.Templates/LinqToDB.SQLite.ttinclude44
-rw-r--r--ComicRackWebViewer/LinqToDB.Templates/LinqToDB.SqlCe.ttinclude28
-rw-r--r--ComicRackWebViewer/LinqToDB.Templates/LinqToDB.SqlServer.ttinclude107
-rw-r--r--ComicRackWebViewer/LinqToDB.Templates/LinqToDB.Sybase.ttinclude82
-rw-r--r--ComicRackWebViewer/LinqToDB.Templates/LinqToDB.Tools.ttinclude1
-rw-r--r--ComicRackWebViewer/LinqToDB.Templates/LinqToDB.ttinclude477
-rw-r--r--ComicRackWebViewer/LinqToDB.Templates/NotifyPropertyChanged.ttinclude379
-rw-r--r--ComicRackWebViewer/LinqToDB.Templates/PluralizationService.ttinclude121
-rw-r--r--ComicRackWebViewer/LinqToDB.Templates/T4Model.ttinclude1361
-rw-r--r--ComicRackWebViewer/LinqToDB.Templates/Validation.ttinclude175
-rw-r--r--ComicRackWebViewer/Modules/BCRModule.cs2
-rw-r--r--ComicRackWebViewer/packages.config3
-rw-r--r--ComicRackWebViewer/tablet/app/controller/ComicSettings.js23
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
new file mode 100644
index 0000000..1845ffd
--- /dev/null
+++ b/ComicRackWebViewer/Bcr.s3db
Binary files differ
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)
{