在 sql server json 列中搜索并使用实体框架核心使用它

Posted

技术标签:

【中文标题】在 sql server json 列中搜索并使用实体框架核心使用它【英文标题】:Searching in sql server json column and consume it using entity framework core 【发布时间】:2021-11-16 21:55:31 【问题描述】:

长话短说,我有一个这样的模型。

public class User : IEntity

    public int Id  get; set; 
    public string Properties  get; set; 
    public DateTime? CreatedAt  get; set; 
    public DateTime? UpdatedAt  get; set; 
    [NotMapped]
    public UserDTO _Properties
    
        get
        
            return Properties == null ? null : JsonConvert.DeserializeObject<UserDTO>(Properties);
        
        set
        
            Properties = JsonConvert.SerializeObject(value);
        
    

我在属性列中存储 json 数据,那部分没问题,然后我有一个查询,我需要按名称或电子邮件搜索,所以我有一个存储库模式和

alter procedure [dbo].[spSearchUsers]
@term nvarchar(max) AS BEGIN
select Id, JSON_VALUE(Properties, '$.FirstName') as FirstName,
    JSON_VALUE(Properties, '$.LastName') as LastName, 
    JSON_VALUE(Properties, '$.Email') as Email,
    JSON_VALUE(Properties, '$.Role') As [Role],
    JSON_VALUE(Properties, '$.ProgramInformation') as ProgramInformation,
    JSON_VALUE(Properties, '$.CertsAndCredentials') as CertsAndCredentials,
    JSON_VALUE(Properties, '$.Phone') as Phone,
    JSON_VALUE(Properties, '$.LogoUrl') as LogoUrl,
    JSON_VALUE(Properties, '$.ProfessionalPic') as ProfessionalPic,
    JSON_VALUE(Properties, '$.Biography') as Biography,
    CreatedAt, UpdatedAt
from Users
where CONCAT(JSON_VALUE(Properties, '$.FirstName'),' ',JSON_VALUE(Properties, '$.LastName')) like '%'+ @term +'%' 
    or JSON_VALUE(Properties, '$.Email') like '%'+ @term +'%'
    and JSON_VALUE(Properties, '$.IsActive') = 'true'
    and JSON_VALUE(Properties, '$.IsLockedOut') = 'false'
    and JSON_VALUE(Properties, '$.IsVerified') = 'true' END

当我在 sql server management studio 中执行存储过程时,我得到了正确的信息:

exec spSearchUsers 'Raul'

我得到这个结果:

1 Raul Alejandro Baez Camarillo raul.baez.camarillo@gmail.com 0 NULL NULL NULL NULL NULL NULL 2021-11-16 03:08:09.6630000 2021-11-16 03:08:09.6630000

所以现在我想在我的控制器中使用该存储过程,我使用 Repository 模式,并且我尝试过像这样使用 context.Model.FromSqlRaw。

var result = await _context.Users.FromSqlRaw("EXEC dbo.spSearchUsers 0", term).ToListAsync()

但是当我执行它时,我得到了这个错误:

System.InvalidOperationException:“FromSql”操作的结果中不存在所需的“Properties”列。 在 Microsoft.EntityFrameworkCore.Query.Internal.FromSqlQueryingEnumerable1.BuildIndexMap(IReadOnlyList1 columnNames,DbDataReader dataReader) 在 Microsoft.EntityFrameworkCore.Query.Internal.FromSqlQueryingEnumerable1.AsyncEnumerator.InitializeReaderAsync(DbContext _, Boolean result, CancellationToken cancellationToken) at Microsoft.EntityFrameworkCore.SqlServer.Storage.Internal.SqlServerExecutionStrategy.ExecuteAsync[TState,TResult](TState state, Func4 操作,Func4 verifySucceeded, CancellationToken cancellationToken) at Microsoft.EntityFrameworkCore.Query.Internal.FromSqlQueryingEnumerable1.AsyncEnumerator.MoveNextAsync() 在 Microsoft.EntityFrameworkCore.EntityFrameworkQueryableExtensions.ToListAsync[TSource](IQueryable1 source, CancellationToken cancellationToken) at Microsoft.EntityFrameworkCore.EntityFrameworkQueryableExtensions.ToListAsync[TSource](IQueryable1 源,CancellationToken cancelToken) 在 D:\Projects\Accomplishment\AcadminRest\AcadminRest\Controllers\UsersController.cs:line 71 中的 AcadminRest.Controllers.UsersController.Search(String term) 在 Microsoft.AspNetCore.Mvc.Infrastructure.ActionMethodExecutor.TaskOfIActionResultExecutor.Execute(IActionResultTypeMapper 映射器,ObjectMethodExecutor 执行器,对象控制器,对象 [] 参数) 在 Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.g__Awaited|12_0(ControllerActionInvoker 调用程序,ValueTask`1 actionResultValueTask)

谁能帮我搞定这个?提前致谢。

【问题讨论】:

【参考方案1】:

嗯 - 你可以使用价值转换:

https://docs.microsoft.com/en-us/ef/core/modeling/value-conversions?tabs=data-annotations#composite-value-objects

另一种选择是为该特定查询创建一个无密钥实体:

https://docs.microsoft.com/en-us/ef/core/modeling/keyless-entity-types?tabs=data-annotations

编辑:这里有一些关于如何设置无钥匙实体的信息。 https://***.com/a/69924584/4122889

【讨论】:

感谢 sommen,它帮助我解决了我的问题。【参考方案2】:

sommmen 提供的选项是我也会提供的选项。 特别是 -- 如果你根本不依赖你的程序。

这两个选项都为您提供了在存储库中更简单的查询方法。 就个人而言——我会选择价值转换,但是......这里有两个更详细的选项。

价值转换:您可能希望使用您的UserDTO 并实际映射它。你的模型应该是这样的:

public class User : IEntity

    public int Id  get; set; 
    public UserDTO Properties  get; set; 
    public DateTime? CreatedAt  get; set; 
    public DateTime? UpdatedAt  get; set; 

不需要额外的属性。然后您的数据库上下文将更新为使用Properties 的转换:

// somewhere within OnModelCreating
modelBuilder.Entity<User>(entity =>

    entity.Property(r => r.Properties)
        .HasConversion(
            v => JsonConvert.SerializeObject(v),
            v => JsonConvert.DeserializeObject<UserDTO>(v)
        );
);

现在在您的存储库中,您可以在 Users 上使用 linq 进行搜索。

var result = await _context.Users
    .Where(u => u.Properties.FirstName.Contains(term) ||
                u.Properties.LastName.Contains(term) || 
                u.Properties.Email.Contains(term))
    .Where(u => u.IsActive && !u.IsLockedOut && u.IsVerified).ToListAsync();

-- 或者--

另一个选项(但与无密钥实体略有不同) - 如果您想使用您在过程中已有的查询。 使用您的查询创建一个视图 (UserDTO_Properties),其中不包含第一个/最后一个字段的串联,然后是一个无键实体与您的视图一起使用。

您将使用您的UserDTO 并创建无钥匙实体以映射到您的视图。在您的数据库上下文中:

    // context prop for dbset
    DbSet<UserDTO> UserDTOs  get; set; 

    // and then somewhere in OnModelCreating
    modelBuilder.Entity<UserDTO>(entity =>
    
        entity.ToTable("UserDTO_Properties");
        entity.HasNoKey();
    

现在一个类似的查询来得到你的结果:

var result = await _context.UserDTOs
    .Where(u => u.FirstName.Contains(term) ||
                u.LastName.Contains(term) || 
                u.Email.Contains(term)).ToListAsync();

【讨论】:

以上是关于在 sql server json 列中搜索并使用实体框架核心使用它的主要内容,如果未能解决你的问题,请参考以下文章

如何将复杂的 JSON 字符串放入 SQL Server 列中

如何在 SQL Server 中的 OR 条件中确定优先级

在 SQL Server 表中搜索值列表的最有效方法

在 SQL Server 中分组并在一列中连接记录

有没有办法在 SQL Server 2017 中确定查询的列中是不是存在重复项并更改该输入?

SQL Server:SQL 通配符