在当今的软件开发中,Entity Framework 是一个广泛使用的 ORM(对象关系映射)框架。其中,代码优先(Code First)的方法因其提高代码模块化和易于维护的特性而广受欢迎。与数据库优先(Database First)的方法相比,Entity Framework 目前并没有直接提供与用户定义函数直接通信的简单方式。
在处理一个项目时,遇到了一个需求,需要直接使用 Entity Framework 与用户定义的函数进行通信。虽然可以通过以下代码实现:
db.Database.SqlQuery<MyModel>("select * from usr_func()");
但希望代码更加清晰和结构化,以便更好地处理这个函数。因此,决定编写一个库,实现自定义逻辑,使这个功能更加模块化和结构化。在查阅了不同的博客后,发现了 aureolin 写的《Code First Stored Procedures》博客。下载了它的最新版本,并对其进行了重大修改,最终成功了。
首先,来看一个使用 SQL Server 创建的简单表值函数:
CREATE FUNCTION ufn_MyFunction(@id int) RETURNS TABLE AS RETURN (SELECT * from tbl_Customer where Id = @id);
这个函数接受 @id 作为参数,并根据 id 返回表记录。这只是一个示例,实际实现可以接收任何类型或没有参数,并返回包含大量逻辑的查询。
现在,让看看如何使用代码优先方法映射这个函数。首先,创建一个类来传递这个函数的参数。使用以下代码创建它:
public class TestFunctionParams {
[CodeFunctionAttributes.FunctionOrder(1)]
[CodeFunctionAttributes.Name("id")]
[CodeFunctionAttributes.ParameterType(System.Data.SqlDbType.Int)]
public int Id { get; set; }
}
在这里,提到了函数顺序,因为按顺序传递函数参数非常重要。如果有多个参数,那么需要添加它们的顺序以传递给函数。此外,还编写了许多其他属性来处理不同的操作,如参数的名称和类型。
现在,将在数据上下文类中初始化库,并添加以下代码到 DbContext 类的构造函数中:
public DataContext() : base("name=SqlConn") {
this.InitializeTableFunctions();
}
接下来,将函数添加为数据上下文类中的属性,如下所示:
[CodeFunctionAttributes.Schema("dbo")]
[CodeFunctionAttributes.Name("ufn_MyFunction")]
[CodeFunctionAttributes.ReturnTypes(typeof(Customer))]
public TableValueFunction<TestFunctionParams> CustomerFunction { get; set; }
现在整个数据库上下文将如下所示:
public class DataContext : DbContext {
public DataContext() : base("name=SqlConn") {
System.Data.Entity.Database.SetInitializer<DataContext>(null);
this.InitializeTableFunctions();
}
public DbSet<Customer> Customers { get; set; }
[CodeFunctionAttributes.Schema("dbo")]
[CodeFunctionAttributes.Name("ufn_MyFunction")]
[CodeFunctionAttributes.ReturnTypes(typeof(Customer))]
public TableValueFunction<TestFunctionParams> CustomerFunction { get; set; }
}
最后,一切都设置好了。现在可以很容易地使用以下代码调用这个函数:
static void Main(string[] args) {
using (var db = new DataContext()) {
var funcParams = new TestFunctionParams() { Id = 1 };
var entity = db.CustomerFunction.ExecuteFunction(funcParams).ToList<Customer>();
}
}
函数的最终结果将返回到对象中,并转换为列表并映射到模型。通过这种方式,可以添加可以接收任何数量和任何类型的参数的函数(还可以使用方法参数使用 SqlDbType.Structured)并将其映射到模型。