* LCDecoder.cs: chore: 调通 Query
* LCFile.cs: * LCRole.cs: * LCUser.cs: * LCQuery.cs: * LCObject.cs: * LCHttpClient.cs: * LCObjectData.cs: * LCSubClassInfo.cs: * LCCompositionalCondition.cs:
parent
8132a465ba
commit
362ef92079
|
@ -1,189 +0,0 @@
|
||||||
using NUnit.Framework;
|
|
||||||
using System;
|
|
||||||
using System.Text;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Threading;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
using System.Collections;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
|
|
||||||
namespace LeanCloud.Test {
|
|
||||||
public class LCObject {
|
|
||||||
public string Id {
|
|
||||||
get; set;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Dictionary<string, object> Data {
|
|
||||||
get; set;
|
|
||||||
}
|
|
||||||
|
|
||||||
public LCObject(string id) {
|
|
||||||
Data = new Dictionary<string, object>();
|
|
||||||
Id = id;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static Stack<Batch> Batch(IEnumerable<LCObject> objects) {
|
|
||||||
Stack<Batch> batches = new Stack<Batch>();
|
|
||||||
|
|
||||||
IEnumerable<object> deps = objects;
|
|
||||||
do {
|
|
||||||
// 只添加本层依赖的 LCObject
|
|
||||||
IEnumerable<LCObject> avObjects = deps.OfType<LCObject>();
|
|
||||||
if (avObjects.Any()) {
|
|
||||||
batches.Push(new Batch(avObjects));
|
|
||||||
}
|
|
||||||
|
|
||||||
HashSet<object> childSets = new HashSet<object>();
|
|
||||||
foreach (object dep in deps) {
|
|
||||||
IEnumerable children = null;
|
|
||||||
if (dep is IList) {
|
|
||||||
children = dep as IList;
|
|
||||||
} else if (dep is IDictionary) {
|
|
||||||
children = dep as IDictionary;
|
|
||||||
} else if (dep is LCObject) {
|
|
||||||
children = (dep as LCObject).Data.Values;
|
|
||||||
}
|
|
||||||
if (children != null) {
|
|
||||||
foreach (object child in children) {
|
|
||||||
childSets.Add(child);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
deps = childSets;
|
|
||||||
} while (deps != null && deps.Any());
|
|
||||||
|
|
||||||
return batches;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static bool HasCircleReference(object obj, HashSet<LCObject> parents) {
|
|
||||||
if (parents.Contains(obj)) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
IEnumerable deps = null;
|
|
||||||
if (obj is IList) {
|
|
||||||
deps = obj as IList;
|
|
||||||
} else if (obj is IDictionary) {
|
|
||||||
deps = (obj as IDictionary).Values;
|
|
||||||
} else if (obj is LCObject) {
|
|
||||||
deps = (obj as LCObject).Data.Values;
|
|
||||||
}
|
|
||||||
HashSet<LCObject> depParent = new HashSet<LCObject>(parents);
|
|
||||||
if (obj is LCObject) {
|
|
||||||
depParent.Add((LCObject) obj);
|
|
||||||
}
|
|
||||||
if (deps != null) {
|
|
||||||
foreach (object dep in deps) {
|
|
||||||
HashSet<LCObject> set = new HashSet<LCObject>(depParent);
|
|
||||||
if (HasCircleReference(dep, set)) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Stack<Batch> Batch() {
|
|
||||||
return Batch(new List<LCObject> { this });
|
|
||||||
}
|
|
||||||
|
|
||||||
public bool HasCircleReference() {
|
|
||||||
return HasCircleReference(this, new HashSet<LCObject>());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public class Batch {
|
|
||||||
HashSet<LCObject> ObjectSet {
|
|
||||||
get; set;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Batch() {
|
|
||||||
ObjectSet = new HashSet<LCObject>();
|
|
||||||
}
|
|
||||||
|
|
||||||
public Batch(IEnumerable<LCObject> objects) : this() {
|
|
||||||
foreach (LCObject obj in objects) {
|
|
||||||
ObjectSet.Add(obj);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public override string ToString() {
|
|
||||||
StringBuilder sb = new StringBuilder();
|
|
||||||
sb.AppendLine("----------------------------");
|
|
||||||
foreach (LCObject obj in ObjectSet) {
|
|
||||||
sb.AppendLine(obj.Id);
|
|
||||||
}
|
|
||||||
sb.AppendLine("----------------------------");
|
|
||||||
return sb.ToString();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public class AVObjectTest {
|
|
||||||
void PrintBatches(Stack<Batch> batches) {
|
|
||||||
while (batches.Any()) {
|
|
||||||
Batch batch = batches.Pop();
|
|
||||||
TestContext.WriteLine(batch);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
[Test]
|
|
||||||
public void Simple() {
|
|
||||||
LCObject a = new LCObject("a");
|
|
||||||
LCObject b = new LCObject("b");
|
|
||||||
LCObject c = new LCObject("c");
|
|
||||||
a.Data["child"] = b;
|
|
||||||
b.Data["child"] = c;
|
|
||||||
|
|
||||||
Assert.IsFalse(a.HasCircleReference());
|
|
||||||
|
|
||||||
Stack<Batch> batches = a.Batch();
|
|
||||||
PrintBatches(batches);
|
|
||||||
}
|
|
||||||
|
|
||||||
[Test]
|
|
||||||
public void Array() {
|
|
||||||
LCObject a = new LCObject("a");
|
|
||||||
LCObject b = new LCObject("b");
|
|
||||||
LCObject c = new LCObject("c");
|
|
||||||
a.Data["children"] = new List<LCObject> { b, c };
|
|
||||||
|
|
||||||
Assert.IsFalse(a.HasCircleReference());
|
|
||||||
|
|
||||||
Stack<Batch> batches = a.Batch();
|
|
||||||
PrintBatches(batches);
|
|
||||||
}
|
|
||||||
|
|
||||||
[Test]
|
|
||||||
public void SimpleCircleReference() {
|
|
||||||
LCObject a = new LCObject("a");
|
|
||||||
LCObject b = new LCObject("b");
|
|
||||||
a.Data["child"] = b;
|
|
||||||
b.Data["child"] = a;
|
|
||||||
|
|
||||||
Assert.IsTrue(a.HasCircleReference());
|
|
||||||
}
|
|
||||||
|
|
||||||
[Test]
|
|
||||||
public void ComplexCircleReference() {
|
|
||||||
LCObject a = new LCObject("a");
|
|
||||||
LCObject b = new LCObject("b");
|
|
||||||
LCObject c = new LCObject("c");
|
|
||||||
a.Data["arr"] = new List<object> { 1, b };
|
|
||||||
a.Data["child"] = c;
|
|
||||||
b.Data["arr"] = new List<object> { 2, a };
|
|
||||||
|
|
||||||
Assert.IsTrue(a.HasCircleReference());
|
|
||||||
}
|
|
||||||
|
|
||||||
[Test]
|
|
||||||
public void ComplexCircleReference2() {
|
|
||||||
LCObject a = new LCObject("a");
|
|
||||||
LCObject b = new LCObject("b");
|
|
||||||
List<object> list = new List<object>();
|
|
||||||
a.Data["list"] = list;
|
|
||||||
b.Data["list"] = list;
|
|
||||||
a.Data["child"] = b;
|
|
||||||
|
|
||||||
Assert.IsFalse(a.HasCircleReference());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -24,8 +24,8 @@ namespace LeanCloud.Storage.Internal.Codec {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Dictionary<string, object> d = new Dictionary<string, object>();
|
Dictionary<string, object> d = new Dictionary<string, object>();
|
||||||
foreach (KeyValuePair<string, object> kv in dict) {
|
foreach (DictionaryEntry kv in dict) {
|
||||||
string key = kv.Key;
|
string key = kv.Key.ToString();
|
||||||
object value = kv.Value;
|
object value = kv.Value;
|
||||||
d[key] = Decode(value);
|
d[key] = Decode(value);
|
||||||
}
|
}
|
||||||
|
|
|
@ -37,8 +37,38 @@ namespace LeanCloud.Storage.Internal.Http {
|
||||||
client.DefaultRequestHeaders.Add("X-LC-Key", appKey);
|
client.DefaultRequestHeaders.Add("X-LC-Key", appKey);
|
||||||
}
|
}
|
||||||
|
|
||||||
internal Task Get() {
|
internal async Task<Dictionary<string, object>> Get(string path,
|
||||||
return null;
|
Dictionary<string, object> headers = null,
|
||||||
|
Dictionary<string, object> queryParams = null) {
|
||||||
|
HttpRequestMessage request = new HttpRequestMessage {
|
||||||
|
RequestUri = new Uri($"{server}/{apiVersion}/{path}"),
|
||||||
|
Method = HttpMethod.Get
|
||||||
|
};
|
||||||
|
HttpUtils.PrintRequest(client, request);
|
||||||
|
HttpResponseMessage response = await client.SendAsync(request, HttpCompletionOption.ResponseHeadersRead);
|
||||||
|
request.Dispose();
|
||||||
|
|
||||||
|
string resultString = await response.Content.ReadAsStringAsync();
|
||||||
|
response.Dispose();
|
||||||
|
HttpUtils.PrintResponse(response, resultString);
|
||||||
|
|
||||||
|
HttpStatusCode statusCode = response.StatusCode;
|
||||||
|
if (response.IsSuccessStatusCode) {
|
||||||
|
Dictionary<string, object> ret = JsonConvert.DeserializeObject<Dictionary<string, object>>(resultString, new LeanCloudJsonConverter());
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
int code = (int)statusCode;
|
||||||
|
string message = resultString;
|
||||||
|
try {
|
||||||
|
// 尝试获取 LeanCloud 返回错误信息
|
||||||
|
Dictionary<string, object> error = JsonConvert.DeserializeObject<Dictionary<string, object>>(resultString, new LeanCloudJsonConverter());
|
||||||
|
code = (int)error["code"];
|
||||||
|
message = error["error"].ToString();
|
||||||
|
} catch (Exception e) {
|
||||||
|
Logger.Error(e.Message);
|
||||||
|
} finally {
|
||||||
|
throw new LCException(code, message);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
internal async Task<Dictionary<string, object>> Post(string path,
|
internal async Task<Dictionary<string, object>> Post(string path,
|
||||||
|
|
|
@ -32,8 +32,8 @@ namespace LeanCloud.Storage.Internal.Object {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
LCObjectData objectData = new LCObjectData();
|
LCObjectData objectData = new LCObjectData();
|
||||||
foreach (KeyValuePair<string, object> kv in dict) {
|
foreach (DictionaryEntry kv in dict) {
|
||||||
string key = kv.Key;
|
string key = kv.Key.ToString();
|
||||||
object value = kv.Value;
|
object value = kv.Value;
|
||||||
if (key == "className") {
|
if (key == "className") {
|
||||||
objectData.ClassName = value.ToString();
|
objectData.ClassName = value.ToString();
|
||||||
|
|
|
@ -1,7 +1,23 @@
|
||||||
using System;
|
using System;
|
||||||
|
|
||||||
namespace LeanCloud.Storage.Internal.Object {
|
namespace LeanCloud.Storage.Internal.Object {
|
||||||
public class LCSubClassInfo {
|
internal class LCSubclassInfo {
|
||||||
public LCSubClassInfo() {
|
internal string ClassName {
|
||||||
|
get;
|
||||||
|
}
|
||||||
|
|
||||||
|
internal Type Type {
|
||||||
|
get;
|
||||||
|
}
|
||||||
|
|
||||||
|
internal Func<LCObject> Constructor {
|
||||||
|
get;
|
||||||
|
}
|
||||||
|
|
||||||
|
internal LCSubclassInfo(string className, Type type, Func<LCObject> constructor) {
|
||||||
|
ClassName = className;
|
||||||
|
Type = type;
|
||||||
|
Constructor = constructor;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -110,7 +110,7 @@ namespace LeanCloud.Storage.Internal.Query {
|
||||||
Add(cond);
|
Add(cond);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Add(ILCQueryCondition cond) {
|
internal void Add(ILCQueryCondition cond) {
|
||||||
if (cond == null) {
|
if (cond == null) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -129,7 +129,7 @@ namespace LeanCloud.Storage.Internal.Query {
|
||||||
orderByList.Add(key);
|
orderByList.Add(key);
|
||||||
}
|
}
|
||||||
|
|
||||||
internal void OrderByDesending(string key) {
|
internal void OrderByDescending(string key) {
|
||||||
OrderBy($"-{key}");
|
OrderBy($"-{key}");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
using System;
|
using System;
|
||||||
namespace LeanCloud.Storage {
|
namespace LeanCloud.Storage {
|
||||||
public class LCFile : LCObject {
|
public class LCFile : LCObject {
|
||||||
public LCFile() {
|
public LCFile() : base("_File") {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,7 @@
|
||||||
using System.Collections.Generic;
|
using System;
|
||||||
|
using System.Collections;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
using LeanCloud.Storage.Internal.Object;
|
using LeanCloud.Storage.Internal.Object;
|
||||||
using LeanCloud.Storage.Internal.Operation;
|
using LeanCloud.Storage.Internal.Operation;
|
||||||
|
|
||||||
|
@ -22,6 +25,9 @@ namespace LeanCloud.Storage {
|
||||||
/// </summary>
|
/// </summary>
|
||||||
Dictionary<string, ILCOperation> operationDict;
|
Dictionary<string, ILCOperation> operationDict;
|
||||||
|
|
||||||
|
static readonly Dictionary<Type, LCSubclassInfo> subclassTypeDict = new Dictionary<Type, LCSubclassInfo>();
|
||||||
|
static readonly Dictionary<string, LCSubclassInfo> subclassNameDict = new Dictionary<string, LCSubclassInfo>();
|
||||||
|
|
||||||
public string ClassName {
|
public string ClassName {
|
||||||
get {
|
get {
|
||||||
return data.ClassName;
|
return data.ClassName;
|
||||||
|
@ -34,12 +40,154 @@ namespace LeanCloud.Storage {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public LCObject() {
|
public DateTime CreatedAt {
|
||||||
|
get {
|
||||||
|
return data.CreatedAt;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public DateTime UpdatedAt {
|
||||||
|
get {
|
||||||
|
return data.UpdatedAt;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public LCACL ACL {
|
||||||
|
get {
|
||||||
|
return this["ACL"] as LCACL ;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool isNew;
|
||||||
|
|
||||||
|
public LCObject(string className) {
|
||||||
|
if (string.IsNullOrEmpty(className)) {
|
||||||
|
throw new ArgumentNullException(nameof(className));
|
||||||
|
}
|
||||||
|
data = new LCObjectData();
|
||||||
|
estimatedData = new Dictionary<string, object>();
|
||||||
|
operationDict = new Dictionary<string, ILCOperation>();
|
||||||
|
|
||||||
|
data.ClassName = className;
|
||||||
|
isNew = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static LCObject CreateWithoutData(string className, string objectId) {
|
||||||
|
if (string.IsNullOrEmpty(objectId)) {
|
||||||
|
throw new ArgumentNullException(nameof(objectId));
|
||||||
|
}
|
||||||
|
LCObject obj = new LCObject(className);
|
||||||
|
obj.data.ObjectId = objectId;
|
||||||
|
obj.isNew = false;
|
||||||
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
internal static LCObject Create(string className) {
|
internal static LCObject Create(string className) {
|
||||||
// TODO
|
if (subclassNameDict.TryGetValue(className, out LCSubclassInfo subclassInfo)) {
|
||||||
|
return subclassInfo.Constructor.Invoke();
|
||||||
|
}
|
||||||
|
return new LCObject(className);
|
||||||
|
}
|
||||||
|
|
||||||
|
internal static LCObject Create(Type type) {
|
||||||
|
if (subclassTypeDict.TryGetValue(type, out LCSubclassInfo subclassInfo)) {
|
||||||
|
return subclassInfo.Constructor.Invoke();
|
||||||
|
}
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public object this[string key] {
|
||||||
|
get {
|
||||||
|
object value = estimatedData[key];
|
||||||
|
if (value is LCRelation<LCObject> relation) {
|
||||||
|
relation.Key = key;
|
||||||
|
relation.Parent = this;
|
||||||
|
}
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
set {
|
||||||
|
if (string.IsNullOrEmpty(key)) {
|
||||||
|
throw new ArgumentNullException(nameof(key));
|
||||||
|
}
|
||||||
|
if (key.StartsWith("_")) {
|
||||||
|
throw new ArgumentException("key should not start with '_'");
|
||||||
|
}
|
||||||
|
if (key == "objectId" || key == "createdAt" || key == "updatedAt") {
|
||||||
|
throw new ArgumentException($"{key} is reserved by LeanCloud");
|
||||||
|
}
|
||||||
|
LCSetOperation setOp = new LCSetOperation(value);
|
||||||
|
ApplyOperation(key, setOp);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Unset(string key) {
|
||||||
|
if (string.IsNullOrEmpty(key)) {
|
||||||
|
throw new ArgumentNullException(nameof(key));
|
||||||
|
}
|
||||||
|
LCDeleteOperation deleteOp = new LCDeleteOperation();
|
||||||
|
ApplyOperation(key, deleteOp);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void RegisterSubclass(string className, Type type, Func<LCObject> constructor) {
|
||||||
|
LCSubclassInfo subclassInfo = new LCSubclassInfo(className, type, constructor);
|
||||||
|
subclassNameDict[className] = subclassInfo;
|
||||||
|
subclassTypeDict[type] = subclassInfo;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ApplyOperation(string key, ILCOperation op) {
|
||||||
|
if (operationDict.TryGetValue(key, out ILCOperation previousOp)) {
|
||||||
|
operationDict[key] = op.MergeWithPrevious(previousOp);
|
||||||
|
} else {
|
||||||
|
operationDict[key] = op;
|
||||||
|
}
|
||||||
|
if (op is LCDeleteOperation) {
|
||||||
|
estimatedData.Remove(key);
|
||||||
|
} else {
|
||||||
|
object oldValue = estimatedData[key];
|
||||||
|
estimatedData[key] = op.Apply(oldValue, key);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
internal void Merge(LCObjectData objectData) {
|
||||||
|
data.ClassName = objectData.ClassName ?? data.ClassName;
|
||||||
|
data.ObjectId = objectData.ObjectId ?? data.ObjectId;
|
||||||
|
data.CreatedAt = objectData.CreatedAt != null ? objectData.CreatedAt : data.CreatedAt;
|
||||||
|
data.UpdatedAt = objectData.UpdatedAt != null ? objectData.UpdatedAt : data.UpdatedAt;
|
||||||
|
// 先将本地的预估数据直接替换
|
||||||
|
data.CustomPropertyDict = estimatedData;
|
||||||
|
// 再将服务端的数据覆盖
|
||||||
|
foreach (KeyValuePair<string, object> kv in objectData.CustomPropertyDict) {
|
||||||
|
string key = kv.Key;
|
||||||
|
object value = kv.Value;
|
||||||
|
data.CustomPropertyDict[key] = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 最后重新生成预估数据,用于后续访问和操作
|
||||||
|
RebuildEstimatedData();
|
||||||
|
// 清空操作
|
||||||
|
operationDict.Clear();
|
||||||
|
isNew = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void RebuildEstimatedData() {
|
||||||
|
estimatedData = new Dictionary<string, object>();
|
||||||
|
foreach (KeyValuePair<string, object> kv in data.CustomPropertyDict) {
|
||||||
|
string key = kv.Key;
|
||||||
|
object value = kv.Value;
|
||||||
|
if (value is IList list) {
|
||||||
|
estimatedData[key] = new List<object>(list.Cast<object>());
|
||||||
|
} else if (value is IDictionary dict) {
|
||||||
|
Dictionary<string, object> d = new Dictionary<string, object>();
|
||||||
|
foreach (DictionaryEntry entry in dict) {
|
||||||
|
string k = entry.Key.ToString();
|
||||||
|
object v = entry.Value;
|
||||||
|
d[k] = v;
|
||||||
|
}
|
||||||
|
estimatedData[key] = d;
|
||||||
|
} else {
|
||||||
|
estimatedData[key] = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,364 @@
|
||||||
using System;
|
using System;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Collections;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using LeanCloud.Storage.Internal.Query;
|
||||||
|
using LeanCloud.Storage.Internal.Object;
|
||||||
|
|
||||||
namespace LeanCloud.Storage {
|
namespace LeanCloud.Storage {
|
||||||
public class LCQuery {
|
/// <summary>
|
||||||
public LCQuery() {
|
/// 查询类
|
||||||
|
/// </summary>
|
||||||
|
/// <typeparam name="T"></typeparam>
|
||||||
|
public class LCQuery<T> where T : LCObject {
|
||||||
|
public string ClassName {
|
||||||
|
get; private set;
|
||||||
|
}
|
||||||
|
|
||||||
|
LCCompositionalCondition condition;
|
||||||
|
|
||||||
|
public LCQuery(string className) {
|
||||||
|
ClassName = className;
|
||||||
|
condition = new LCCompositionalCondition();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 等于
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="key"></param>
|
||||||
|
/// <param name="value"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
public LCQuery<T> WhereEqualTo(string key, object value) {
|
||||||
|
condition.WhereEqualTo(key, value);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 不等于
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="key"></param>
|
||||||
|
/// <param name="value"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
public LCQuery<T> WhereNotEqualTo(string key, object value) {
|
||||||
|
condition.WhereNotEqualTo(key, value);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 包含
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="key"></param>
|
||||||
|
/// <param name="values"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
public LCQuery<T> WhereContainedIn(string key, IEnumerable values) {
|
||||||
|
condition.WhereContainedIn(key, values);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 包含全部
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="key"></param>
|
||||||
|
/// <param name="values"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
public LCQuery<T> WhereContainsAll(string key, IEnumerable values) {
|
||||||
|
condition.WhereContainsAll(key, values);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 存在
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="key"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
public LCQuery<T> WhereExists(string key) {
|
||||||
|
condition.WhereExists(key);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 不存在
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="key"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
public LCQuery<T> WhereDoesNotExist(string key) {
|
||||||
|
condition.WhereDoesNotExist(key);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 长度等于
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="key"></param>
|
||||||
|
/// <param name="size"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
public LCQuery<T> WhereSizeEqualTo(string key, int size) {
|
||||||
|
condition.WhereSizeEqualTo(key, size);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 大于
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="key"></param>
|
||||||
|
/// <param name="value"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
public LCQuery<T> WhereGreaterThan(string key, object value) {
|
||||||
|
condition.WhereGreaterThan(key, value);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 大于等于
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="key"></param>
|
||||||
|
/// <param name="value"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
public LCQuery<T> WhereGreaterThanOrEqualTo(string key, object value) {
|
||||||
|
condition.WhereGreaterThanOrEqualTo(key, value);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 小于
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="key"></param>
|
||||||
|
/// <param name="value"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
public LCQuery<T> WhereLessThan(string key, object value) {
|
||||||
|
condition.WhereLessThan(key, value);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 小于等于
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="key"></param>
|
||||||
|
/// <param name="value"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
public LCQuery<T> WhereLessThanOrEqualTo(string key, object value) {
|
||||||
|
condition.WhereLessThanOrEqualTo(key, value);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 相邻
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="key"></param>
|
||||||
|
/// <param name="point"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
public LCQuery<T> WhereNear(string key, LCGeoPoint point) {
|
||||||
|
condition.WhereNear(key, point);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 在坐标区域内
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="key"></param>
|
||||||
|
/// <param name="southwest"></param>
|
||||||
|
/// <param name="northeast"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
public LCQuery<T> WhereWithinGeoBox(string key, LCGeoPoint southwest, LCGeoPoint northeast) {
|
||||||
|
condition.WhereWithinGeoBox(key, southwest, northeast);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 相关
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="parent"></param>
|
||||||
|
/// <param name="key"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
public LCQuery<T> WhereRelatedTo(LCObject parent, string key) {
|
||||||
|
condition.WhereRelatedTo(parent, key);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 前缀
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="key"></param>
|
||||||
|
/// <param name="prefix"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
public LCQuery<T> WhereStartsWith(string key, string prefix) {
|
||||||
|
condition.WhereStartsWith(key, prefix);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 后缀
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="key"></param>
|
||||||
|
/// <param name="suffix"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
public LCQuery<T> WhereEndsWith(string key, string suffix) {
|
||||||
|
condition.WhereEndsWith(key, suffix);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 字符串包含
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="key"></param>
|
||||||
|
/// <param name="subString"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
public LCQuery<T> WhereContains(string key, string subString) {
|
||||||
|
condition.WhereContains(key, subString);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 按 key 升序
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="key"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
public LCQuery<T> OrderBy(string key) {
|
||||||
|
condition.OrderBy(key);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 按 key 降序
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="key"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
public LCQuery<T> OrderByDescending(string key) {
|
||||||
|
condition.OrderByDescending(key);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 拉取 key 的完整对象
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="key"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
public LCQuery<T> Include(string key) {
|
||||||
|
condition.Include(key);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 包含 key
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="key"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
public LCQuery<T> Select(string key) {
|
||||||
|
condition.Select(key);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 跳过
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="value"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
public LCQuery<T> Skip(int value) {
|
||||||
|
condition.Skip = value;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 限制数量
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="value"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
public LCQuery<T> Limit(int value) {
|
||||||
|
condition.Limit = value;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<int> Count() {
|
||||||
|
string path = $"classes/{ClassName}";
|
||||||
|
Dictionary<string, object> parameters = BuildParams();
|
||||||
|
parameters["limit"] = 0;
|
||||||
|
parameters["count"] = 1;
|
||||||
|
Dictionary<string, object> ret = await LeanCloud.HttpClient.Get(path, queryParams: parameters);
|
||||||
|
return (int)ret["count"];
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<T> Get(string objectId) {
|
||||||
|
if (string.IsNullOrEmpty(objectId)) {
|
||||||
|
throw new ArgumentNullException(nameof(objectId));
|
||||||
|
}
|
||||||
|
WhereEqualTo("objectId", objectId);
|
||||||
|
Limit(1);
|
||||||
|
List<T> results = await Find();
|
||||||
|
if (results != null) {
|
||||||
|
if (results.Count == 0) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return results[0];
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<List<T>> Find() {
|
||||||
|
string path = $"classes/{ClassName}";
|
||||||
|
Dictionary<string, object> parameters = BuildParams();
|
||||||
|
Dictionary<string, object> response = await LeanCloud.HttpClient.Get(path, queryParams: parameters);
|
||||||
|
List<object> results = response["results"] as List<object>;
|
||||||
|
List<T> list = new List<T>();
|
||||||
|
foreach (object item in results) {
|
||||||
|
LCObjectData objectData = LCObjectData.Decode(item as Dictionary<string, object>);
|
||||||
|
T obj = LCObject.Create(ClassName) as T;
|
||||||
|
obj.Merge(objectData);
|
||||||
|
list.Add(obj);
|
||||||
|
}
|
||||||
|
return list;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<T> First() {
|
||||||
|
Limit(1);
|
||||||
|
List<T> results = await Find();
|
||||||
|
if (results != null && results.Count > 0) {
|
||||||
|
return results[0];
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static LCQuery<T> And(IEnumerable<LCQuery<T>> queries) {
|
||||||
|
if (queries == null || queries.Count() < 1) {
|
||||||
|
throw new ArgumentNullException(nameof(queries));
|
||||||
|
}
|
||||||
|
LCQuery<T> compositionQuery = new LCQuery<T>(null);
|
||||||
|
string className = null;
|
||||||
|
foreach (LCQuery<T> query in queries) {
|
||||||
|
if (className != null && className != query.ClassName) {
|
||||||
|
throw new Exception("All of the queries in an or query must be on the same class.");
|
||||||
|
}
|
||||||
|
className = query.ClassName;
|
||||||
|
compositionQuery.condition.Add(query.condition);
|
||||||
|
}
|
||||||
|
compositionQuery.ClassName = className;
|
||||||
|
return compositionQuery;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static LCQuery<T> Or(IEnumerable<LCQuery<T>> queries) {
|
||||||
|
if (queries == null || queries.Count() < 1) {
|
||||||
|
throw new ArgumentNullException(nameof(queries));
|
||||||
|
}
|
||||||
|
LCQuery<T> compositionQuery = new LCQuery<T>(null);
|
||||||
|
compositionQuery.condition = new LCCompositionalCondition(LCCompositionalCondition.Or);
|
||||||
|
string className = null;
|
||||||
|
foreach (LCQuery<T> query in queries) {
|
||||||
|
if (className != null && className != query.ClassName) {
|
||||||
|
throw new Exception("All of the queries in an or query must be on the same class.");
|
||||||
|
}
|
||||||
|
className = query.ClassName;
|
||||||
|
compositionQuery.condition.Add(query.condition);
|
||||||
|
}
|
||||||
|
compositionQuery.ClassName = className;
|
||||||
|
return compositionQuery;
|
||||||
|
}
|
||||||
|
|
||||||
|
Dictionary<string, object> BuildParams() {
|
||||||
|
return condition.BuildParams(ClassName);
|
||||||
|
}
|
||||||
|
|
||||||
|
string BuildWhere() {
|
||||||
|
return condition.BuildWhere();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
|
|
||||||
namespace LeanCloud.Storage {
|
namespace LeanCloud.Storage {
|
||||||
public class LCRole : LCObject {
|
public class LCRole : LCObject {
|
||||||
public LCRole() {
|
public LCRole() : base("_Role") {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,9 @@
|
||||||
using System;
|
using System;
|
||||||
|
|
||||||
namespace LeanCloud.Storage {
|
namespace LeanCloud.Storage {
|
||||||
public class LCUser : LCObject {
|
public class LCUser : LCObject {
|
||||||
public LCUser() {
|
public LCUser() : base("_User") {
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue