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

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

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

例如

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()。

最佳答案

对于那些感兴趣的人。 我对缺少此功能感到非常生气,因此我使用 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
       }

关于c# - 使用 Entity Framework 5 在 SQL Server 中连接字符串和数字的最佳方法?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/18590880/

相关文章:

c# - 将节点添加到 TreeView 的问题

c# - 最快的 C# 输出解析器生成器工具是什么?

entity-framework - OnDelete 也删除文件

c# - EF Core 左连接计数

entity-framework - Entity Framework 5 中的 "soft deleting"

entity-framework - 是否可以将 EF 迁移仅应用于本地/内存数据?

c# - Include 内使用的 Lambda 表达式无效

c# - ReSharper 好奇号 : "Parameter is only used for precondition check(s)."

sql-server - 类型提供程序无法连接到 localDB

c# - Entity Framework 添加和删除实体?