|
@@ -564,6 +564,7 @@ namespace InABox.Database.SQLite
|
|
|
&& x.GetCustomAttributes().FirstOrDefault(a => a.GetType().Equals(typeof(AggregateAttribute))) == null
|
|
|
&& x.GetCustomAttributes().FirstOrDefault(a => a.GetType().Equals(typeof(FormulaAttribute))) == null
|
|
|
&& x.GetCustomAttributes().FirstOrDefault(a => a.GetType().Equals(typeof(ConditionAttribute))) == null
|
|
|
+ && x.GetCustomAttributes().FirstOrDefault(a => a.GetType().Equals(typeof(ChildEntityAttribute))) == null
|
|
|
&& x.CanWrite
|
|
|
&& x.PropertyType != typeof(UserProperties)
|
|
|
);
|
|
@@ -599,7 +600,9 @@ namespace InABox.Database.SQLite
|
|
|
|
|
|
var properties = type.GetProperties().Where(x =>
|
|
|
x.GetCustomAttribute<DoNotSerialize>() == null && x.GetCustomAttribute<DoNotPersist>() == null &&
|
|
|
- x.GetCustomAttributes().FirstOrDefault(a => a.GetType().IsSubclassOf(typeof(AggregateAttribute))) == null && x.CanWrite);
|
|
|
+ x.GetCustomAttributes().FirstOrDefault(a => a.GetType().IsSubclassOf(typeof(AggregateAttribute))) == null
|
|
|
+ && x.GetCustomAttributes().FirstOrDefault(a => a.GetType().Equals(typeof(ChildEntityAttribute))) == null
|
|
|
+ && x.CanWrite);
|
|
|
foreach (var property in properties)
|
|
|
if (property.PropertyType.GetInterfaces().Contains(typeof(IEntityLink)))
|
|
|
{
|
|
@@ -1767,10 +1770,9 @@ namespace InABox.Database.SQLite
|
|
|
if (prop != null)
|
|
|
{
|
|
|
// LogStart();
|
|
|
- var attr = prop.GetCustomAttributes().FirstOrDefault(x => x.GetType().Equals(typeof(AggregateAttribute)));
|
|
|
// LogStop("GetCustomAttributes(Aggregate)");
|
|
|
|
|
|
- if (attr is AggregateAttribute agg)
|
|
|
+ if (prop.GetCustomAttribute<AggregateAttribute>() is AggregateAttribute agg)
|
|
|
{
|
|
|
bool internalaggregate = agg.Calculator.GetType().GetInterfaces()
|
|
|
.Any(x => x.IsGenericType && x.GetGenericTypeDefinition() == typeof(ICoreAggregate<,>));
|
|
@@ -1862,83 +1864,69 @@ namespace InABox.Database.SQLite
|
|
|
//fieldmap[columnname] = String.Format("{0}.[{1}]", tuple.Item2, agg.AggregateProperty);
|
|
|
}
|
|
|
}
|
|
|
- else
|
|
|
+ else if (prop.GetCustomAttribute<FormulaAttribute>() is FormulaAttribute fnc)
|
|
|
{
|
|
|
- // LogStart();
|
|
|
- attr = prop.GetCustomAttributes().FirstOrDefault(x => x.GetType().Equals(typeof(FormulaAttribute)));
|
|
|
- // LogStop("GetAttribute(Formula)");
|
|
|
|
|
|
- if (attr is FormulaAttribute fnc)
|
|
|
+ // var functionmap = new Dictionary<string, string>();
|
|
|
+ // CheckColumn(columns, fnc.Value);
|
|
|
+ // LoadFieldsandTables(command, type, prefix, functionmap, tables, columns, fnc.Value, useparams);
|
|
|
+ // foreach (var column in fnc.Modifiers)
|
|
|
+ // {
|
|
|
+ // CheckColumn(columns, column);
|
|
|
+ // LoadFieldsandTables(command, type, prefix, functionmap, tables, columns, column, useparams);
|
|
|
+ // }
|
|
|
+ // fieldmap[columnname] = GetFunction(fnc, functionmap, columnname);
|
|
|
+
|
|
|
+ foreach (var field in fnc.Modifiers.Prepend(fnc.Value))
|
|
|
{
|
|
|
-
|
|
|
- // var functionmap = new Dictionary<string, string>();
|
|
|
- // CheckColumn(columns, fnc.Value);
|
|
|
- // LoadFieldsandTables(command, type, prefix, functionmap, tables, columns, fnc.Value, useparams);
|
|
|
- // foreach (var column in fnc.Modifiers)
|
|
|
- // {
|
|
|
- // CheckColumn(columns, column);
|
|
|
- // LoadFieldsandTables(command, type, prefix, functionmap, tables, columns, column, useparams);
|
|
|
- // }
|
|
|
- // fieldmap[columnname] = GetFunction(fnc, functionmap, columnname);
|
|
|
-
|
|
|
- foreach (var field in fnc.Modifiers.Prepend(fnc.Value))
|
|
|
- {
|
|
|
- CheckColumn(columns, field);
|
|
|
- LoadFieldsandTables(command, type, prefix, fieldmap, tables, columns, field, useparams);
|
|
|
- }
|
|
|
- fieldmap[columnname] = GetFunction(fnc, fieldmap, columnname);
|
|
|
-
|
|
|
+ CheckColumn(columns, field);
|
|
|
+ LoadFieldsandTables(command, type, prefix, fieldmap, tables, columns, field, useparams);
|
|
|
}
|
|
|
- else
|
|
|
- {
|
|
|
- // LogStart();
|
|
|
- attr = prop.GetCustomAttributes().FirstOrDefault(x => x.GetType().Equals(typeof(ConditionAttribute)));
|
|
|
- // LogStop("GetAttribute(Condition)");
|
|
|
+ fieldmap[columnname] = GetFunction(fnc, fieldmap, columnname);
|
|
|
|
|
|
- if (attr is ConditionAttribute cnd)
|
|
|
- {
|
|
|
- var cndmap = new Dictionary<string, string>();
|
|
|
- // LogStart();
|
|
|
- CheckColumn(columns, cnd.Left);
|
|
|
- // LogStop("CheckColumn(Left)");
|
|
|
+ }
|
|
|
+ else if(prop.GetCustomAttribute<ConditionAttribute>() is ConditionAttribute cnd)
|
|
|
+ {
|
|
|
+ var cndmap = new Dictionary<string, string>();
|
|
|
+ // LogStart();
|
|
|
+ CheckColumn(columns, cnd.Left);
|
|
|
+ // LogStop("CheckColumn(Left)");
|
|
|
|
|
|
- // LogStart();
|
|
|
- CheckColumn(columns, cnd.Right);
|
|
|
- // LogStop("CheckColumn(Right)");
|
|
|
+ // LogStart();
|
|
|
+ CheckColumn(columns, cnd.Right);
|
|
|
+ // LogStop("CheckColumn(Right)");
|
|
|
|
|
|
- // LogStart();
|
|
|
- CheckColumn(columns, cnd.True);
|
|
|
- // LogStop("CheckColumn(True)");
|
|
|
+ // LogStart();
|
|
|
+ CheckColumn(columns, cnd.True);
|
|
|
+ // LogStop("CheckColumn(True)");
|
|
|
|
|
|
- // LogStart();
|
|
|
- CheckColumn(columns, cnd.False);
|
|
|
- // LogStop("CheckColumn(False)");
|
|
|
+ // LogStart();
|
|
|
+ CheckColumn(columns, cnd.False);
|
|
|
+ // LogStop("CheckColumn(False)");
|
|
|
|
|
|
- //// LogStart();
|
|
|
- LoadFieldsandTables(command, type, prefix, cndmap, tables, columns, cnd.Left, useparams);
|
|
|
- //// LogStop("LoadFieldsAndTables(Left)");
|
|
|
+ //// LogStart();
|
|
|
+ LoadFieldsandTables(command, type, prefix, cndmap, tables, columns, cnd.Left, useparams);
|
|
|
+ //// LogStop("LoadFieldsAndTables(Left)");
|
|
|
|
|
|
- //// LogStart();
|
|
|
- LoadFieldsandTables(command, type, prefix, cndmap, tables, columns, cnd.Right, useparams);
|
|
|
- //// LogStop("LoadFieldsAndTables(Right)");
|
|
|
+ //// LogStart();
|
|
|
+ LoadFieldsandTables(command, type, prefix, cndmap, tables, columns, cnd.Right, useparams);
|
|
|
+ //// LogStop("LoadFieldsAndTables(Right)");
|
|
|
|
|
|
- //// LogStart();
|
|
|
- LoadFieldsandTables(command, type, prefix, cndmap, tables, columns, cnd.True, useparams);
|
|
|
- //// LogStop("LoadFieldsAndTables(True)");
|
|
|
+ //// LogStart();
|
|
|
+ LoadFieldsandTables(command, type, prefix, cndmap, tables, columns, cnd.True, useparams);
|
|
|
+ //// LogStop("LoadFieldsAndTables(True)");
|
|
|
|
|
|
- //// LogStart();
|
|
|
- LoadFieldsandTables(command, type, prefix, cndmap, tables, columns, cnd.False, useparams);
|
|
|
- //// LogStop("LoadFieldsAndTables(False)");
|
|
|
+ //// LogStart();
|
|
|
+ LoadFieldsandTables(command, type, prefix, cndmap, tables, columns, cnd.False, useparams);
|
|
|
+ //// LogStop("LoadFieldsAndTables(False)");
|
|
|
|
|
|
- // LogStart();
|
|
|
- fieldmap[columnname] = GetCondition(cnd, cndmap, columnname);
|
|
|
- // LogStop("GetCondition");
|
|
|
- }
|
|
|
- else
|
|
|
- {
|
|
|
- fieldmap[columnname] = string.Format("{0}1.[{1}]", prefix, columnname);
|
|
|
- }
|
|
|
- }
|
|
|
+ // LogStart();
|
|
|
+ fieldmap[columnname] = GetCondition(cnd, cndmap, columnname);
|
|
|
+ // LogStop("GetCondition");
|
|
|
+ }
|
|
|
+ else
|
|
|
+ {
|
|
|
+ fieldmap[columnname] = string.Format("{0}1.[{1}]", prefix, columnname);
|
|
|
}
|
|
|
}
|
|
|
else
|
|
@@ -1964,46 +1952,61 @@ namespace InABox.Database.SQLite
|
|
|
|
|
|
if (prop.PropertyType.GetInterfaces().Contains(typeof(IEntityLink)))
|
|
|
{
|
|
|
- var linkedtype = prop.PropertyType.GetInheritedGenericTypeArguments().First();
|
|
|
- var remote = linkedtype.GetProperty(bits.Last());
|
|
|
+ var linkedType = prop.PropertyType.GetInheritedGenericTypeArguments().First();
|
|
|
+ var remote = linkedType.GetProperty(bits.Last());
|
|
|
|
|
|
// Are there any other properties for this link? These will form the columns for our subquery
|
|
|
var siblings = columns.Where(x => x.Split('.').First().Equals(bits.First()))
|
|
|
.Select(x => string.Join(".", x.Split('.').Skip(combinecount))).ToList();
|
|
|
if (remote != null && !siblings.Contains(remote.Name))
|
|
|
siblings.Add(remote.Name);
|
|
|
-
|
|
|
- if (siblings.Count.Equals(1) && siblings.First().Equals("ID"))
|
|
|
- fieldmap[columnname] = string.Format("{0}1.[{1}]", prefix, columnname);
|
|
|
-
|
|
|
- else
|
|
|
- {
|
|
|
- if (!siblings.Contains("ID"))
|
|
|
- siblings.Insert(0, "ID");
|
|
|
-
|
|
|
- var subcols = Columns.Create(linkedtype);
|
|
|
-
|
|
|
- foreach (var sibling in siblings)
|
|
|
- subcols.Add(sibling);
|
|
|
|
|
|
- var linkedtable = string.Format("({0})",
|
|
|
- PrepareSelectNonGeneric(linkedtype, command, newprefix, null, subcols, null, null, null, int.MaxValue, false, useparams));
|
|
|
+ if(prop.GetCustomAttribute<ChildEntityAttribute>() is ChildEntityAttribute child)
|
|
|
+ {
|
|
|
+ var parent = child.Calculator.ParentColumn;
|
|
|
+
|
|
|
+ if (!siblings.Contains(nameof(Entity.ID)))
|
|
|
+ siblings.Insert(0, nameof(Entity.ID));
|
|
|
+ if (!siblings.Contains(parent))
|
|
|
+ siblings.Add(parent);
|
|
|
+
|
|
|
+ var subcols = Columns.Create(linkedType, siblings);
|
|
|
+
|
|
|
+ var subPrefix = (char)(newprefix + 1);
|
|
|
+
|
|
|
+ var innerSQL = string.Format("({0})",
|
|
|
+ PrepareSelectNonGeneric(
|
|
|
+ linkedType,
|
|
|
+ command,
|
|
|
+ subPrefix,
|
|
|
+ child.Calculator.Filter,
|
|
|
+ subcols,
|
|
|
+ child.Calculator.Sort,
|
|
|
+ null,
|
|
|
+ null,
|
|
|
+ int.MaxValue,
|
|
|
+ false,
|
|
|
+ useparams));
|
|
|
+
|
|
|
+ var linkedTable = $"(SELECT {string.Join(", ", siblings.Select(x => $"{newprefix}1.[{x}] as [{x}]"))}"
|
|
|
+ + $" FROM {innerSQL} {newprefix}1"
|
|
|
+ + $" GROUP BY [{parent}])";
|
|
|
|
|
|
var link = string.Format("{0}.ID", prop.Name);
|
|
|
var tuple = tables.FirstOrDefault(x =>
|
|
|
- x.Item1.Equals(linkedtable) && x.Item4.Equals(link) && !x.Item2.Equals(string.Format("{0}1", prefix)));
|
|
|
- if (tuple == null)
|
|
|
+ x.Item1.Equals(linkedTable) && x.Item4.Equals(link) && !x.Item2.Equals(string.Format("{0}1", prefix)));
|
|
|
+ if (tuple is null)
|
|
|
{
|
|
|
var alias = tables.Count + 1;
|
|
|
tuple = new Tuple<string, string, string, string>(
|
|
|
- linkedtable,
|
|
|
+ linkedTable,
|
|
|
prefix + alias.ToString(),
|
|
|
string.Format(
|
|
|
- "LEFT OUTER JOIN {0} {1}{2} ON {1}{2}.[ID] = {1}1.[{3}.ID]",
|
|
|
- linkedtable,
|
|
|
+ "LEFT OUTER JOIN {0} {1}{2} ON {1}{2}.[{3}] = {1}1.[ID]",
|
|
|
+ linkedTable,
|
|
|
prefix,
|
|
|
alias,
|
|
|
- string.Join(".", bits.Take(combinecount)) //prop.Name
|
|
|
+ parent
|
|
|
),
|
|
|
link
|
|
|
);
|
|
@@ -2021,6 +2024,55 @@ namespace InABox.Database.SQLite
|
|
|
fieldmap[subcol] = string.Format("{0}.[{1}]", tuple.Item2, sibling);
|
|
|
}
|
|
|
}
|
|
|
+ else
|
|
|
+ {
|
|
|
+ if (siblings.Count.Equals(1) && siblings.First().Equals("ID"))
|
|
|
+ {
|
|
|
+ fieldmap[columnname] = string.Format("{0}1.[{1}]", prefix, columnname);
|
|
|
+ }
|
|
|
+ else
|
|
|
+ {
|
|
|
+ if (!siblings.Contains("ID"))
|
|
|
+ siblings.Insert(0, "ID");
|
|
|
+
|
|
|
+ var subcols = Columns.Create(linkedType, siblings);
|
|
|
+
|
|
|
+ var linkedtable = string.Format("({0})",
|
|
|
+ PrepareSelectNonGeneric(linkedType, command, newprefix, null, subcols, null, null, null, int.MaxValue, false, useparams));
|
|
|
+
|
|
|
+ var link = string.Format("{0}.ID", prop.Name);
|
|
|
+ var tuple = tables.FirstOrDefault(x =>
|
|
|
+ x.Item1.Equals(linkedtable) && x.Item4.Equals(link) && !x.Item2.Equals(string.Format("{0}1", prefix)));
|
|
|
+ if (tuple == null)
|
|
|
+ {
|
|
|
+ var alias = tables.Count + 1;
|
|
|
+ tuple = new Tuple<string, string, string, string>(
|
|
|
+ linkedtable,
|
|
|
+ prefix + alias.ToString(),
|
|
|
+ string.Format(
|
|
|
+ "LEFT OUTER JOIN {0} {1}{2} ON {1}{2}.[ID] = {1}1.[{3}.ID]",
|
|
|
+ linkedtable,
|
|
|
+ prefix,
|
|
|
+ alias,
|
|
|
+ string.Join(".", bits.Take(combinecount)) //prop.Name
|
|
|
+ ),
|
|
|
+ link
|
|
|
+ );
|
|
|
+ tables.Add(tuple);
|
|
|
+ }
|
|
|
+
|
|
|
+ //if (bits.Last().Equals("ID"))
|
|
|
+ // fieldmap[columnname] = String.Format("{0}1.[{1}]", prefix, columnname);
|
|
|
+ //else
|
|
|
+ fieldmap[columnname] = string.Format("{0}.[{1}]", tuple.Item2, string.Join(".", bits.Skip(combinecount)));
|
|
|
+ foreach (var sibling in siblings)
|
|
|
+ {
|
|
|
+ var subcol = string.Format("{0}.{1}", string.Join(".", bits.Take(combinecount)), sibling);
|
|
|
+ if (!subcol.Equals(columnname))
|
|
|
+ fieldmap[subcol] = string.Format("{0}.[{1}]", tuple.Item2, sibling);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
}
|
|
|
else if (prop.PropertyType.GetInterfaces().Contains(typeof(IEnclosedEntity)))
|
|
|
{
|