Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Derivedtype support (part 2) #410

Merged
merged 5 commits into from
May 27, 2016
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion RESTier.sln
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio 14
VisualStudioVersion = 14.0.24720.0
VisualStudioVersion = 14.0.25123.0
MinimumVisualStudioVersion = 10.0.40219.1
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{D5E947EB-03CB-4D04-8937-FF2131BB1F04}"
EndProject
Expand Down
2 changes: 2 additions & 0 deletions src/GlobalSuppressions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -119,13 +119,15 @@
[assembly: SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode", Scope = "member", Target = "Microsoft.Restier.Publishers.OData.Model.RestierModelExtender+ModelMapper.#ModelCache")]
[assembly: SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode", Scope = "member", Target = "Microsoft.Restier.Publishers.OData.Model.RestierModelExtender+ModelMapper.#InnerModelMapper")]
[assembly: SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode", Scope = "member", Target = "System.Linq.Expressions.ExpressionHelperMethods.#QueryableCountGeneric")]
[assembly: SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode", Scope = "member", Target = "System.Linq.Expressions.ExpressionHelperMethods.#QueryableOfTypeGeneric")]
[assembly: SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode", Scope = "member", Target = "System.Linq.Expressions.ExpressionHelperMethods.#QueryableWhereGeneric")]
[assembly: SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode", Scope = "member", Target = "System.Linq.Expressions.ExpressionHelpers.#Where(System.Linq.IQueryable,System.Linq.Expressions.LambdaExpression,System.Type)")]
[assembly: SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode", Scope = "member", Target = "System.Linq.Expressions.ExpressionHelpers.#StripQueryMethod(System.Linq.Expressions.Expression,System.String)")]
[assembly: SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode", Scope = "member", Target = "System.Linq.Expressions.ExpressionHelpers.#StripPagingOperators`1(System.Linq.IQueryable`1<!!0>)")]
[assembly: SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode", Scope = "member", Target = "System.Linq.Expressions.ExpressionHelpers.#Count(System.Linq.Expressions.Expression,System.Type)")]
[assembly: SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode", Scope = "member", Target = "System.Linq.Expressions.ExpressionHelpers.#GetCountableQuery`1(System.Linq.IQueryable`1<!!0>)")]
[assembly: SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode", Scope = "member", Target = "System.Linq.Expressions.ExpressionHelpers.#GetSelectExpandElementType(System.Type)")]
[assembly: SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode", Scope = "member", Target = "System.Linq.Expressions.ExpressionHelpers.#OfType(System.Linq.IQueryable,System.Type)")]
[assembly: SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode", Scope = "member", Target = "Microsoft.Restier.Publishers.OData.Results.BaseResult.#EdmType")]
[assembly: SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode", Scope = "member", Target = "Microsoft.Restier.Publishers.OData.Results.BaseResult.#Context")]
[assembly: SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode", Scope = "member", Target = "System.TypeHelper.#GetUnderlyingTypeOrSelf(System.Type)")]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ public Expression Process(QueryExpressionContext context)
}
}

var dataSourceStubReference = context.ModelReference as DataSourceStubReference;
var dataSourceStubReference = context.ModelReference as DataSourceStubModelReference;
if (dataSourceStubReference == null)
{
return null;
Expand Down
6 changes: 3 additions & 3 deletions src/Microsoft.Restier.Core/Query/DefaultQueryHandler.cs
Original file line number Diff line number Diff line change
Expand Up @@ -139,7 +139,7 @@ public override Expression Visit(Expression node)
{
// Only visit the visited node's children if
// the visited node represents API data
if (!(this.context.ModelReference is DataSourceStubReference))
if (!(this.context.ModelReference is DataSourceStubModelReference))
{
// Visit visited node's children
node = base.Visit(visited);
Expand All @@ -150,7 +150,7 @@ public override Expression Visit(Expression node)

// Try to expand the visited node
// if it represents API data
if (this.context.ModelReference is DataSourceStubReference)
if (this.context.ModelReference is DataSourceStubModelReference)
{
node = this.Expand(visited);
}
Expand All @@ -161,7 +161,7 @@ public override Expression Visit(Expression node)

if (visited == node)
{
if (this.context.ModelReference is DataSourceStubReference)
if (this.context.ModelReference is DataSourceStubModelReference)
{
// If no processing occurred on the visited node
// and it represents API data, then it must be
Expand Down
59 changes: 30 additions & 29 deletions src/Microsoft.Restier.Core/Query/QueryExpressionContext.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
using System.Linq.Expressions;
using System.Reflection;
using System.Runtime.CompilerServices;
using Microsoft.OData.Edm;

namespace Microsoft.Restier.Core.Query
{
Expand Down Expand Up @@ -107,27 +108,26 @@ public void PopVisitedNode()
this.UpdateModelReference();
}

private static QueryModelReference ComputeDerivedDataReference(
/// <summary>
/// This method is called by method call like Where/OfType/SelectMany and so on
/// to create a model reference for whole function call.
/// </summary>
private static QueryModelReference ComputeQueryModelReference(
MethodCallExpression methodCall, QueryModelReference source)
{
var method = methodCall.Method;

// source is a sequence of T and output is also a sequence of T
var sourceType = method.GetParameters()[0]
.ParameterType.FindGenericType(typeof(IEnumerable<>));
var resultType = method.ReturnType
.FindGenericType(typeof(IEnumerable<>));
var sourceType = method.GetParameters()[0].ParameterType.FindGenericType(typeof(IEnumerable<>));
var resultType = method.ReturnType.FindGenericType(typeof(IEnumerable<>));
if (sourceType == resultType)
{
return new DerivedDataReference(source);
return new QueryModelReference(source.EntitySet,source.Type);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

space after comma

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed

}

// source is a sequence of T and output is a single T
var sourceElementType = sourceType.GetGenericArguments()[0];
if (method.ReturnType == sourceElementType)
{
return new CollectionElementReference(source);
}
// source is a sequence of T1 and output is a sequence of T2
// Like query People(key)/Trips or People/NS.DerivedPeople
// TODO Null is return in these cases now, need return correct value

// TODO GitHubIssue#29 : Handle projection operators in query expression
return null;
Expand Down Expand Up @@ -174,6 +174,9 @@ private QueryModelReference ComputeModelReference()
QueryModelReference modelReference = null;

var methodCall = this.VisitedNode as MethodCallExpression;
var parameter = this.VisitedNode as ParameterExpression;
var member = this.VisitedNode as MemberExpression;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Personally I feel the previous pattern is more efficient...You don't need to perform three casts at first.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Two options,

  1. like what I do now.
  2. Any if condition is not null, return
    Which one you prefer?

Previous logic does not return, it will go through all logic and last return.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the info. I prefer option 2. Better check each condition then return if fail.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

OK, updated.


if (methodCall != null)
{
var method = methodCall.Method;
Expand All @@ -184,18 +187,14 @@ private QueryModelReference ComputeModelReference()
}
else if (method.GetCustomAttributes<ExtensionAttribute>().Any())
{
var thisModelReference = this.GetModelReferenceForNode(
methodCall.Arguments[0]);
var thisModelReference = this.GetModelReferenceForNode(methodCall.Arguments[0]);
if (thisModelReference != null)
{
modelReference = ComputeDerivedDataReference(
methodCall, thisModelReference);
modelReference = ComputeQueryModelReference(methodCall, thisModelReference);
}
}
}

var parameter = this.VisitedNode as ParameterExpression;
if (parameter != null)
else if (parameter != null)
{
foreach (var node in this.GetExpressionTrail())
{
Expand All @@ -215,33 +214,35 @@ private QueryModelReference ComputeModelReference()
var typeOfT = sourceType.GetGenericArguments()[0];
if (parameter.Type == typeOfT)
{
modelReference = new CollectionElementReference(modelReference);
break;
var collectionType = modelReference.Type as IEdmCollectionType;
if (collectionType != null)
{
modelReference = new QueryModelReference(modelReference.EntitySet, collectionType.ElementType.Definition);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

avoid deep nest and break this line. It's too long. StyleCop will catch this line.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Updated.

break;
}
}
}
}
}
}
}

var member = this.VisitedNode as MemberExpression;
if (member != null)
else if (member != null)
{
modelReference = this.GetModelReferenceForNode(member.Expression);
if (modelReference != null)
{
modelReference = new PropertyDataReference(
modelReference = new PropertyModelReference(
modelReference, member.Member.Name);
}
}

return modelReference;
}

private DataSourceStubReference ComputeDataSourceStubReference(
private DataSourceStubModelReference ComputeDataSourceStubReference(
MethodCallExpression methodCall)
{
DataSourceStubReference modelReference = null;
DataSourceStubModelReference modelReference = null;
ConstantExpression namespaceName = null;
ConstantExpression name = null;
var argumentIndex = 0;
Expand All @@ -258,12 +259,12 @@ private DataSourceStubReference ComputeDataSourceStubReference(
{
if (namespaceName == null)
{
modelReference = new DataSourceStubReference(
modelReference = new DataSourceStubModelReference(
this.QueryContext, nameValue);
}
else
{
modelReference = new DataSourceStubReference(
modelReference = new DataSourceStubModelReference(
this.QueryContext,
namespaceName.Value as string,
nameValue);
Expand Down
124 changes: 43 additions & 81 deletions src/Microsoft.Restier.Core/Query/QueryModelReference.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,42 +10,65 @@ namespace Microsoft.Restier.Core.Query
/// <summary>
/// Represents a reference to query data in terms of a model.
/// </summary>
public abstract class QueryModelReference
public class QueryModelReference
{

private IEdmEntitySet edmEntitySet;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Set these two fields readonly since they will not be changed after ctor.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Updated


private IEdmType edmType;

internal QueryModelReference()
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is the default ctor useful? If not, please consider remove it.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It is used by sub class.Cannot be removed.

{
}

internal QueryModelReference(IEdmEntitySet entitySet, IEdmType type)
{
this.edmEntitySet = entitySet;
this.edmType = type;
}

/// <summary>
/// Gets the entity set that ultimately contains the data.
/// </summary>
public abstract IEdmEntitySet EntitySet { get; }
public virtual IEdmEntitySet EntitySet
{
get
{
return this.edmEntitySet;
}
}

/// <summary>
/// Gets the type of the data, if any.
/// </summary>
public abstract IEdmType Type { get; }
public virtual IEdmType Type
{
get
{
return this.edmType;
}
}
}

/// <summary>
/// Represents a reference to data source stub in terms of a model.
/// </summary>
public class DataSourceStubReference : QueryModelReference
public class DataSourceStubModelReference : QueryModelReference
{
private readonly QueryContext context;
private readonly string namespaceName;
private readonly string name;

/// <summary>
/// Initializes a new instance of the <see cref="DataSourceStubReference" /> class.
/// Initializes a new instance of the <see cref="DataSourceStubModelReference" /> class.
/// </summary>
/// <param name="context">
/// A query context.
/// </param>
/// <param name="name">
/// The name of an entity set, singleton or function import.
/// </param>
public DataSourceStubReference(QueryContext context, string name)
public DataSourceStubModelReference(QueryContext context, string name)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Mark as internal

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Updated

{
Ensure.NotNull(context, "context");
Ensure.NotNull(name, "name");
Expand All @@ -54,7 +77,7 @@ public DataSourceStubReference(QueryContext context, string name)
}

/// <summary>
/// Initializes a new instance of the <see cref="DataSourceStubReference" /> class referring to a function.
/// Initializes a new instance of the <see cref="DataSourceStubModelReference" /> class referring to a function.
/// </summary>
/// <param name="context">
/// A query context.
Expand All @@ -65,7 +88,7 @@ public DataSourceStubReference(QueryContext context, string name)
/// <param name="name">
/// The name of a function.
/// </param>
public DataSourceStubReference(
public DataSourceStubModelReference(
QueryContext context,
string namespaceName,
string name)
Expand Down Expand Up @@ -154,18 +177,26 @@ public IEdmElement Element
}

/// <summary>
/// Represents a reference to derived data in terms of a model.
/// Represents a reference to property data in terms of a model.
/// </summary>
public class DerivedDataReference : QueryModelReference
public class PropertyModelReference : QueryModelReference
{
private readonly string propertyName;

/// <summary>
/// Initializes a new instance of the <see cref="DerivedDataReference" /> class.
/// Initializes a new instance of the <see cref="PropertyModelReference" /> class.
/// </summary>
/// <param name="source">
/// A source query model reference.
/// </param>
public DerivedDataReference(QueryModelReference source)
/// <param name="propertyName">
/// The name of a property.
/// </param>
public PropertyModelReference(QueryModelReference source, string propertyName)
Copy link
Contributor

@lewischeng-ms lewischeng-ms May 27, 2016

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Mark the ctor internal: 1) keep consistent; 2) we don't want user to be able to instantiate this class.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Updated

{
Ensure.NotNull(propertyName, "propertyName");
this.propertyName = propertyName;

Ensure.NotNull(source, "source");
this.Source = source;
}
Expand All @@ -186,75 +217,6 @@ public override IEdmEntitySet EntitySet
}
}

/// <summary>
/// Gets the type of the data.
/// </summary>
public override IEdmType Type
{
get
{
return this.Source.Type;
}
}
}

/// <summary>
/// Represents a reference to element data in terms of a model.
/// </summary>
public class CollectionElementReference : DerivedDataReference
{
/// <summary>
/// Initializes a new instance of the <see cref="CollectionElementReference" /> class.
/// </summary>
/// <param name="source">
/// A source query model reference.
/// </param>
public CollectionElementReference(QueryModelReference source)
: base(source)
{
}

/// <summary>
/// Gets the type of the data.
/// </summary>
public override IEdmType Type
{
get
{
var collectionType = this.Source.Type as IEdmCollectionType;
if (collectionType != null)
{
return collectionType.ElementType.Definition;
}

return null;
}
}
}

/// <summary>
/// Represents a reference to property data in terms of a model.
/// </summary>
public class PropertyDataReference : DerivedDataReference
{
private readonly string propertyName;

/// <summary>
/// Initializes a new instance of the <see cref="PropertyDataReference" /> class.
/// </summary>
/// <param name="source">
/// A source query model reference.
/// </param>
/// <param name="propertyName">
/// The name of a property.
/// </param>
public PropertyDataReference(QueryModelReference source, string propertyName)
: base(source)
{
Ensure.NotNull(propertyName, "propertyName");
this.propertyName = propertyName;
}

/// <summary>
/// Gets the type of the queryable data.
/// </summary>
Expand Down
Loading