# 八、扩展实体框架

## 调用数据库函数

LINQ 为一些常见的数据库功能提供了操作符，但是如果您想调用另一个不受支持的操作符会发生什么？

例如，在 SQL Server 中，我们可以定义一个名为 CalculateValue 的函数，该函数将字符串作为其唯一参数，并基于该参数返回一个整数。

```cs
  CREATE FUNCTION
  CalculateValue
  (
         @parameter NVARCHAR
  )
  RETURNS INT
  AS
  BEGIN
         -- roll out your own implementation

         RETURN 0
  END

```

可以在 LINQ 查询中调用这个函数；为此，我们需要声明一个扩展方法，并用[edfmfunctionattribute](http://msdn.microsoft.com/en-us/library/system.data.objects.dataclasses.edmfunctionattribute.aspx)修饰它。这告诉实体框架，它应该用给定的名称和签名调用一个数据库函数，并将扩展方法参数传递给它。我们来看一个例子。

```cs
public static class StringExtensions
{
  [EdmFunction("SqlServer", "CalculateValue")]
  public static Int32 CalculateValue(this String phrase)
  {
    throw (new NotImplementedException());
  }
}

```

[EdfFunctionAttribute](http://msdn.microsoft.com/en-us/library/system.data.objects.dataclasses.edmfunctionattribute.aspx)的第一个参数是一个名称空间，但是只要使用你喜欢的就可以了。第二个是函数名，以防它与。NET 方法名。这个实现抛出了一个异常，因为它不是直接调用的，而是在数据库上调用的。

```cs
var value = ctx.Projects.Select(x => x.Name.CalculateValue()).ToList();

```

您可以使用这种技术来调用其他函数，包括您定义的函数，但是这不适用于表值函数(TVL)，只能用于标量函数。此外，您只能传递基本类型的参数，而不能传递实体或复杂类型的参数。

| ![](img/note.png) | 注意:这与[函数](http://msdn.microsoft.com/en-us/library/system.data.objects.sqlclient.sqlfunctions.aspx)和[实体函数](http://msdn.microsoft.com/en-us/library/system.data.objects.entityfunctions.aspx)类上的方法使用的技术完全相同。 |

## 实施 LINQ 推广方法

另一种技术包括利用 LINQ 表达式从扩展方法构建复杂的查询。

之间的 SQL 运算符没有对应的 LINQ 表达式。我们可以在 LINQ 表达式中同时使用两个条件，一个用于范围的低端(> X)和高端(< Y)。我们还可以实现一个 LINQ 扩展方法，用一个表达式为我们提供这个功能。

```cs
public static class QueryableExtensions
{
  public static IQueryable<TSource> Between<TSource, TKey>(
    this IQueryable<TSource> source, 
    Expression<Func<TSource, TKey>> property, TKey low, TKey high
  ) where TKey : IComparable<TKey>
  {
    var sourceParameter = Expression.Parameter(typeof(TSource));
    var body = property.Body;
    var parameter = property.Parameters[0];
    var compareMethod = typeof(TKey).GetMethod("CompareTo", 
      new Type[] { typeof(TKey) });
    var zero = Expression.Constant(0, typeof(Int32));
    var upper = Expression.LessThanOrEqual(Expression.Call(body, compareMethod,
      Expression.Constant(high)), zero);
    var lower = Expression.GreaterThanOrEqual(Expression.Call(body, compareMethod, 
      Expression.Constant(low)), zero);
    var andExpression = Expression.AndAlso(upper, lower); 
    var whereCallExpression = Expression.Call
    (
      typeof(Queryable),
      "Where",
      new Type[] { source.ElementType },
      source.Expression,
      Expression.Lambda<Func<TSource, Boolean>>(andExpression, 
        new ParameterExpression[] { parameter })
    );

    return (source.Provider.CreateQuery<TSource>(whereCallExpression));
  }
}

```

为了更好地理解这是如何实现的，理解 LINQ 表达式至关重要。网上有一些不错的链接。这项技术虽然掌握起来很复杂，但潜力巨大，引起了很多关注。

这是 [IQueryable < T >](http://msdn.microsoft.com/en-us/library/bb351562.aspx) 上的一个扩展方法，可以这样使用。

```cs
//get projects starting between two dates
var projectsBetweenTodayAndTheDayBefore = ctx.Projects
.Between(x => x.Start, DateTime.Today.AddDays(-1), DateTime.Today).ToList();

//projects with 10 to 20 resources
var projectsWithTwoOrThreeResources = ctx.Projects.Select(x => 
new { x.Name, ResourceCount = x.ProjectResources.Count() })
.Between(x => x.ResourceCount, 10, 20).ToList();

```

LINQ 提供者将愉快地咀嚼新的表达式，并将其翻译成适当的 SQL。