使用值对象时,如何使用EF Core映射和使用SQL Server用户定义的函数?
我想调用用户定义的SQL Server函数,并用作参数为值对象的属性。 EF核心文档仅显示具有原始类型的样本。我无法创建工作映射。
我们业务领域的实体需要支持多语言文本属性。该文本由用户提供。我们创建了一个内部使用字典的多语言值对象(ML< t>)。在数据库中,在nvarchar(max)
列中将多语言属性保存为JSON。 EF Core Converter可以处理与字符串数据类型的转换。
这是一个简化的国家实体,只是为了理解以下测试代码:
public class Country : VersionedEntity
{
public string CountryId { get; set; }
public ML<CountryName> Name { get; set; }
}
数据库中的示例行看起来像:
deu | [{“ language”:“ de-de-de”,“ value”:“ deutschland”},{“ language”:“ en-us”,“ value”:“ dermany”},{“ language”:“ it-it “,” value”:“ tedesco”}]
文本(即产品描述)可能不会用所有受支持的语言翻译。因此,用户可以定义语言偏好列表。将显示最好的匹配语言。
由于分页需要在数据库中进行排序,因此我在SQL Server中创建了一个用户定义的函数getLanguage
,该函数返回每行的最佳匹配语言,并允许为每个用户的语言偏好进行排序。
我在dbcontext中声明了该函数
public string? GetLanguage(string json, string preferredLanguages)
=> throw new NotSupportedException($"{nameof(GetLanguage)} cannot be called client side");
,并映射了它
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.HasDbFunction(typeof(AccountsDbContext)
.GetMethod(nameof(GetLanguage),
new[] { typeof(string), typeof(string) })!);
}
,试图在测试LINQ查询中调用
string preferredLanguages = "de-DE,en-US,fr-FR";
List<string?> list = _dbContext.Country.Select(c => _dbContext.GetLanguage(c.Name, preferredLanguages)).ToList();
该函数。 C.名称会产生错误,因为它是ML&lt; Counturnname&gt; gt;而不是类型字符串作为函数的第一个参数定义。
我期望EF Core不会转换属性,因为该功能在服务器上运行,并且只需要使用数据库的表列即可。它应该创建SQL,就像
Select dbo.GetLanguage(name, 'de-DE,en-US,fr-FR');
有办法告诉EF Core仅使用表列吗?
为了进行测试,我尝试将函数声明更改为
public string? GetLanguage(ML<CountryName> json, string preferredLanguages)
=> throw new NotSupportedException($"{nameof(GetLanguage)} cannot be called client side");
已编译的代码
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.HasDbFunction(typeof(AccountsDbContext)
.GetMethod(nameof(GetLanguage),
new[] { typeof(ML<CountryName>), typeof(string) })!);
}
,但是我遇到了一个运行时错误。
system.invalidoperationException:参数'json' dbfunction 'app.accounts.persistence.accountsdbcontext.getlanguage(app.sharedkernel.domain.ml&lt; app.sharedkernel.domain.countrynryname&gt;&gt;&gt;&gt;,string)''' 具有无效类型的“ ML&lt;乡村名称”。确保参数类型可以 由当前提供商映射。
这些类型是相同的,一种是完全合格的,另一种不是。我不明白为什么会发生此错误以及如何解决。
当使用具有转换器的值对象时,这似乎是一个一般问题。 EF Core应该创建使用DB函数和表列的SQL,因为所有内容都应在服务器上运行。 我该如何实现?
I want to call a user defined SQL Server function and use as parameter a property that is a value object. The EF Core documentation shows only samples with primitive types. I can't manage to create a working mapping.
The entities of our business domain need to support multi-language text properties. The text is provided by the user. We created a multi-language value object (ML<T>) that internally uses a dictionary. In the database multi-language properties are saved as JSON in a nvarchar(max)
column. An EF Core converter handles the conversion to and from string data type.
This is a simplified country entity just to comprehend the test code below:
public class Country : VersionedEntity
{
public string CountryId { get; set; }
public ML<CountryName> Name { get; set; }
}
A sample row in the database looks like this:
DEU |
[{"language":"de-DE","value":"Deutschland"},{"language":"en-US","value":"Germany"},{"language":"it-IT","value":"Tedesco"}]
Text (i.e. a product description) might not be translated in all supported languages. Therefore the users can define a language preference list. The best matching language will be displayed.
Since paging requires sorting in the database, I created a user defined function GetLanguage
in SQL Server that returns the best matching language for each row and allows sorting for each user's language preference.
I declared the function in the DbContext
public string? GetLanguage(string json, string preferredLanguages)
=> throw new NotSupportedException(quot;{nameof(GetLanguage)} cannot be called client side");
and mapped it
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.HasDbFunction(typeof(AccountsDbContext)
.GetMethod(nameof(GetLanguage),
new[] { typeof(string), typeof(string) })!);
}
I tried to call the function in a test LINQ query
string preferredLanguages = "de-DE,en-US,fr-FR";
List<string?> list = _dbContext.Country.Select(c => _dbContext.GetLanguage(c.Name, preferredLanguages)).ToList();
It does not compile. c.Name creates an error, because it is of type ML<CountryName> and not of type string as the first parameter of the function is defined.
I was expecting that EF Core would not convert the property, because the function runs on the server and just needs to use the table column of the database. It should create SQL like
Select dbo.GetLanguage(name, 'de-DE,en-US,fr-FR');
Is there a way to tell EF Core to just use the table column?
For testing I tried to change the function declaration to
public string? GetLanguage(ML<CountryName> json, string preferredLanguages)
=> throw new NotSupportedException(quot;{nameof(GetLanguage)} cannot be called client side");
and the mapping to
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.HasDbFunction(typeof(AccountsDbContext)
.GetMethod(nameof(GetLanguage),
new[] { typeof(ML<CountryName>), typeof(string) })!);
}
The code compiled, but I get a runtime error.
System.InvalidOperationException : The parameter 'json' for the
DbFunction
'App.Accounts.Persistence.AccountsDbContext.GetLanguage(App.SharedKernel.Domain.ML<App.SharedKernel.Domain.CountryName>>,string)'
has an invalid type 'ML<CountryName>'. Ensure the parameter type can
be mapped by the current provider.
The types are identical, one just is fully qualified and the other not. I don't understand why this error occurs and how I can fix it.
It seems a general issue when a value object with a converter is used. EF Core should create SQL that uses the DB function and the table column, because everything should run on the server.
How can I achieve this?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
on https://github.com/dotnet/efcore/efcore/issues/issues/28393 答案提供了。
因此,解决方法是将对象用作DBFunction定义的类型并声明数据库类型:
然后使用对象类型转换调用函数:
它创建Clean SQL:
On https://github.com/dotnet/efcore/issues/28393 an answer was provided.
So a workaround is to use object as type for the DbFunction definition and declare the database type:
Then call the function with object type conversion:
It creates clean SQL: