You've already forked linux-packaging-mono
Imported Upstream version 4.6.0.125
Former-commit-id: a2155e9bd80020e49e72e86c44da02a8ac0e57a4
This commit is contained in:
parent
a569aebcfd
commit
e79aa3c0ed
291
mcs/class/referencesource/System.Data.Linq/DataShape.cs
Normal file
291
mcs/class/referencesource/System.Data.Linq/DataShape.cs
Normal file
@@ -0,0 +1,291 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
using System.Reflection;
|
||||
using System.Linq;
|
||||
using System.Linq.Expressions;
|
||||
using System.Collections;
|
||||
using System.Data.Linq.SqlClient;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
|
||||
namespace System.Data.Linq {
|
||||
sealed public class DataLoadOptions {
|
||||
bool frozen;
|
||||
Dictionary<MetaPosition, MemberInfo> includes = new Dictionary<MetaPosition, MemberInfo>();
|
||||
Dictionary<MetaPosition, LambdaExpression> subqueries = new Dictionary<MetaPosition, LambdaExpression>();
|
||||
|
||||
/// <summary>
|
||||
/// Describe a property that is automatically loaded when the containing instance is loaded
|
||||
/// </summary>
|
||||
[SuppressMessage("Microsoft.Design", "CA1006:DoNotNestGenericTypesInMemberSignatures", Justification = "[....]: Generic types are an important part of Linq APIs and they could not exist without nested generic support.")]
|
||||
[SuppressMessage("Microsoft.Design", "CA1011:ConsiderPassingBaseTypesAsParameters", Justification = "[....]: Need to provide static typing.")]
|
||||
public void LoadWith<T>(Expression<Func<T, object>> expression) {
|
||||
if (expression == null) {
|
||||
throw Error.ArgumentNull("expression");
|
||||
}
|
||||
MemberInfo mi = GetLoadWithMemberInfo(expression);
|
||||
this.Preload(mi);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Describe a property that is automatically loaded when the containing instance is loaded
|
||||
/// </summary>
|
||||
public void LoadWith(LambdaExpression expression) {
|
||||
if (expression == null) {
|
||||
throw Error.ArgumentNull("expression");
|
||||
}
|
||||
MemberInfo mi = GetLoadWithMemberInfo(expression);
|
||||
this.Preload(mi);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Place a subquery on the given association.
|
||||
/// </summary>
|
||||
[SuppressMessage("Microsoft.Design", "CA1006:DoNotNestGenericTypesInMemberSignatures", Justification = "[....]: Generic types are an important part of Linq APIs and they could not exist without nested generic support.")]
|
||||
[SuppressMessage("Microsoft.Design", "CA1011:ConsiderPassingBaseTypesAsParameters", Justification = "[....]: Need to provide static typing.")]
|
||||
public void AssociateWith<T>(Expression<Func<T, object>> expression) {
|
||||
if (expression == null) {
|
||||
throw Error.ArgumentNull("expression");
|
||||
}
|
||||
this.AssociateWithInternal(expression);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Place a subquery on the given association.
|
||||
/// </summary>
|
||||
public void AssociateWith(LambdaExpression expression) {
|
||||
if (expression == null) {
|
||||
throw Error.ArgumentNull("expression");
|
||||
}
|
||||
this.AssociateWithInternal(expression);
|
||||
}
|
||||
|
||||
private void AssociateWithInternal(LambdaExpression expression) {
|
||||
// Strip the cast-to-object.
|
||||
Expression op = expression.Body;
|
||||
while (op.NodeType == ExpressionType.Convert || op.NodeType == ExpressionType.ConvertChecked) {
|
||||
op = ((UnaryExpression)op).Operand;
|
||||
}
|
||||
LambdaExpression lambda = Expression.Lambda(op, expression.Parameters.ToArray());
|
||||
MemberInfo mi = Searcher.MemberInfoOf(lambda);
|
||||
this.Subquery(mi, lambda);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Determines if the member is automatically loaded with its containing instances.
|
||||
/// </summary>
|
||||
/// <param name="member">The member this is automatically loaded.</param>
|
||||
/// <returns>True if the member is automatically loaded.</returns>
|
||||
internal bool IsPreloaded(MemberInfo member) {
|
||||
if (member == null) {
|
||||
throw Error.ArgumentNull("member");
|
||||
}
|
||||
return includes.ContainsKey(new MetaPosition(member));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Two shapes are equivalent if any of the following are true:
|
||||
/// (1) They are the same object instance
|
||||
/// (2) They are both null or empty
|
||||
/// (3) They contain the same preloaded members
|
||||
/// </summary>
|
||||
internal static bool ShapesAreEquivalent(DataLoadOptions ds1, DataLoadOptions ds2) {
|
||||
bool shapesAreSameOrEmpty = (ds1 == ds2) || ((ds1 == null || ds1.IsEmpty) && (ds2 == null || ds2.IsEmpty));
|
||||
if (!shapesAreSameOrEmpty) {
|
||||
if (ds1 == null || ds2 == null || ds1.includes.Count != ds2.includes.Count) {
|
||||
return false;
|
||||
}
|
||||
|
||||
foreach (MetaPosition metaPosition in ds2.includes.Keys) {
|
||||
if (!ds1.includes.ContainsKey(metaPosition)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the subquery expression associated with the member.
|
||||
/// </summary>
|
||||
/// <param name="member">The member with the subquery.</param>
|
||||
/// <returns></returns>
|
||||
internal LambdaExpression GetAssociationSubquery(MemberInfo member) {
|
||||
if (member == null) {
|
||||
throw Error.ArgumentNull("member");
|
||||
}
|
||||
LambdaExpression expression = null;
|
||||
subqueries.TryGetValue(new MetaPosition(member), out expression);
|
||||
return expression;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Freeze the shape. Any further attempts to modify the shape will result in
|
||||
/// an exception.
|
||||
/// </summary>
|
||||
internal void Freeze() {
|
||||
this.frozen = true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Describe a property that is automatically loaded when the containing instance is loaded
|
||||
/// </summary>
|
||||
internal void Preload(MemberInfo association) {
|
||||
if (association == null) {
|
||||
throw Error.ArgumentNull("association");
|
||||
}
|
||||
if (this.frozen) {
|
||||
throw Error.IncludeNotAllowedAfterFreeze();
|
||||
}
|
||||
this.includes.Add(new MetaPosition(association), association);
|
||||
ValidateTypeGraphAcyclic();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Place a subquery on the given association.
|
||||
/// </summary>
|
||||
private void Subquery(MemberInfo association, LambdaExpression subquery) {
|
||||
if (this.frozen) {
|
||||
throw Error.SubqueryNotAllowedAfterFreeze();
|
||||
}
|
||||
subquery = (LambdaExpression)System.Data.Linq.SqlClient.Funcletizer.Funcletize(subquery); // Layering violation.
|
||||
ValidateSubqueryMember(association);
|
||||
ValidateSubqueryExpression(subquery);
|
||||
this.subqueries[new MetaPosition(association)] = subquery;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// If the lambda specified is of the form p.A, where p is the parameter
|
||||
/// and A is a member on p, the MemberInfo for A is returned. If
|
||||
/// the expression is not of this form, an exception is thrown.
|
||||
/// </summary>
|
||||
private static MemberInfo GetLoadWithMemberInfo(LambdaExpression lambda)
|
||||
{
|
||||
// When the specified member is a value type, there will be a conversion
|
||||
// to object that we need to strip
|
||||
Expression body = lambda.Body;
|
||||
if (body != null && (body.NodeType == ExpressionType.Convert || body.NodeType == ExpressionType.ConvertChecked))
|
||||
{
|
||||
body = ((UnaryExpression)body).Operand;
|
||||
}
|
||||
|
||||
MemberExpression mex = body as MemberExpression;
|
||||
if (mex != null && mex.Expression.NodeType == ExpressionType.Parameter)
|
||||
{
|
||||
return mex.Member;
|
||||
}
|
||||
else
|
||||
{
|
||||
throw Error.InvalidLoadOptionsLoadMemberSpecification();
|
||||
}
|
||||
}
|
||||
|
||||
private static class Searcher {
|
||||
static internal MemberInfo MemberInfoOf(LambdaExpression lambda) {
|
||||
Visitor v = new Visitor();
|
||||
v.VisitLambda(lambda);
|
||||
return v.MemberInfo;
|
||||
}
|
||||
private class Visitor : System.Data.Linq.SqlClient.ExpressionVisitor {
|
||||
internal MemberInfo MemberInfo;
|
||||
internal override Expression VisitMemberAccess(MemberExpression m) {
|
||||
this.MemberInfo = m.Member;
|
||||
return base.VisitMemberAccess(m);
|
||||
}
|
||||
|
||||
internal override Expression VisitMethodCall(MethodCallExpression m) {
|
||||
this.Visit(m.Object);
|
||||
foreach (Expression arg in m.Arguments) {
|
||||
this.Visit(arg);
|
||||
break; // Only follow the extension method 'this'
|
||||
}
|
||||
return m;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
private void ValidateTypeGraphAcyclic() {
|
||||
IEnumerable<MemberInfo> edges = this.includes.Values;
|
||||
int removed = 0;
|
||||
|
||||
for (int loop = 0; loop < this.includes.Count; ++loop) {
|
||||
// Build a list of all edge targets.
|
||||
HashSet<Type> edgeTargets = new HashSet<Type>();
|
||||
foreach (MemberInfo edge in edges) {
|
||||
edgeTargets.Add(GetIncludeTarget(edge));
|
||||
}
|
||||
// Remove all edges with sources matching no target.
|
||||
List<MemberInfo> newEdges = new List<MemberInfo>();
|
||||
bool someRemoved = false;
|
||||
foreach (MemberInfo edge in edges) {
|
||||
if (edgeTargets.Where(et=>et.IsAssignableFrom(edge.DeclaringType) || edge.DeclaringType.IsAssignableFrom(et)).Any()) {
|
||||
newEdges.Add(edge);
|
||||
}
|
||||
else {
|
||||
++removed;
|
||||
someRemoved = true;
|
||||
if (removed == this.includes.Count)
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (!someRemoved) {
|
||||
throw Error.IncludeCycleNotAllowed(); // No edges removed, there must be a loop.
|
||||
}
|
||||
edges = newEdges;
|
||||
}
|
||||
throw new InvalidOperationException("Bug in ValidateTypeGraphAcyclic"); // Getting here means a bug.
|
||||
}
|
||||
|
||||
private static Type GetIncludeTarget(MemberInfo mi) {
|
||||
Type mt = System.Data.Linq.SqlClient.TypeSystem.GetMemberType(mi);
|
||||
if (mt.IsGenericType) {
|
||||
return mt.GetGenericArguments()[0];
|
||||
}
|
||||
return mt;
|
||||
}
|
||||
|
||||
private static void ValidateSubqueryMember(MemberInfo mi) {
|
||||
Type memberType = System.Data.Linq.SqlClient.TypeSystem.GetMemberType(mi);
|
||||
if (memberType == null) {
|
||||
throw Error.SubqueryNotSupportedOn(mi);
|
||||
}
|
||||
if (!typeof(IEnumerable).IsAssignableFrom(memberType)) {
|
||||
throw Error.SubqueryNotSupportedOnType(mi.Name, mi.DeclaringType);
|
||||
}
|
||||
}
|
||||
|
||||
private static void ValidateSubqueryExpression(LambdaExpression subquery) {
|
||||
if (!typeof(IEnumerable).IsAssignableFrom(subquery.Body.Type)) {
|
||||
throw Error.SubqueryMustBeSequence();
|
||||
}
|
||||
new SubqueryValidator().VisitLambda(subquery);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Ensure that the subquery follows the rules for subqueries.
|
||||
/// </summary>
|
||||
private class SubqueryValidator : System.Data.Linq.SqlClient.ExpressionVisitor {
|
||||
bool isTopLevel = true;
|
||||
internal override Expression VisitMethodCall(MethodCallExpression m) {
|
||||
bool was = isTopLevel;
|
||||
try {
|
||||
if (isTopLevel && !SubqueryRules.IsSupportedTopLevelMethod(m.Method))
|
||||
throw Error.SubqueryDoesNotSupportOperator(m.Method.Name);
|
||||
isTopLevel = false;
|
||||
return base.VisitMethodCall(m);
|
||||
}
|
||||
finally {
|
||||
isTopLevel = was;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Whether there have been LoadOptions specified.
|
||||
/// </summary>
|
||||
internal bool IsEmpty {
|
||||
get { return this.includes.Count == 0 && this.subqueries.Count == 0; }
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user