在C#中使用EF Core查询时,如果需要调用数据库中的自定义函数(如标量函数或表值函数),可以通过映射函数到模型并使用
FromSqlRaw或
HasDbFunction来实现。下面介绍两种主要方式:映射数据库函数和在LINQ中调用。
1. 映射数据库标量函数(Scalar Function)
假设你在SQL Server中有一个标量函数:
CREATE FUNCTION dbo.CalculateDiscount(@price DECIMAL(18,2), @rate DECIMAL(3,2)) RETURNS DECIMAL(18,2) AS BEGIN RETURN @price * (1 - @rate) END你可以在EF Core的
DbContext中映射这个函数:
步骤:
在DbContext中定义一个静态方法,并用
[DbFunction]标记 确保该方法名与数据库函数名一致(或指定名称) public class AppDbContext : DbContext { [DbFunction("CalculateDiscount", Schema = "dbo")] public static decimal CalculateDiscount(decimal price, decimal rate) { // 方法体不需要实现,EF Core会生成SQL调用 throw new NotSupportedException(); }
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
// 可选:显式配置函数名(如果方法名不同)
modelBuilder.HasDbFunction(typeof(AppDbContext).GetMethod(nameof(CalculateDiscount)));
} }
然后在LINQ查询中使用:
var result = context.Products .Select(p => new { Name = p.Name, DiscountedPrice = CalculateDiscount(p.Price, 0.1m) }) .ToList();EF Core会生成类似
SELECT Name, dbo.CalculateDiscount(Price, 0.1) AS DiscountedPrice FROM Products的SQL。
2. 调用表值函数(Table-Valued Function)
如果函数返回的是结果集(例如TVF),可以使用
FromSqlRaw直接调用。
例如SQL Server中的TVF:
CREATE FUNCTION dbo.GetProductsByCategory(@categoryId INT) RETURNS TABLE AS RETURN ( SELECT Id, Name, Price FROM Products WHERE CategoryId = @categoryId )你需要定义一个只读实体类来接收结果:
public class ProductSummary { public int Id { get; set; } public string Name { get; set; } public decimal Price { get; set; } }然后通过
FromSqlRaw调用: var products = context.Set
3. 使用 HasDbFunction 显式配置(推荐方式)
除了属性方式,也可以在
OnModelCreating中更灵活地配置: protected override void OnModelCreating(ModelBuilder modelBuilder) { modelBuilder.HasDbFunction(typeof(AppDbContext).GetMethod(nameof(CalculateDiscount))) .HasName("CalculateDiscount") .HasSchema("dbo"); }
这种方式便于集中管理,也支持重命名。
注意事项
标量函数必须是static且返回类型匹配 方法体应抛出
NotSupportedException,防止被意外调用 仅支持可翻译成SQL的函数调用,不能用于内存查询(如
.Where()之后) 注意数据库大小写敏感性,尤其是在非SQL Server环境下
基本上就这些。根据你的数据库函数类型选择合适的方式即可。
