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. 33
      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. 19
      MySQLConnector/Connection.cs

@ -22,7 +22,11 @@ namespace FLocal.Common {
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) {
if(data[this.table.idName] != this.id.ToString()) {
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() {
this.fromHash(Config.instance.mainConnection.LoadById(this.table, this.id.ToString()));
}
@ -43,7 +51,16 @@ namespace FLocal.Common {
}
}
protected void LoadFromHash(Dictionary<string, string> data) {
lock(this.lockInitializer) {
if(this.isLoaded) throw new CriticalException("already initialized");
this.fromHash(data);
this.isLoaded = true;
}
}
protected void LoadIfNotLoaded() {
if(!this.isLoaded) {
lock(this.lockInitializer) {
if(!this.isLoaded) {
this.doLoad();
@ -51,9 +68,13 @@ namespace FLocal.Common {
}
}
}
}
public void ReLoad() {
lock(this.lockInitializer) {
this.doLoad();
this.isLoaded = true;
}
}
protected override void AfterCreate(bool forLoadingFromHash) {
@ -61,7 +82,7 @@ namespace FLocal.Common {
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);
@ -72,19 +93,23 @@ namespace FLocal.Common {
}
}
List<int> loadedIds = new List<int>();
if(idsToQuery.Count > 0) {
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()));
foreach(Dictionary<string, string> row in rawData) {
int id = int.Parse(row[table.idName]);
loadedIds.Add(id);
if(!rawRes.ContainsKey(id)) throw new CriticalException("wrong id");
rawRes[id].fromHash(row);
rawRes[id].LoadFromHash(row);
}
}
List<T> res = new List<T>();
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]);
}

@ -7,64 +7,106 @@ using System.Xml.Linq;
namespace FLocal.Common.dataobjects {
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 string name { get { return "Boards"; } }
public string idName { get { return "Id"; } }
public string name { get { return TABLE; } }
public string idName { get { return FIELD_ID; } }
}
protected override FLocal.Core.DB.ITableSpec table { get { return TableSpec.instance; } }
private string _name;
public string name {
private int _sortOrder;
public int sortOrder {
get {
this.LoadIfNotLoaded();
return this._name;
return this._sortOrder;
}
}
private string _description;
public string description {
private int _categoryId;
public int categoryId {
get {
this.LoadIfNotLoaded();
return this._description;
return this._categoryId;
}
}
public Category category {
get {
return Category.LoadById(this.categoryId);
}
}
private int? _lastPostId;
public int lastPostId {
public int? lastPostId {
get {
this.LoadIfNotLoaded();
return this._lastPostId.Value;
return this._lastPostId;
}
}
private int _categoryId;
public int categoryId {
private int _totalPosts;
public int totalPosts {
get {
this.LoadIfNotLoaded();
return this._categoryId;
return this._totalPosts;
}
}
public Category category {
private int _totalThreads;
public int totalThreads {
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) {
this._name = data["Name"];
this._description = data["Comment"];
if(data["LastPostId"] != "") {
this._lastPostId = int.Parse(data["LastPostId"]);
this._sortOrder = int.Parse(data[TableSpec.FIELD_SORTORDER]);
this._categoryId = int.Parse(data[TableSpec.FIELD_CATEGORYID]);
if(data[TableSpec.FIELD_LASTPOSTID] != "") {
this._lastPostId = int.Parse(data[TableSpec.FIELD_LASTPOSTID]);
} else {
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",
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("description", this.description)
);

@ -2,17 +2,25 @@
using System.Collections.Generic;
using System.Linq;
using System.Text;
using FLocal.Core;
using FLocal.Core.DB;
using System.Xml.Linq;
namespace FLocal.Common.dataobjects {
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 string name { get { return "Categories"; } }
public string idName { get { return "Id"; } }
public string name { get { return TABLE; } }
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;
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) {
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" />
</ItemGroup>
<ItemGroup>
<Compile Include="Cache.cs" />
<Compile Include="Config.cs" />
<Compile Include="DataObject.cs" />
<Compile Include="DB\conditions\AbstractCondition.cs" />
@ -54,8 +55,10 @@
<Compile Include="DB\conditions\ComparisonType.cs" />
<Compile Include="DB\conditions\ComplexCondition.cs" />
<Compile Include="DB\conditions\ConditionsJoinType.cs" />
<Compile Include="DB\conditions\EmptyCondition.cs" />
<Compile Include="DB\conditions\IsNullCondition.cs" />
<Compile Include="DB\conditions\MultiValueCondition.cs" />
<Compile Include="DB\conditions\NotEmptyCondition.cs" />
<Compile Include="DB\conditions\NotIsNullCondition.cs" />
<Compile Include="DB\conditions\SimpleCondition.cs" />
<Compile Include="DB\Diapasone.cs" />

@ -6,17 +6,23 @@ using System.Text;
namespace FLocal.Core.DB {
public class Diapasone {
public readonly int start;
public readonly int count;
public int total {
public readonly long start;
public readonly long count;
public long total {
get;
set;
}
public Diapasone(int start, int count) {
public Diapasone(long start, long count) {
this.start = start;
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);
}
public static ColumnSpec getColumnSpec(this ITableSpec table, string column) {
return new ColumnSpec(table, column);
}
}
}

@ -4,13 +4,13 @@ using System.Linq;
using System.Text;
namespace FLocal.Core.DB.conditions {
public class ComplexCondition : AbstractCondition {
public class ComplexCondition : NotEmptyCondition {
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.innerConditions = innerConditions;
}

@ -4,6 +4,6 @@ using System.Linq;
using System.Text;
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);
}
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>();
foreach(TKey id in ids) {
res[id] = registry.Get(id, true);

@ -5,7 +5,7 @@ using System.Text;
namespace FLocal.Core {
static class ExtensionMethods {
public static class ExtensionMethods {
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";
}
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) {
return val ? "1" : "0";
}
@ -202,7 +206,7 @@ namespace FLocal.Core {
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();
}

@ -24,21 +24,7 @@ namespace FLocal.IISHandler.handlers {
return new XDocument(
new XElement("root",
new XElement("title", Config.instance.AppInfo),
new XElement("categories",
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()
)
)
)
new XElement("categories", from category in Category.allCategories select category.exportToXmlForMainPage())
)
);
}

@ -82,7 +82,7 @@ namespace FLocal.MySQLConnector {
private string CompileCondition(ComplexCondition condition) {
List<string> parts = new List<string>();
foreach(AbstractCondition innerCondition in condition.innerConditions) {
foreach(NotEmptyCondition innerCondition in condition.innerConditions) {
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) {
return CompileCondition((ComplexCondition)condition);
} 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) {
ConditionCompiler compiler = new ConditionCompiler(traits);
string compiled = compiler.CompileCondition(condition);

@ -42,7 +42,7 @@ namespace FLocal.MySQLConnector {
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();
foreach(KeyValuePair<string, string> kvp in paramsHolder.data) {
command.AddParameter(kvp.Key, kvp.Value);
@ -78,7 +78,8 @@ namespace FLocal.MySQLConnector {
command.CommandType = System.Data.CommandType.Text;
var conditionsCompiled = ConditionCompiler.Compile(conditions, this.traits);
string queryConditions = conditionsCompiled.Key;
string queryConditions = "";
if(conditionsCompiled.Key != "") queryConditions = "WHERE " + conditionsCompiled.Key;
ParamsHolder paramsHolder = conditionsCompiled.Value;
string queryJoins = "";
@ -89,13 +90,19 @@ namespace FLocal.MySQLConnector {
}
string querySorts = "";
{
if(sorts.Length > 0) {
throw new NotImplementedException();
List<string> sortParts = new List<string>();
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) {
command.AddParameter(kvp.Key, kvp.Value);
@ -103,7 +110,7 @@ namespace FLocal.MySQLConnector {
command.CommandText = "SELECT COUNT(*) " + queryMain;
object rawCount = command.ExecuteScalar();
int count = (int)rawCount;
long count = (long)rawCount;
if(count < 1) {
diapasone.total = 0;
return new List<string>();

Loading…
Cancel
Save