Core.Cache implemented; nearly full implementation of the main page (/boards/); some critical bugs fixed

main
Inga 🏳‍🌈 15 years ago
parent 9b3cb8d297
commit b6aa5d8a3b
  1. 2
      Builder/IISMainHandler/build.txt
  2. 43
      Common/SqlObject.cs
  3. 86
      Common/dataobjects/Board.cs
  4. 88
      Common/dataobjects/Category.cs
  5. 37
      Core/Cache.cs
  6. 3
      Core/Core.csproj
  7. 14
      Core/DB/Diapasone.cs
  8. 4
      Core/DB/ITableSpec.cs
  9. 6
      Core/DB/conditions/ComplexCondition.cs
  10. 2
      Core/DB/conditions/SimpleCondition.cs
  11. 2
      Core/DataObject.cs
  12. 8
      Core/extensions/Extensions.cs
  13. 16
      IISMainHandler/handlers/BoardsHandler.cs
  14. 18
      MySQLConnector/ConditionCompiler.cs
  15. 21
      MySQLConnector/Connection.cs

@ -22,7 +22,11 @@ namespace FLocal.Common {
abstract protected void doFromHash(Dictionary<string, string> data); abstract protected void doFromHash(Dictionary<string, string> data);
protected void fromHash(Dictionary<string, string> data) { /// <summary>
/// Note that this method does not updates isLoaded field!
/// </summary>
/// <param name="data"></param>
private void fromHash(Dictionary<string, string> data) {
lock(this.lockFiller) { lock(this.lockFiller) {
if(data[this.table.idName] != this.id.ToString()) { if(data[this.table.idName] != this.id.ToString()) {
throw new CriticalException("Id mismatch"); throw new CriticalException("Id mismatch");
@ -31,6 +35,10 @@ namespace FLocal.Common {
} }
} }
/// <summary>
/// Note that this method does not updates isLoaded field!
/// </summary>
/// <param name="data"></param>
private void doLoad() { private void doLoad() {
this.fromHash(Config.instance.mainConnection.LoadById(this.table, this.id.ToString())); this.fromHash(Config.instance.mainConnection.LoadById(this.table, this.id.ToString()));
} }
@ -43,17 +51,30 @@ namespace FLocal.Common {
} }
} }
protected void LoadIfNotLoaded() { protected void LoadFromHash(Dictionary<string, string> data) {
lock(this.lockInitializer) { lock(this.lockInitializer) {
if(!this.isLoaded) { if(this.isLoaded) throw new CriticalException("already initialized");
this.doLoad(); this.fromHash(data);
this.isLoaded = true; this.isLoaded = true;
}
}
protected void LoadIfNotLoaded() {
if(!this.isLoaded) {
lock(this.lockInitializer) {
if(!this.isLoaded) {
this.doLoad();
this.isLoaded = true;
}
} }
} }
} }
public void ReLoad() { public void ReLoad() {
this.doLoad(); lock(this.lockInitializer) {
this.doLoad();
this.isLoaded = true;
}
} }
protected override void AfterCreate(bool forLoadingFromHash) { protected override void AfterCreate(bool forLoadingFromHash) {
@ -61,7 +82,7 @@ namespace FLocal.Common {
if(!forLoadingFromHash) this.Load(); if(!forLoadingFromHash) this.Load();
} }
public static List<T> LoadByIds(List<int> ids) { public static List<T> LoadByIds(IEnumerable<int> ids) {
Dictionary<int, T> rawRes = LoadByIdsForLoadingFromHash(ids); Dictionary<int, T> rawRes = LoadByIdsForLoadingFromHash(ids);
@ -72,19 +93,23 @@ namespace FLocal.Common {
} }
} }
List<int> loadedIds = new List<int>();
if(idsToQuery.Count > 0) { if(idsToQuery.Count > 0) {
ITableSpec table = rawRes[idsToQuery[0]].table; ITableSpec table = rawRes[idsToQuery[0]].table;
List<Dictionary<string, string>> rawData = Config.instance.mainConnection.LoadByIds(table, new List<string>(from int id in idsToQuery select id.ToString())); List<Dictionary<string, string>> rawData = Config.instance.mainConnection.LoadByIds(table, new List<string>(from int id in idsToQuery select id.ToString()));
foreach(Dictionary<string, string> row in rawData) { foreach(Dictionary<string, string> row in rawData) {
int id = int.Parse(row[table.idName]); int id = int.Parse(row[table.idName]);
loadedIds.Add(id);
if(!rawRes.ContainsKey(id)) throw new CriticalException("wrong id"); if(!rawRes.ContainsKey(id)) throw new CriticalException("wrong id");
rawRes[id].fromHash(row); rawRes[id].LoadFromHash(row);
} }
} }
List<T> res = new List<T>(); List<T> res = new List<T>();
foreach(int id in ids) { foreach(int id in ids) {
if(!rawRes[id].isLoaded) throw new CriticalException("not loaded"); if(!rawRes[id].isLoaded) {
throw new CriticalException("#" + id + " not loaded (all ids (" + ids.ToPrintableString() + "), idsToQuery (" + idsToQuery.ToPrintableString() + "), loaded ids (" + loadedIds.ToPrintableString() + ")");
}
res.Add(rawRes[id]); res.Add(rawRes[id]);
} }

@ -7,64 +7,106 @@ using System.Xml.Linq;
namespace FLocal.Common.dataobjects { namespace FLocal.Common.dataobjects {
public class Board : SqlObject<Board> { public class Board : SqlObject<Board> {
private class TableSpec : FLocal.Core.DB.ITableSpec { public class TableSpec : FLocal.Core.DB.ITableSpec {
public const string TABLE = "Boards";
public const string FIELD_ID = "Id";
public const string FIELD_SORTORDER = "SortOrder";
public const string FIELD_CATEGORYID = "CategoryId";
public const string FIELD_LASTPOSTID = "LastPostId";
public const string FIELD_TOTALPOSTS = "TotalPosts";
public const string FIELD_TOTALTHREADS = "TotalThreads";
public const string FIELD_NAME = "Name";
public const string FIELD_DESCRIPTION = "Comment";
public static readonly TableSpec instance = new TableSpec(); public static readonly TableSpec instance = new TableSpec();
public string name { get { return "Boards"; } } public string name { get { return TABLE; } }
public string idName { get { return "Id"; } } public string idName { get { return FIELD_ID; } }
} }
protected override FLocal.Core.DB.ITableSpec table { get { return TableSpec.instance; } } protected override FLocal.Core.DB.ITableSpec table { get { return TableSpec.instance; } }
private string _name; private int _sortOrder;
public string name { public int sortOrder {
get { get {
this.LoadIfNotLoaded(); this.LoadIfNotLoaded();
return this._name; return this._sortOrder;
} }
} }
private string _description; private int _categoryId;
public string description { public int categoryId {
get { get {
this.LoadIfNotLoaded(); this.LoadIfNotLoaded();
return this._description; return this._categoryId;
}
}
public Category category {
get {
return Category.LoadById(this.categoryId);
} }
} }
private int? _lastPostId; private int? _lastPostId;
public int lastPostId { public int? lastPostId {
get { get {
this.LoadIfNotLoaded(); this.LoadIfNotLoaded();
return this._lastPostId.Value; return this._lastPostId;
} }
} }
private int _categoryId; private int _totalPosts;
public int categoryId { public int totalPosts {
get { get {
this.LoadIfNotLoaded(); this.LoadIfNotLoaded();
return this._categoryId; return this._totalPosts;
} }
} }
public Category category {
private int _totalThreads;
public int totalThreads {
get { get {
return Category.LoadById(this.categoryId); this.LoadIfNotLoaded();
return this._totalThreads;
}
}
private string _name;
public string name {
get {
this.LoadIfNotLoaded();
return this._name;
}
}
private string _description;
public string description {
get {
this.LoadIfNotLoaded();
return this._description;
} }
} }
protected override void doFromHash(Dictionary<string, string> data) { protected override void doFromHash(Dictionary<string, string> data) {
this._name = data["Name"]; this._sortOrder = int.Parse(data[TableSpec.FIELD_SORTORDER]);
this._description = data["Comment"]; this._categoryId = int.Parse(data[TableSpec.FIELD_CATEGORYID]);
if(data["LastPostId"] != "") { if(data[TableSpec.FIELD_LASTPOSTID] != "") {
this._lastPostId = int.Parse(data["LastPostId"]); this._lastPostId = int.Parse(data[TableSpec.FIELD_LASTPOSTID]);
} else { } else {
this._lastPostId = null; this._lastPostId = null;
} }
this._categoryId = int.Parse(data["CategoryId"]); this._totalPosts = int.Parse(data[TableSpec.FIELD_TOTALPOSTS]);
this._totalThreads = int.Parse(data[TableSpec.FIELD_TOTALTHREADS]);
this._name = data[TableSpec.FIELD_NAME];
this._description = data[TableSpec.FIELD_DESCRIPTION];
} }
public XElement exportToXml() { public XElement exportToXmlForMainPage() {
return new XElement("board", return new XElement("board",
new XElement("id", this.id),
new XElement("sortOrder", this.sortOrder),
new XElement("categoryId", this.categoryId),
new XElement("lastPostId", this.lastPostId),
new XElement("totalPosts", this.totalPosts),
new XElement("totalThreads", this.totalThreads),
new XElement("name", this.name), new XElement("name", this.name),
new XElement("description", this.description) new XElement("description", this.description)
); );

@ -2,17 +2,25 @@
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Text; using System.Text;
using FLocal.Core;
using FLocal.Core.DB;
using System.Xml.Linq;
namespace FLocal.Common.dataobjects { namespace FLocal.Common.dataobjects {
public class Category : SqlObject<Category> { public class Category : SqlObject<Category> {
private class TableSpec : FLocal.Core.DB.ITableSpec { public class TableSpec : ITableSpec {
public const string TABLE = "Categories";
public const string FIELD_ID = "Id";
public const string FIELD_SORTORDER = "SortOrder";
public const string FIELD_NAME = "Name";
public static readonly TableSpec instance = new TableSpec(); public static readonly TableSpec instance = new TableSpec();
public string name { get { return "Categories"; } } public string name { get { return TABLE; } }
public string idName { get { return "Id"; } } public string idName { get { return FIELD_ID; } }
} }
protected override FLocal.Core.DB.ITableSpec table { get { return TableSpec.instance; } } protected override ITableSpec table { get { return TableSpec.instance; } }
private string _name; private string _name;
public string name { public string name {
@ -22,8 +30,78 @@ namespace FLocal.Common.dataobjects {
} }
} }
private int _sortOrder;
public int sortOrder {
get {
this.LoadIfNotLoaded();
return this._sortOrder;
}
}
protected override void doFromHash(Dictionary<string, string> data) { protected override void doFromHash(Dictionary<string, string> data) {
this._name = data["Name"]; this._name = data[TableSpec.FIELD_NAME];
this._sortOrder = int.Parse(data[TableSpec.FIELD_SORTORDER]);
}
private static readonly object allCategories_Locker = new object();
public static IEnumerable<Category> allCategories {
get {
return
from id in Cache<IEnumerable<int>>.instance.get(
allCategories_Locker,
() => {
IEnumerable<int> ids = from stringId in Config.instance.mainConnection.LoadIdsByConditions(
TableSpec.instance,
new FLocal.Core.DB.conditions.EmptyCondition(),
Diapasone.unlimited,
new JoinSpec[0]
) select int.Parse(stringId);
Category.LoadByIds(ids);
return ids;
}
)
let category = Category.LoadById(id)
orderby category.sortOrder, category.id
select category;
}
}
internal static void allCategories_Reset() {
Cache<IEnumerable<int>>.instance.delete(allCategories_Locker);
}
private readonly object subBoards_Locker = new object();
public IEnumerable<Board> subBoards {
get {
return
from id in Cache<IEnumerable<int>>.instance.get(
this.subBoards_Locker,
() => {
IEnumerable<int> ids = from stringId in Config.instance.mainConnection.LoadIdsByConditions(
Board.TableSpec.instance,
new FLocal.Core.DB.conditions.ComparisonCondition(
Board.TableSpec.instance.getColumnSpec(Board.TableSpec.FIELD_CATEGORYID),
FLocal.Core.DB.conditions.ComparisonType.EQUAL,
this.id.ToString()
),
Diapasone.unlimited,
new JoinSpec[0]
) select int.Parse(stringId);
Board.LoadByIds(ids);
return ids;
}
)
let board = Board.LoadById(id)
orderby board.sortOrder, board.id
select board;
}
}
public XElement exportToXmlForMainPage() {
return new XElement("category",
new XElement("name", this.name),
new XElement("sortOrder", this.sortOrder),
new XElement("boards", from board in this.subBoards select board.exportToXmlForMainPage())
);
} }
} }

@ -0,0 +1,37 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace FLocal.Core {
public class Cache<T> {
public static readonly Cache<T> instance = new Cache<T>();
private Dictionary<object, T> cache;
private Cache() {
this.cache = new Dictionary<object,T>();
}
public T get(object id, Func<T> getter) {
if(!this.cache.ContainsKey(id)) {
lock(id) {
if(!this.cache.ContainsKey(id)) {
this.cache[id] = getter();
}
}
}
return this.cache[id];
}
public void delete(object id) {
lock(id) {
if(this.cache.ContainsKey(id)) {
this.cache.Remove(id);
}
}
}
}
}

@ -45,6 +45,7 @@
<Reference Include="System.Xml" /> <Reference Include="System.Xml" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<Compile Include="Cache.cs" />
<Compile Include="Config.cs" /> <Compile Include="Config.cs" />
<Compile Include="DataObject.cs" /> <Compile Include="DataObject.cs" />
<Compile Include="DB\conditions\AbstractCondition.cs" /> <Compile Include="DB\conditions\AbstractCondition.cs" />
@ -54,8 +55,10 @@
<Compile Include="DB\conditions\ComparisonType.cs" /> <Compile Include="DB\conditions\ComparisonType.cs" />
<Compile Include="DB\conditions\ComplexCondition.cs" /> <Compile Include="DB\conditions\ComplexCondition.cs" />
<Compile Include="DB\conditions\ConditionsJoinType.cs" /> <Compile Include="DB\conditions\ConditionsJoinType.cs" />
<Compile Include="DB\conditions\EmptyCondition.cs" />
<Compile Include="DB\conditions\IsNullCondition.cs" /> <Compile Include="DB\conditions\IsNullCondition.cs" />
<Compile Include="DB\conditions\MultiValueCondition.cs" /> <Compile Include="DB\conditions\MultiValueCondition.cs" />
<Compile Include="DB\conditions\NotEmptyCondition.cs" />
<Compile Include="DB\conditions\NotIsNullCondition.cs" /> <Compile Include="DB\conditions\NotIsNullCondition.cs" />
<Compile Include="DB\conditions\SimpleCondition.cs" /> <Compile Include="DB\conditions\SimpleCondition.cs" />
<Compile Include="DB\Diapasone.cs" /> <Compile Include="DB\Diapasone.cs" />

@ -6,17 +6,23 @@ using System.Text;
namespace FLocal.Core.DB { namespace FLocal.Core.DB {
public class Diapasone { public class Diapasone {
public readonly int start; public readonly long start;
public readonly int count; public readonly long count;
public int total { public long total {
get; get;
set; set;
} }
public Diapasone(int start, int count) { public Diapasone(long start, long count) {
this.start = start; this.start = start;
this.count = count; this.count = count;
} }
public static Diapasone unlimited {
get {
return new Diapasone(0, -1);
}
}
} }
} }

@ -21,6 +21,10 @@ namespace FLocal.Core.DB {
return new ColumnSpec(table, table.idName); return new ColumnSpec(table, table.idName);
} }
public static ColumnSpec getColumnSpec(this ITableSpec table, string column) {
return new ColumnSpec(table, column);
}
} }
} }

@ -4,13 +4,13 @@ using System.Linq;
using System.Text; using System.Text;
namespace FLocal.Core.DB.conditions { namespace FLocal.Core.DB.conditions {
public class ComplexCondition : AbstractCondition { public class ComplexCondition : NotEmptyCondition {
public readonly ConditionsJoinType type; public readonly ConditionsJoinType type;
public readonly List<AbstractCondition> innerConditions; public readonly List<NotEmptyCondition> innerConditions;
public ComplexCondition(ConditionsJoinType type, List<AbstractCondition> innerConditions) { public ComplexCondition(ConditionsJoinType type, List<NotEmptyCondition> innerConditions) {
this.type = type; this.type = type;
this.innerConditions = innerConditions; this.innerConditions = innerConditions;
} }

@ -4,6 +4,6 @@ using System.Linq;
using System.Text; using System.Text;
namespace FLocal.Core.DB.conditions { namespace FLocal.Core.DB.conditions {
public abstract class SimpleCondition : AbstractCondition { public abstract class SimpleCondition : NotEmptyCondition {
} }
} }

@ -39,7 +39,7 @@ namespace FLocal.Core {
return registry.Get(id, false); return registry.Get(id, false);
} }
protected static Dictionary<TKey, T> LoadByIdsForLoadingFromHash(List<TKey> ids) { protected static Dictionary<TKey, T> LoadByIdsForLoadingFromHash(IEnumerable<TKey> ids) {
Dictionary<TKey, T> res = new Dictionary<TKey,T>(); Dictionary<TKey, T> res = new Dictionary<TKey,T>();
foreach(TKey id in ids) { foreach(TKey id in ids) {
res[id] = registry.Get(id, true); res[id] = registry.Get(id, true);

@ -5,7 +5,7 @@ using System.Text;
namespace FLocal.Core { namespace FLocal.Core {
static class ExtensionMethods { public static class ExtensionMethods {
public static Dictionary<TKey, TValue> ToDictionary<TKey, TValue>(this IEnumerable<KeyValuePair<TKey, TValue>> kvps) { public static Dictionary<TKey, TValue> ToDictionary<TKey, TValue>(this IEnumerable<KeyValuePair<TKey, TValue>> kvps) {
@ -49,6 +49,10 @@ namespace FLocal.Core {
return val ? "Enabled" : "Disabled"; return val ? "Enabled" : "Disabled";
} }
public static string ToPrintableString<T>(this IEnumerable<T> list) {
return string.Join(",", (from elem in list select elem.ToString()).ToArray());
}
public static string ToXmlApiRequestString(this bool val) { public static string ToXmlApiRequestString(this bool val) {
return val ? "1" : "0"; return val ? "1" : "0";
} }
@ -202,7 +206,7 @@ namespace FLocal.Core {
return from elem in list select replacer(elem); return from elem in list select replacer(elem);
} }
public static string ToStringOnFail(this bool test, Lazy<string> onFail) { internal static string ToStringOnFail(this bool test, Lazy<string> onFail) {
return test ? "" : onFail(); return test ? "" : onFail();
} }

@ -24,21 +24,7 @@ namespace FLocal.IISHandler.handlers {
return new XDocument( return new XDocument(
new XElement("root", new XElement("root",
new XElement("title", Config.instance.AppInfo), new XElement("title", Config.instance.AppInfo),
new XElement("categories", new XElement("categories", from category in Category.allCategories select category.exportToXmlForMainPage())
new XElement("category",
new XElement("name", board1.category.name),
new XElement("boards",
board1.exportToXml(),
board2.exportToXml()
)
),
new XElement("category",
new XElement("name", board3.category.name),
new XElement("boards",
board3.exportToXml()
)
)
)
) )
); );
} }

@ -82,7 +82,7 @@ namespace FLocal.MySQLConnector {
private string CompileCondition(ComplexCondition condition) { private string CompileCondition(ComplexCondition condition) {
List<string> parts = new List<string>(); List<string> parts = new List<string>();
foreach(AbstractCondition innerCondition in condition.innerConditions) { foreach(NotEmptyCondition innerCondition in condition.innerConditions) {
parts.Add("(" + CompileCondition(innerCondition) + ")"); parts.Add("(" + CompileCondition(innerCondition) + ")");
} }
@ -96,7 +96,7 @@ namespace FLocal.MySQLConnector {
} }
} }
private string CompileCondition(AbstractCondition condition) { private string CompileCondition(NotEmptyCondition condition) {
if(condition is ComplexCondition) { if(condition is ComplexCondition) {
return CompileCondition((ComplexCondition)condition); return CompileCondition((ComplexCondition)condition);
} else if(condition is SimpleCondition) { } else if(condition is SimpleCondition) {
@ -106,6 +106,20 @@ namespace FLocal.MySQLConnector {
} }
} }
private string CompileCondition(EmptyCondition condition) {
return "";
}
private string CompileCondition(AbstractCondition condition) {
if(condition is NotEmptyCondition) {
return CompileCondition((NotEmptyCondition)condition);
} else if(condition is EmptyCondition) {
return CompileCondition((EmptyCondition)condition);
} else {
throw new NotImplementedException();
}
}
public static KeyValuePair<string, ParamsHolder> Compile(AbstractCondition condition, IDBTraits traits) { public static KeyValuePair<string, ParamsHolder> Compile(AbstractCondition condition, IDBTraits traits) {
ConditionCompiler compiler = new ConditionCompiler(traits); ConditionCompiler compiler = new ConditionCompiler(traits);
string compiled = compiler.CompileCondition(condition); string compiled = compiler.CompileCondition(condition);

@ -42,7 +42,7 @@ namespace FLocal.MySQLConnector {
placeholder.Add(this.traits.markParam(paramsHolder.Add(id))); placeholder.Add(this.traits.markParam(paramsHolder.Add(id)));
} }
command.CommandText = "SELECT * FROM " + table.compile(this.traits) + " WHERE " + table.getIdSpec().compile(this.traits) + " = " + string.Join(", ", placeholder.ToArray()) + ""; command.CommandText = "SELECT * FROM " + table.compile(this.traits) + " WHERE " + table.getIdSpec().compile(this.traits) + " IN (" + string.Join(", ", placeholder.ToArray()) + ")";
//command.Prepare(); //command.Prepare();
foreach(KeyValuePair<string, string> kvp in paramsHolder.data) { foreach(KeyValuePair<string, string> kvp in paramsHolder.data) {
command.AddParameter(kvp.Key, kvp.Value); command.AddParameter(kvp.Key, kvp.Value);
@ -78,7 +78,8 @@ namespace FLocal.MySQLConnector {
command.CommandType = System.Data.CommandType.Text; command.CommandType = System.Data.CommandType.Text;
var conditionsCompiled = ConditionCompiler.Compile(conditions, this.traits); var conditionsCompiled = ConditionCompiler.Compile(conditions, this.traits);
string queryConditions = conditionsCompiled.Key; string queryConditions = "";
if(conditionsCompiled.Key != "") queryConditions = "WHERE " + conditionsCompiled.Key;
ParamsHolder paramsHolder = conditionsCompiled.Value; ParamsHolder paramsHolder = conditionsCompiled.Value;
string queryJoins = ""; string queryJoins = "";
@ -89,13 +90,19 @@ namespace FLocal.MySQLConnector {
} }
string querySorts = ""; string querySorts = "";
{ if(sorts.Length > 0) {
if(sorts.Length > 0) { List<string> sortParts = new List<string>();
throw new NotImplementedException(); foreach(SortSpec sortSpec in sorts) {
if(sortSpec.ascending) {
sortParts.Add(sortSpec.column.compile(this.traits) + " ASC");
} else {
sortParts.Add(sortSpec.column.compile(this.traits) + " DESC");
}
} }
querySorts = "ORDER BY " + string.Join(", ", sortParts.ToArray());
} }
string queryMain = "FROM " + table.compile(this.traits) + " " + queryJoins + " WHERE " + queryConditions; string queryMain = "FROM " + table.compile(this.traits) + " " + queryJoins + " " + queryConditions;
foreach(KeyValuePair<string, string> kvp in paramsHolder.data) { foreach(KeyValuePair<string, string> kvp in paramsHolder.data) {
command.AddParameter(kvp.Key, kvp.Value); command.AddParameter(kvp.Key, kvp.Value);
@ -103,7 +110,7 @@ namespace FLocal.MySQLConnector {
command.CommandText = "SELECT COUNT(*) " + queryMain; command.CommandText = "SELECT COUNT(*) " + queryMain;
object rawCount = command.ExecuteScalar(); object rawCount = command.ExecuteScalar();
int count = (int)rawCount; long count = (long)rawCount;
if(count < 1) { if(count < 1) {
diapasone.total = 0; diapasone.total = 0;
return new List<string>(); return new List<string>();

Loading…
Cancel
Save