在 EF6 Code First 中支持表值函数吗?
Posted
技术标签:
【中文标题】在 EF6 Code First 中支持表值函数吗?【英文标题】:Support for Table Valued Functions in EF6 Code First? 【发布时间】:2014-03-13 03:37:20 【问题描述】:是否可以在 EF6 Code First 中调用 TVF?
我首先使用 EF6 数据库开始了一个新项目,EF 能够将 TVF 导入模型并调用它就好了。
但是对于没有 RI 的大型只读数据库,更新模型变得非常耗时且存在问题,我一直在处理这些问题。
所以我首先尝试使用 Power Tools 逆向工程工具转换为 EF6 代码以生成上下文和模型类。
很遗憾,逆向工程工具没有导入 TVF。
接下来,我尝试将 DBFunction 从旧的 Database First DbContext 复制到新的 Code First DbContext,但这给了我一个错误,即我的 TVF: “无法解析为有效的类型或函数”。
是否可以为 TVF 创建代码优先的 Fluent 映射?
如果没有,是否有解决方法?
我想我可以使用 SP 而不是 TVF,但希望我可以主要使用 TVF 来处理我遇到的有问题的数据库。
感谢您提供任何解决方法
【问题讨论】:
我不这么认为,至少这就是我仍然首先使用模型的原因,但是要解决大型模型的问题,不要将所有内容导入一个模型但具有较小的域特定的代理/客户 显然可以:msdn.microsoft.com/en-ca/data/hh859577.aspx 感谢 zahorak 的想法,如果我找不到代码的解决方法,我可能会先切换回使用数据库的方法。我的意图是使用逆向工程工具仅导入我需要的 TVF,并仅使用它们来访问我无法从代码优先上下文更改的大而丑陋的数据库。 TheVedge,那篇文章谈到了使用 EDMX 模型的 Database First,我需要 Code First 中的它,这意味着没有 EDMX 模型,而是使用 Fluent API 映射的 DbContext。 @Patrick 对不起,读得太快了。目前不可能,推迟到EF6之后:data.uservoice.com/forums/… 【参考方案1】:[已测试] 使用:
Install-Package EntityFramework.CodeFirstStoreFunctions
为输出结果声明一个类:
public class MyCustomObject
[Key]
public int Id get; set;
public int Rank get; set;
在你的 DbContext 类中创建一个方法
[DbFunction("MyContextType", "SearchSomething")]
public virtual IQueryable<MyCustomObject> SearchSomething(string keywords)
var keywordsParam = new ObjectParameter("keywords", typeof(string))
Value = keywords
;
return (this as IObjectContextAdapter).ObjectContext
.CreateQuery<MyCustomObject>(
"MyContextType.SearchSomething(@keywords)", keywordsParam);
添加
public DbSet<MyCustomObject> SearchResults get; set;
到你的 DbContext 类
添加覆盖的OnModelCreating
方法:
modelBuilder.Conventions.Add(new FunctionsConvention<MyContextType>("dbo"));
现在你可以打电话/加入 像这样的表值函数:
CREATE FUNCTION SearchSomething
(
@keywords nvarchar(4000)
)
RETURNS TABLE
AS
RETURN
(SELECT KEY_TBL.RANK AS Rank, Id
FROM MyTable
LEFT JOIN freetexttable(MyTable , ([MyColumn1],[MyColumn2]), @keywords) AS KEY_TBL
ON MyTable.Id = KEY_TBL.[KEY]
WHERE KEY_TBL.RANK > 0
)
GO
【讨论】:
NB 如果返回类型不是表(因此将其添加为DbSet
将尝试在迁移中创建它)将其添加为复杂类型。 [ComplexType]
属性不起作用,但流畅的 API 可以:在您的上下文类型的 OnModelBuilding
中添加 modelBuilder.ComplexType<MyCustomerObject>();
@Nina 我做了所有列出的事情,但仍然得到cannot be resolved into a valid type or function. Near member access expression
。如何修复?我希望这是因为我在IRepostory
接口中添加了函数,而不是添加到 DbContext,因为我不想将本地函数添加到全局上下文中。
你可以遵循这个指南,它可以工作blog.3d-logic.com/2014/04/09/…【参考方案2】:
这现在是可能的。我创建了一个自定义模型约定,它允许在 EF6.1 的 CodeFirst 中使用存储函数。该约定在 NuGet http://www.nuget.org/packages/EntityFramework.CodeFirstStoreFunctions 上可用。这是包含所有详细信息的博文链接:http://blog.3d-logic.com/2014/04/09/support-for-store-functions-tvfs-and-stored-procs-in-entity-framework-6-1/
【讨论】:
谢谢!我必须将 base.OnModelCreating(modelBuilder) 添加到 OnModelCreating,否则 Add-Migrations 会发现大量更改。 @Pawel 有什么原因不能与 Npgsql 一起使用吗?【参考方案3】:我可以使用以下代码访问 TVF。这适用于 EF6。模型属性名称必须与数据库列名称匹配。
List<MyModel> data =
db.Database.SqlQuery<MyModel>(
"select * from dbo.my_function(@p1, @p2, @p3)",
new SqlParameter("@p1", new System.DateTime(2015,1,1)),
new SqlParameter("@p2", new System.DateTime(2015, 8, 1)),
new SqlParameter("@p3", 12))
.ToList();
【讨论】:
您发布的代码甚至与实体框架相关。 是的,任何事情都可以使用 SQL 查询来完成,但通常有一种 EF 方式可以做到这一点,也有一种 SQL 查询方式可以做到这一点。如果有 EF 方法,那么显然使用 EF 方法,对于 EF 不支持它的情况,EF 的创建者创建了 SQL 查询。我的观点是在写这篇文章的时候,没有办法使用 EF 来做到这一点,这是一种方法。如果您不同意这一点,请发布一个答案,您将如何严格使用 EF 执行此操作。通过使用 EF 执行此操作,您可以获得自动对象映射、ORM 的 O 和 M。 现在你的答案是错误的,另外两个答案是正确的。所以我对你的投了反对票,对另外两个投了赞成票。我不必提供自己的答案就可以否决别人的答案。这就是世界的流动。当你逃避时;曾经正确的答案现在可能不正确。还;在您回答时,CodeFirstStoreFunctions 已经可用。因此,当您发布答案时甚至是错误的。不要把它看得太私人。当有人问如何使用螺丝刀而你递给他们一把锤子时,这很糟糕。 CodeFirstStoreFunctions 是第 3 方 NuGet 包,而不是标准 EF 功能。查看下面的示例代码,它只是 SqlQuery 的一个包装器,带有一些花里胡哨的东西。很多地方不允许将第 3 方代码下载到代码库中,因此在 MSFT 将其捆绑到下一个 EF 版本之前,这对这些人来说并不是一个真正的选择。 这个答案对我来说是最好的答案,因为我不想过多地依赖第 3 方解决方案,而且每个 ORM 都有一段时间不足。这种方法特别容易被发现,因为它可以防止 sql 注入并打开泄漏的抽象,并使开发人员意识到如果需要,他总是可以在这里和那里使用一点 sql。【参考方案4】:我实际上是在 EF6.1 中开始研究它,并且有一些东西正在用于夜间构建。检查this 和this。
【讨论】:
我刚刚尝试在 EF 6.1 的官方版本中使用 CodeFirstFunctions,我可以证明它非常有效。似乎有一些警告,但我能够创建一个可组合的函数,它返回一个复杂的类型而没有任何麻烦。项目中提供的端到端单元测试是如何完成它的完美示例。 感谢您提供的信息 - 很高兴听到这个消息。我正在准备一个 NuGet 包,这样您就可以“开箱即用”地使用它(即无需自己编译),还有一篇关于如何使用它的博客文章。你能分享更多关于警告的信息吗?他们是错误还是更多关于缺乏文档? 我遇到的唯一问题是我的 TVF 具有可为空的参数。这导致了一些我不记得的错误,但它让我进入了代码,在那里我注意到了一些关于此类事情的 TODO。这就是我所指的。一旦我将函数的 C# 定义更改为使用常规标量参数,一切都很好。 谢谢。我确实需要添加对可空参数的支持。我不打算为 Alpha 版执行此操作,但看起来我应该这样做。 @EricTheRed - 昨天我发布了大会的测试版。可空参数类型的问题现已修复。还有一些新功能,例如支持返回多个结果集的存储过程或支持标量 UDF - 请看这里:blog.3d-logic.com/2014/08/11/…【参考方案5】:我为此功能开发了一个库。你可以查看我的文章 UserTableFunctionCodeFirst。 您无需编写 SQL 查询即可使用您的函数。
更新
首先你必须添加对上述库的引用,然后你必须为你的函数创建参数类。该类可以包含任意数量和类型的参数
public class TestFunctionParams
[CodeFunctionAttributes.FunctionOrder(1)]
[CodeFunctionAttributes.Name("id")]
[CodeFunctionAttributes.ParameterType(System.Data.SqlDbType.Int)]
public int Id get; set;
现在您必须在 DbContext 中添加以下属性来调用函数并映射到该属性。
[CodeFunctionAttributes.Schema("dbo")] // This is optional as it is set as dbo as default if not provided.
[CodeFunctionAttributes.Name("ufn_MyFunction")] // Name of function in database.
[CodeFunctionAttributes.ReturnTypes(typeof(Customer))]
public TableValueFunction<TestFunctionParams> CustomerFunction get; set;
然后你可以如下调用你的函数。
using (var db = new DataContext())
var funcParams = new TestFunctionParams() Id = 1 ;
var entity = db.CustomerFunction.ExecuteFunction(funcParams).ToList<Customer>();
这将调用您的用户定义函数并映射到实体。
【讨论】:
【参考方案6】:EntityFramework.Functions 包现在也提供了类似的功能。这个好像比较新。
源代码在GitHub 上,introductory article 解释了基本原理。
【讨论】:
以上是关于在 EF6 Code First 中支持表值函数吗?的主要内容,如果未能解决你的问题,请参考以下文章
MVC5与EF6 Code First 第一个入门完整实例教程