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

Modify query loader the way it generates parametrized SQL queries #163

Open
wants to merge 2 commits into
base: develop
Choose a base branch
from
Open
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
63 changes: 63 additions & 0 deletions GraphDiff/GraphDiff.Tests/Tests/QueryLoaderBehaviours.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,9 @@
using System.Data.Entity.Infrastructure;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using RefactorThis.GraphDiff.Tests.Models;
using System;
using System.Data.Entity;
using System.Linq;

namespace RefactorThis.GraphDiff.Tests.Tests
{
Expand Down Expand Up @@ -95,5 +98,65 @@ public void ShouldPerformMutlipleQueriesWhenRequested()

// TODO how do I test number of queries..
}

[TestMethod]
public void ShouldPrametrizeLoadingQuery()
{
using (var context = new TestDbContext())
{
context.Set<OneToOneOneToOneAssociatedModel>().Add(_oneToOneAssociated);
context.Set<OneToOneOneToManyAssociatedModel>().Add(_oneToManyAssociated);
context.Nodes.Add(_node);
context.SaveChanges();
}

var x = new LocalDbConnectionFactory("v11.0");
var connection = x.CreateConnection("GraphDiff");

using (var context = new TestDbContext(connection))
{
using (var logCollector = new QueryLogCollector(context))
{
// Setup mapping
context.UpdateGraph(
entity: _node,
mapping: map => map.OwnedEntity(p => p.OneToOneOwned, with => with
.OwnedEntity(p => p.OneToOneOneToOneOwned)
.AssociatedEntity(p => p.OneToOneOneToOneAssociated)
.OwnedCollection(p => p.OneToOneOneToManyOwned)
.AssociatedCollection(p => p.OneToOneOneToManyAssociated)),
updateParams: new UpdateParams { QueryMode = QueryMode.SingleQuery });

Assert.IsTrue(logCollector.Logs.Any(l => l.Contains("@p__linq__0")),
"Can't find a parameter in the loading query");
}
}

// TODO how do I test number of queries..
}
}

internal class QueryLogCollector : IDisposable
{
private readonly DbContext _context;
private readonly Action<string> _originalLogger;
private readonly List<string> _logs = new List<string>();

public List<string> Logs
{
get { return _logs; }
}

public QueryLogCollector(DbContext context)
{
_context = context;
_originalLogger = _context.Database.Log;
_context.Database.Log = line => _logs.Add(line);
}

public void Dispose()
{
_context.Database.Log = _originalLogger;
}
}
}
26 changes: 25 additions & 1 deletion GraphDiff/GraphDiff/Internal/QueryLoader.cs
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,31 @@ public QueryLoader(DbContext context, IEntityManager entityManager)

private static Expression CreateEqualsExpression(object entity, PropertyInfo keyProperty, Expression parameter)
{
return Expression.Equal(Expression.Property(parameter, keyProperty), Expression.Constant(keyProperty.GetValue(entity, null), keyProperty.PropertyType));
return Expression.Equal(Expression.Property(parameter, keyProperty),
ExpressionParameterHelper.GetParameter(keyProperty.GetValue(entity, null),
keyProperty.PropertyType));
}
}

internal class ExpressionParameterHelper
{
public static MemberExpression GetParameter(object value, Type type)
{
MethodInfo method = typeof(ExpressionParameterHelper).GetMethod("GetParameterInternal", BindingFlags.Static | BindingFlags.NonPublic);
MethodInfo genericMethod = method.MakeGenericMethod(type);
return (MemberExpression)genericMethod.Invoke(null, new object[] { value });
}

private static MemberExpression GetParameterInternal<TValue>(TValue value)
{
var closure = new ExpressionParameterField<TValue> { Value = value };
return Expression.Field(Expression.Constant(closure), "Value");
}

private class ExpressionParameterField<T>
{
public T Value;
}
}

}