使用Entity Framework 5在SQL Server中连接字符串和数字的最佳方法?

时间:2013-09-03 11:15:52

标签: c# entity-framework entity-framework-5 expression-trees

出于某种原因,微软决定不支持EF5中的简单连接。

e.g。

Select(foo => new 
           {
             someProp = "hello" + foo.id + "/" + foo.bar
           }

如果foo.id或foo.bar是数字,则会抛出。

我发现的解决方法显然是代码:

Select(foo => new 
           {
             someProp = "hello" + 
             SqlFunctions.StringConvert((double?)foo.id).Trim()  + 
             "/" + 
             SqlFunctions.StringConvert((double?)foo.bar).Trim() 
           }

哪种方法很好,但看起来很可怕。

那么,有没有一些体面的方法来实现这个更清洁的代码? 我对这个客户端不感兴趣,所以没有.AsEnumerable()请回答。

1 个答案:

答案 0 :(得分:3)

对于那些感兴趣的人。 我对这个功能的缺乏感到很生气,我自己使用ExpressionVisitor实现了它。

您现在可以编写原始问题中的代码。

using System;
using System.Collections;
using System.Collections.Generic;
using System.Data.Objects.SqlClient;
using System.Linq;
using System.Linq.Expressions;

namespace Crawlr.Web.Code
{
    public static class ObjectSetExExtensions
    {
        public static ObjectSetEx<T> Extend<T>(this IQueryable<T> self) where T : class
        {
            return new ObjectSetEx<T>(self);
        }
    }

    public class ObjectSetEx<T> : IOrderedQueryable<T>
    {
        private readonly QueryProviderEx provider;
        private readonly IQueryable<T> source;

        public ObjectSetEx(IQueryable<T> source)
        {
            this.source = source;
            provider = new QueryProviderEx(this.source.Provider);
        }

        #region IQueryableEx<T> Members

        public IEnumerator<T> GetEnumerator()
        {
            return source.GetEnumerator();
        }

        IEnumerator IEnumerable.GetEnumerator()
        {
            return source.GetEnumerator();
        }

        public Type ElementType
        {
            get { return source.ElementType; }
        }

        public Expression Expression
        {
            get { return source.Expression; }
        }

        public IQueryProvider Provider
        {
            get { return provider; }
        }
        #endregion
    }

    public class QueryProviderEx : IQueryProvider
    {
        private readonly IQueryProvider source;

        public QueryProviderEx(IQueryProvider source)
        {
            this.source = source;
        }

        #region IQueryProvider Members

        public IQueryable<TElement> CreateQuery<TElement>(Expression expression)
        {
            Expression newExpression = ExpressionReWriterVisitor.Default.Visit(expression);
            IQueryable<TElement> query = source.CreateQuery<TElement>(newExpression);
            return new ObjectSetEx<TElement>(query);
        }

        public IQueryable CreateQuery(Expression expression)
        {
            Expression newExpression = ExpressionReWriterVisitor.Default.Visit(expression);
            IQueryable query = source.CreateQuery(newExpression);
            return query;
        }

        public TResult Execute<TResult>(Expression expression)
        {
            Expression newExpression = ExpressionReWriterVisitor.Default.Visit(expression);
            return source.Execute<TResult>(newExpression);
        }

        public object Execute(Expression expression)
        {
            Expression newExpression = ExpressionReWriterVisitor.Default.Visit(expression);
            return source.Execute(newExpression);
        }

        #endregion
    }

    public class ExpressionReWriterVisitor : ExpressionVisitor
    {
        public static readonly ExpressionReWriterVisitor Default = new ExpressionReWriterVisitor();

        protected override Expression VisitUnary(UnaryExpression node)
        {
            if (node.NodeType == ExpressionType.Convert && node.Operand.Type == typeof(int) && node.Type==typeof(object))
            {
                var operand = node.Operand;
                var stringConvertMethod = typeof(SqlFunctions).GetMethod("StringConvert", new Type[] { typeof(double?) });
                var trimMethod = typeof(string).GetMethod("Trim",new Type[] {});


                var dOperand = Expression.Convert(operand, typeof(double?));
                return Expression.Call(Expression.Call(stringConvertMethod, dOperand),trimMethod);
            }

            return base.VisitUnary(node);
        }      
    }
}

用法:

  var res = model
  .FooSet
  .Extend() //<- applies the magic
  .Select(foo => new 
       {
         someProp = "hello" + foo.id + "/" + foo.bar
       }