Skip to content

Commit

Permalink
Further updates based on PR input
Browse files Browse the repository at this point in the history
  • Loading branch information
mikary committed Mar 12, 2015
1 parent b6a59ed commit fd0421c
Show file tree
Hide file tree
Showing 11 changed files with 111 additions and 52 deletions.
8 changes: 8 additions & 0 deletions src/EntityFramework.Core/Properties/Strings.Designer.cs

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 3 additions & 0 deletions src/EntityFramework.Core/Properties/Strings.resx
Original file line number Diff line number Diff line change
Expand Up @@ -468,4 +468,7 @@
<data name="NonGenericOptions" xml:space="preserve">
<value>The DbContextOptions object registered in the service provider must be a DbContextOptions&lt;TContext&gt; where TContext is the type of the DbContext being used.</value>
</data>
<data name="AnnotationNotSupported" xml:space="preserve">
<value>The annotation '{annotation}' is not supported in this context.</value>
</data>
</root>
14 changes: 14 additions & 0 deletions src/EntityFramework.Core/Query/CompiledQueryCache.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
using System.Threading;
using System.Threading.Tasks;
using JetBrains.Annotations;
using Microsoft.Data.Entity.Infrastructure;
using Microsoft.Data.Entity.Internal;
using Microsoft.Data.Entity.Query.ExpressionTreeVisitors;
using Microsoft.Data.Entity.Query.ResultOperators;
Expand Down Expand Up @@ -55,6 +56,19 @@ public virtual TResult Execute<TResult>(
var compiledQuery
= GetOrAdd(query, queryContext, dataStore, isAsync: false, compiler: (q, ds) =>
{
if (query.Type == typeof(ConstantExpression))
{
var annotatable = ((ConstantExpression)query).Value as IAnnotatable;

foreach (var annotation in annotatable.Annotations)
{
if (!dataStore.IsSupported(annotation))
{
throw new InvalidOperationException(Strings.AnnotationNotSupported(annotation.Name));
}
}
}

var queryModel = CreateQueryParser().GetParsedQuery(q);

var streamedSequenceInfo
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
using System.Linq.Expressions;
using System.Reflection;
using JetBrains.Annotations;
using Microsoft.Data.Entity.Infrastructure;
using Microsoft.Data.Entity.Utilities;

namespace Microsoft.Data.Entity.Query.ExpressionTreeVisitors
Expand All @@ -22,12 +23,14 @@ protected override Expression VisitConstantExpression(ConstantExpression constan
if (constantExpression.Type.GetTypeInfo().IsGenericType
&& constantExpression.Type.GetGenericTypeDefinition() == typeof(EntityQueryable<>))
{
return VisitEntityQueryable(((IQueryable)constantExpression.Value).ElementType);
return VisitEntityQueryable(
((IQueryable)constantExpression.Value).ElementType,
(IAnnotatable)constantExpression.Value);
}

return constantExpression;
}

protected abstract Expression VisitEntityQueryable([NotNull] Type elementType);
protected abstract Expression VisitEntityQueryable([NotNull] Type elementType, IAnnotatable annotatable);
}
}
8 changes: 8 additions & 0 deletions src/EntityFramework.Core/Storage/DataStore.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
using System.Threading.Tasks;
using JetBrains.Annotations;
using Microsoft.Data.Entity.ChangeTracking.Internal;
using Microsoft.Data.Entity.Infrastructure;
using Microsoft.Data.Entity.Metadata;
using Microsoft.Data.Entity.Query;
using Microsoft.Data.Entity.Utilities;
Expand Down Expand Up @@ -47,6 +48,13 @@ protected DataStore(

public virtual EntityMaterializerSource EntityMaterializerSource { get; }

public virtual bool IsSupported([NotNull] IAnnotation annotation)
{
Check.NotNull(annotation, nameof(annotation));

return false;
}

public abstract int SaveChanges(IReadOnlyList<InternalEntityEntry> entries);

public abstract Task<int> SaveChangesAsync(
Expand Down
5 changes: 4 additions & 1 deletion src/EntityFramework.Core/Storage/IDataStore.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
using System.Threading.Tasks;
using JetBrains.Annotations;
using Microsoft.Data.Entity.ChangeTracking.Internal;
using Microsoft.Data.Entity.Infrastructure;
using Microsoft.Data.Entity.Metadata;
using Microsoft.Data.Entity.Query;
using Microsoft.Framework.Logging;
Expand All @@ -21,10 +22,12 @@ public interface IDataStore
EntityKeyFactorySource EntityKeyFactorySource { get; }
EntityMaterializerSource EntityMaterializerSource { get; }

bool IsSupported([NotNull] IAnnotation annotation);

int SaveChanges([NotNull] IReadOnlyList<InternalEntityEntry> entries);

Task<int> SaveChangesAsync(
[NotNull] IReadOnlyList<InternalEntityEntry> entries,
[NotNull] IReadOnlyList<InternalEntityEntry> entries,
CancellationToken cancellationToken = default(CancellationToken));

Func<QueryContext, IEnumerable<TResult>> CompileQuery<TResult>([NotNull] QueryModel queryModel);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
using System.Reflection;
using JetBrains.Annotations;
using Microsoft.Data.Entity.ChangeTracking.Internal;
using Microsoft.Data.Entity.Infrastructure;
using Microsoft.Data.Entity.Metadata;
using Microsoft.Data.Entity.Query;
using Microsoft.Data.Entity.Query.ExpressionTreeVisitors;
Expand Down Expand Up @@ -186,7 +187,7 @@ public InMemoryEntityQueryableExpressionTreeVisitor(

private new InMemoryQueryModelVisitor QueryModelVisitor => (InMemoryQueryModelVisitor)base.QueryModelVisitor;

protected override Expression VisitEntityQueryable(Type elementType)
protected override Expression VisitEntityQueryable(Type elementType, IAnnotatable annotatable)
{
Check.NotNull(elementType, nameof(elementType));

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,8 @@
using System.Data.Common;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
using JetBrains.Annotations;
using Microsoft.Data.Entity.Infrastructure;
using Microsoft.Data.Entity.Metadata;
using Microsoft.Data.Entity.Query;
using Microsoft.Data.Entity.Query.ExpressionTreeVisitors;
using Microsoft.Data.Entity.Relational.Query.Expressions;
Expand Down Expand Up @@ -71,54 +69,11 @@ protected override Expression VisitMethodCallExpression([NotNull] MethodCallExpr
return base.VisitMethodCallExpression(methodCallExpression);
}

protected override Expression VisitConstantExpression(ConstantExpression constantExpression)
{
Check.NotNull(constantExpression, nameof(constantExpression));

if (constantExpression.Type.GetTypeInfo().IsGenericType
&& constantExpression.Type.GetGenericTypeDefinition() == typeof(EntityQueryable<>))
{
var sql = ((IAnnotatable)constantExpression.Value)["Sql"];

if (sql != null)
{
return VisitRawSqlQueryable(((IQueryable)constantExpression.Value).ElementType, sql);
}
}

return base.VisitConstantExpression(constantExpression);
}

protected override Expression VisitEntityQueryable(Type elementType)
{
Check.NotNull(elementType, nameof(elementType));

return CreateQuery(elementType,
(entityType, tableName, alias) =>
new TableExpression(
tableName,
QueryModelVisitor.QueryCompilationContext.GetSchema(entityType),
alias,
_querySource));
}

protected virtual Expression VisitRawSqlQueryable(Type elementType, string sql)
protected override Expression VisitEntityQueryable(Type elementType, IAnnotatable annotatable)
{
Check.NotNull(elementType, nameof(elementType));
Check.NotNull(sql, nameof(sql));
Check.NotNull(annotatable, nameof(annotatable));

return CreateQuery(elementType,
(entityType, tableName, alias) =>
new RawSqlDerivedTableExpression(
sql,
alias,
_querySource));
}

private Expression CreateQuery(
Type elementType,
Func<IEntityType, string, string, TableExpressionBase> createTableExpression)
{
var queryMethodInfo = RelationalQueryModelVisitor.CreateValueReaderMethodInfo;
var entityType = QueryModelVisitor.QueryCompilationContext.Model.GetEntityType(elementType);

Expand All @@ -129,7 +84,23 @@ private Expression CreateQuery(
? tableName.First().ToString().ToLower()
: _querySource.ItemName;

selectExpression.AddTable(createTableExpression(entityType, tableName, alias));
if (annotatable["Sql"] != null)
{
selectExpression.AddTable(
new RawSqlDerivedTableExpression(
annotatable["Sql"],
alias,
_querySource));
}
else
{
selectExpression.AddTable(
new TableExpression(
tableName,
QueryModelVisitor.QueryCompilationContext.GetSchema(entityType),
alias,
_querySource));
}

QueryModelVisitor.AddQuery(_querySource, selectExpression);

Expand Down
12 changes: 12 additions & 0 deletions src/EntityFramework.Relational/RelationalDataStore.cs
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,18 @@ protected RelationalDataStore(

public virtual IDbContextOptions DbContextOptions => _options;

public override bool IsSupported([NotNull] IAnnotation annotation)
{
Check.NotNull(annotation, nameof(annotation));

if (base.IsSupported(annotation))
{
return true;
}

return annotation.Name.Equals("Sql");
}

public override int SaveChanges(
IReadOnlyList<InternalEntityEntry> entries)
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,27 @@ FROM Customers
entryCount: 6);
}

[Fact]
public virtual void From_sql_annotations_do_not_modify_successive_calls()
{
using (var context = CreateContext())
{
TestHelpers.AssertResults(
NorthwindData.Set<Customer>().Where(c => c.ContactName.Contains("z")).ToArray(),
context.Customers.FromSql("SELECT * FROM Customers WHERE Customers.ContactName LIKE '%z%'").ToArray(),
assertOrder: false);

Assert.Equal(14, context.ChangeTracker.Entries().Count());

TestHelpers.AssertResults(
NorthwindData.Set<Customer>().ToArray(),
context.Customers.ToArray(),
assertOrder: false);

Assert.Equal(91, context.ChangeTracker.Entries().Count());
}
}

protected NorthwindContext CreateContext()
{
return Fixture.CreateContext();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,21 @@ FROM Customers
Sql);
}

public override void From_sql_annotations_do_not_modify_successive_calls()
{
base.From_sql_annotations_do_not_modify_successive_calls();

Assert.Equal(
@"SELECT [c].[Address], [c].[City], [c].[CompanyName], [c].[ContactName], [c].[ContactTitle], [c].[Country], [c].[CustomerID], [c].[Fax], [c].[Phone], [c].[PostalCode], [c].[Region]
FROM (
SELECT * FROM Customers WHERE Customers.ContactName LIKE '%z%'
) AS [c]
SELECT [c].[Address], [c].[City], [c].[CompanyName], [c].[ContactName], [c].[ContactTitle], [c].[Country], [c].[CustomerID], [c].[Fax], [c].[Phone], [c].[PostalCode], [c].[Region]
FROM [Customers] AS [c]",
Sql);
}

public FromSqlQuerySqlServerTest(NorthwindQuerySqlServerFixture fixture)
: base(fixture)
{
Expand Down

0 comments on commit fd0421c

Please sign in to comment.