176 lines
5.5 KiB
C#
176 lines
5.5 KiB
C#
|
using LeanCloud.Storage.Internal;
|
|||
|
using System;
|
|||
|
using System.Collections.Generic;
|
|||
|
using System.ComponentModel;
|
|||
|
using System.Diagnostics;
|
|||
|
using System.Linq;
|
|||
|
using System.Linq.Expressions;
|
|||
|
using System.Reflection;
|
|||
|
using System.Text;
|
|||
|
|
|||
|
namespace LeanCloud
|
|||
|
{
|
|||
|
/// <summary>
|
|||
|
/// A common base class for AVRelations.
|
|||
|
/// </summary>
|
|||
|
[EditorBrowsable(EditorBrowsableState.Never)]
|
|||
|
public abstract class AVRelationBase : IJsonConvertible
|
|||
|
{
|
|||
|
private AVObject parent;
|
|||
|
private string key;
|
|||
|
private string targetClassName;
|
|||
|
|
|||
|
internal AVRelationBase(AVObject parent, string key)
|
|||
|
{
|
|||
|
EnsureParentAndKey(parent, key);
|
|||
|
}
|
|||
|
|
|||
|
internal AVRelationBase(AVObject parent, string key, string targetClassName)
|
|||
|
: this(parent, key)
|
|||
|
{
|
|||
|
this.targetClassName = targetClassName;
|
|||
|
}
|
|||
|
|
|||
|
internal static IObjectSubclassingController SubclassingController
|
|||
|
{
|
|||
|
get
|
|||
|
{
|
|||
|
return AVPlugins.Instance.SubclassingController;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
internal void EnsureParentAndKey(AVObject parent, string key)
|
|||
|
{
|
|||
|
this.parent = this.parent ?? parent;
|
|||
|
this.key = this.key ?? key;
|
|||
|
Debug.Assert(this.parent == parent, "Relation retrieved from two different objects");
|
|||
|
Debug.Assert(this.key == key, "Relation retrieved from two different keys");
|
|||
|
}
|
|||
|
|
|||
|
internal void Add(AVObject obj)
|
|||
|
{
|
|||
|
var change = new AVRelationOperation(new[] { obj }, null);
|
|||
|
parent.PerformOperation(key, change);
|
|||
|
targetClassName = change.TargetClassName;
|
|||
|
}
|
|||
|
|
|||
|
internal void Remove(AVObject obj)
|
|||
|
{
|
|||
|
var change = new AVRelationOperation(null, new[] { obj });
|
|||
|
parent.PerformOperation(key, change);
|
|||
|
targetClassName = change.TargetClassName;
|
|||
|
}
|
|||
|
|
|||
|
IDictionary<string, object> IJsonConvertible.ToJSON()
|
|||
|
{
|
|||
|
return new Dictionary<string, object> {
|
|||
|
{ "__type", "Relation"},
|
|||
|
{ "className", targetClassName}
|
|||
|
};
|
|||
|
}
|
|||
|
|
|||
|
internal AVQuery<T> GetQuery<T>() where T : AVObject
|
|||
|
{
|
|||
|
if (targetClassName != null)
|
|||
|
{
|
|||
|
return new AVQuery<T>(targetClassName)
|
|||
|
.WhereRelatedTo(parent, key);
|
|||
|
}
|
|||
|
|
|||
|
return new AVQuery<T>(parent.ClassName)
|
|||
|
.RedirectClassName(key)
|
|||
|
.WhereRelatedTo(parent, key);
|
|||
|
}
|
|||
|
|
|||
|
internal AVQuery<T> GetReverseQuery<T>(T target) where T : AVObject
|
|||
|
{
|
|||
|
if (target.ObjectId == null)
|
|||
|
{
|
|||
|
throw new ArgumentNullException("target.ObjectId", "can not query a relation without target ObjectId.");
|
|||
|
}
|
|||
|
|
|||
|
return new AVQuery<T>(parent.ClassName).WhereEqualTo(key, target);
|
|||
|
}
|
|||
|
|
|||
|
internal string TargetClassName
|
|||
|
{
|
|||
|
get
|
|||
|
{
|
|||
|
return targetClassName;
|
|||
|
}
|
|||
|
set
|
|||
|
{
|
|||
|
targetClassName = value;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// Produces the proper AVRelation<T> instance for the given classname.
|
|||
|
/// </summary>
|
|||
|
internal static AVRelationBase CreateRelation(AVObject parent,
|
|||
|
string key,
|
|||
|
string targetClassName)
|
|||
|
{
|
|||
|
var targetType = SubclassingController.GetType(targetClassName) ?? typeof(AVObject);
|
|||
|
|
|||
|
Expression<Func<AVRelation<AVObject>>> createRelationExpr =
|
|||
|
() => CreateRelation<AVObject>(parent, key, targetClassName);
|
|||
|
var createRelationMethod =
|
|||
|
((MethodCallExpression)createRelationExpr.Body)
|
|||
|
.Method
|
|||
|
.GetGenericMethodDefinition()
|
|||
|
.MakeGenericMethod(targetType);
|
|||
|
return (AVRelationBase)createRelationMethod.Invoke(null, new object[] { parent, key, targetClassName });
|
|||
|
}
|
|||
|
|
|||
|
private static AVRelation<T> CreateRelation<T>(AVObject parent, string key, string targetClassName)
|
|||
|
where T : AVObject
|
|||
|
{
|
|||
|
return new AVRelation<T>(parent, key, targetClassName);
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// Provides access to all of the children of a many-to-many relationship. Each instance of
|
|||
|
/// AVRelation is associated with a particular parent and key.
|
|||
|
/// </summary>
|
|||
|
/// <typeparam name="T">The type of the child objects.</typeparam>
|
|||
|
public sealed class AVRelation<T> : AVRelationBase where T : AVObject
|
|||
|
{
|
|||
|
|
|||
|
internal AVRelation(AVObject parent, string key) : base(parent, key) { }
|
|||
|
|
|||
|
internal AVRelation(AVObject parent, string key, string targetClassName)
|
|||
|
: base(parent, key, targetClassName) { }
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// Adds an object to this relation. The object must already have been saved.
|
|||
|
/// </summary>
|
|||
|
/// <param name="obj">The object to add.</param>
|
|||
|
public void Add(T obj)
|
|||
|
{
|
|||
|
base.Add(obj);
|
|||
|
}
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// Removes an object from this relation. The object must already have been saved.
|
|||
|
/// </summary>
|
|||
|
/// <param name="obj">The object to remove.</param>
|
|||
|
public void Remove(T obj)
|
|||
|
{
|
|||
|
base.Remove(obj);
|
|||
|
}
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// Gets a query that can be used to query the objects in this relation.
|
|||
|
/// </summary>
|
|||
|
public AVQuery<T> Query
|
|||
|
{
|
|||
|
get
|
|||
|
{
|
|||
|
return base.GetQuery<T>();
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
}
|