-
Notifications
You must be signed in to change notification settings - Fork 137
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
Changes from all commits
0596ad7
027e188
b8e5abd
1da12be
c69ac00
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -7,6 +7,7 @@ | |
using System.Linq.Expressions; | ||
using System.Reflection; | ||
using System.Runtime.CompilerServices; | ||
using Microsoft.OData.Edm; | ||
|
||
namespace Microsoft.Restier.Core.Query | ||
{ | ||
|
@@ -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); | ||
} | ||
|
||
// 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; | ||
|
@@ -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; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Two options,
Previous logic does not return, it will go through all logic and last return. There was a problem hiding this comment. Choose a reason for hiding this commentThe 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. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. OK, updated. |
||
|
||
if (methodCall != null) | ||
{ | ||
var method = methodCall.Method; | ||
|
@@ -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()) | ||
{ | ||
|
@@ -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); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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. There was a problem hiding this comment. Choose a reason for hiding this commentThe 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; | ||
|
@@ -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); | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -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; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Updated |
||
|
||
private IEdmType edmType; | ||
|
||
internal QueryModelReference() | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Is the default ctor useful? If not, please consider remove it. There was a problem hiding this comment. Choose a reason for hiding this commentThe 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) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Mark as internal There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Updated |
||
{ | ||
Ensure.NotNull(context, "context"); | ||
Ensure.NotNull(name, "name"); | ||
|
@@ -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. | ||
|
@@ -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) | ||
|
@@ -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) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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. There was a problem hiding this comment. Choose a reason for hiding this commentThe 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; | ||
} | ||
|
@@ -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> | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
space after comma
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fixed