1
Vote

Clean calls in GetMemberAccessExpression

description

Hi, I just runt into a problem where integrating the query to RIA Services. By exposing it to RIA each filter can add .ToLower() to the filter expression for each propery.
Then that is not anymore a expression.NodeType.Is(ExpressionType.MemberAccess) but a expression.NodeType.Is(ExpressionType.Call)
then here it is a small snippet of that method:
        private static MemberExpression GetMemberAccessExpression(AttributeBuilder builder, Expression expression)
        {
            if (expression.NodeType.Is(ExpressionType.MemberAccess))
            {
                // Commented out by Stephen for the purpose of an anonomous object in the query.
                // Example:
                // var query = new { Cn = string.Empty };
                // Users.Where(u => query.Cn == "My Name");

                //MemberExpression memberExpression = (MemberExpression)expression;

                //if (memberExpression.Member.DeclaringType == builder.Parent.GetObjectClassType())
                //{
                //    return memberExpression;
                //}

                // End comment by Stephen

                return (MemberExpression)expression;
            }
            // Cleans up the method call and creates a member expression without the method call:
            // Example: Param.AccountName.ToLower()
            // Param.AccountName
            if (expression.NodeType.Is(ExpressionType.Call))
            {
                var memberExpression = (MemberExpression)((MethodCallExpression)expression).Object;
                if (memberExpression != null)
                {
                    var parameterExpression = (ParameterExpression)memberExpression.Expression;
                    var memberInfo = memberExpression.Member;
                    return Expression.MakeMemberAccess(parameterExpression, memberInfo);
                }
            }
            return null;
        }

file attachments

comments

paulovila wrote Nov 4, 2013 at 7:36 AM

Text - In this comment it is attached the small changes that are need in order to work expressions like:

Users.Where(w=> !w.AccountName.Contains("ext"))
so that the query is translated to account names not containing a text


Users.Where(Expression1).Where(Expression2), so that the query is translated the ldap as Expression1 and Expression2

Bests,

Karaken12 wrote Nov 4, 2013 at 9:35 AM

Hi, Paulovila. Thanks for reporting this issue. Because LDAP queries are (by default) case insensitive, LinqToAD doesn't implement ToLower(). We should change this, but your patch is too broad; it could break existing code.

I haven't used RIA Services, but I think you can set the IsCaseSensitive property to true.
By setting it to true, RIA Services will remove all calls to ToLower(), and it should work as you expect it to, case insensitively. I know that's a little backwards, but that might be the easiest fix for you until we release a new version of LinqToAD.

Best wishes,
Tom

Karaken12 wrote Nov 4, 2013 at 9:39 AM

Thanks for submitting your patch. We will be including the ! functionality soon (hopefully in the next release), so thank you for that. However, query expressions of the form Users.Where(expression1).Where(expression2) should already work, without your patch. Are you having trouble with expressions like this? If so, can you please provide an example?

Thanks very much,
Tom

paulovila wrote Nov 4, 2013 at 3:07 PM

This test fails because it finds 'computer' objects on the search result: (that is why I'm trying Where composition)

Correct me if I'm wrong, the following code is supposed to find only users or persons on the domain (not computers)
        [TestMethod]
        public void CollectionLdapQueryMockTest()
        {
            var context = new MockDirectoryContext(); 
            var r = context.Users.
                Where(w => w.DisplayName.Contains("Let")).
                Take(30).ToList();
            Assert.IsTrue(r.Count < 30);
        }
   /// </summary>
    [DirectoryType("User", "DC=XX,DC=XX")]
    public class LdapUserEntry : UserEntryObject
    {     
        [DirectoryProperty("samaccountname")]
        public string AccountName { get; set; }

        [DirectoryProperty("displayname")]
        public string DisplayName { get; set; }
    
        [DirectoryProperty("givenname")]
        public string GivenName { get; set; }

        [DirectoryProperty("mail")]
        public string Mail { get; set; }
    }
 public class MockDirectoryContext : DirectoryContext
    {
          private IEntrySet<LdapUserEntry> _users;

         public IEntrySet<LdapUserEntry> Users
        {
            get { return _users ?? (_users = CreateEntrySet<LdapUserEntry>()); }
        }
    }

paulovila wrote Nov 4, 2013 at 4:33 PM

By the way thank you for considering the ! , I'll test if I it's possible to have a workaround on the client, but it is giving too much responsibility con the client side. there might be something to securable so that it is not possible to inject arbitrary queries when it is exposed on the net.

Tks