[英]Searching in sql server json column and consume it using entity framework core
To make the story short, i have a model like this.简而言之,我有一个这样的模型。
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);
}
}
}
And im storing json data inside the properties column, that part is ok, then i have a query where i need to search by Name or Email, so i have a Repository pattern and我将 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
When i execute the stored procedure inside the sql server management studio i get the information right:当我在 sql server management studio 中执行存储过程时,我得到的信息是正确的:
exec spSearchUsers 'Raul'
I get this result:我得到这个结果:
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
1 Raul Alejandro Baez Camarillo raul.baez.camarillo@gmail.com 0 NULL NULL NULL NULL NULL NULL NULL 2021-11-16 03:08:09.6630000 2021-11-16 03:08:09.6630000
So now i want to consume that stored procedure in my controller, im using Repository pattern and i have tried using the context.Model.FromSqlRaw like this.所以现在我想在我的控制器中使用那个存储过程,我使用 Repository 模式,我已经尝试过像这样使用 context.Model.FromSqlRaw。
var result = await _context.Users.FromSqlRaw("EXEC dbo.spSearchUsers {0}", term).ToListAsync()
But when i execute it im getting this error:但是当我执行它时,我收到了这个错误:
System.InvalidOperationException: The required column 'Properties' was not present in the results of a 'FromSql' operation.
System.InvalidOperationException:“FromSql”操作的结果中不存在所需的“Properties”列。 at Microsoft.EntityFrameworkCore.Query.Internal.FromSqlQueryingEnumerable
1.BuildIndexMap(IReadOnlyList
1 columnNames, DbDataReader dataReader) at Microsoft.EntityFrameworkCore.Query.Internal.FromSqlQueryingEnumerable1.AsyncEnumerator.InitializeReaderAsync(DbContext _, Boolean result, CancellationToken cancellationToken) at Microsoft.EntityFrameworkCore.SqlServer.Storage.Internal.SqlServerExecutionStrategy.ExecuteAsync[TState,TResult](TState state, Func
4 operation, Func4 verifySucceeded, CancellationToken cancellationToken) at Microsoft.EntityFrameworkCore.Query.Internal.FromSqlQueryingEnumerable
1.AsyncEnumerator.MoveNextAsync() at Microsoft.EntityFrameworkCore.EntityFrameworkQueryableExtensions.ToListAsync[TSource](IQueryable1 source, CancellationToken cancellationToken) at Microsoft.EntityFrameworkCore.EntityFrameworkQueryableExtensions.ToListAsync[TSource](IQueryable
1 source, CancellationToken cancellationToken) at AcadminRest.Controllers.UsersController.Search(String term) in在 Microsoft.EntityFrameworkCore.Query.Internal.FromSqlQueryingEnumerable
1.BuildIndexMap(IReadOnlyList
1 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, Func
BooleanCancellationToken) Microsoft.EntityFrameworkCore.Query.Internal.FromSqlQueryingEnumerable1.BuildIndexMap(IReadOnlyList
1 columnNames, DbDataReader dataReader)1.AsyncEnumerator.InitializeReaderAsync(DbContext _, Boolean result, CancellationToken cancellationToken) at Microsoft.EntityFrameworkCore.SqlServer.Storage.Internal.SqlServerExecutionStrategy.ExecuteAsync[TState,TResult](TState state, Func
4 operation, Func4 verifySucceeded, CancellationToken cancellationToken) at Microsoft.EntityFrameworkCore.Query.Internal.FromSqlQueryingEnumerable
1.AsyncEnumerator.MoveNextAsync() 在 Microsoft。 EntityFrameworkCore.EntityFrameworkQueryableExtensions.ToListAsync[TSource](IQueryable1 source, CancellationToken cancellationToken) at Microsoft.EntityFrameworkCore.EntityFrameworkQueryableExtensions.ToListAsync[TSource](IQueryable
令牌1 source, CancellationToken cancellationToken) at Microsoft.EntityFrameworkCore.EntityFrameworkQueryableExtensions.ToListAsync[TSource](IQueryable
1 源,CancellationToken 取消令牌)在 AcadminRest.Controllers.UsersController.Search(字符串术语)中 D:\\Projects\\Accomplishment\\AcadminRest\\AcadminRest\\Controllers\\UsersController.cs:line 71 at Microsoft.AspNetCore.Mvc.Infrastructure.ActionMethodExecutor.TaskOfIActionResultExecutor.Execute(IActionResultTypeMapper mapper, ObjectMethodExecutor executor, Object controller, Object[] arguments) at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.g__Awaited|12_0(ControllerActionInvoker invoker, ValueTask`1 actionResultValueTask)D:\\Projects\\Accomplishment\\AcadminRest\\AcadminRest\\Controllers\\UsersController.cs:Microsoft.AspNetCore.Mvc.Infrastructure.ActionMethodExecutor.TaskOfIActionResultExecutor.Execute(IActionResultTypeMapper mapper, ObjectMethodExecutor executor, Object controller, Object[] arguments) 的第 71 行.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.g__Awaited|12_0(ControllerActionInvoker invoker, ValueTask`1 actionResultValueTask)
Can someone help me getting this?有人可以帮我得到这个吗? thanks in advance.
提前致谢。
Well - you could use Value conversion:好吧 - 你可以使用价值转换:
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/value-conversions?tabs=data-annotations#composite-value-objects
Another option would be to create a keyless entity for that specific query:另一种选择是为该特定查询创建一个无键实体:
https://docs.microsoft.com/en-us/ef/core/modeling/keyless-entity-types?tabs=data-annotations https://docs.microsoft.com/en-us/ef/core/modeling/keyless-entity-types?tabs=data-annotations
Edit: here's a bit on how to setup a keyless entity.编辑:这里有一些关于如何设置无钥匙实体的信息。 https://stackoverflow.com/a/69924584/4122889
https://stackoverflow.com/a/69924584/4122889
The options that sommmen gave are ones that I would have also offered up. sommmen 提供的选项是我也会提供的选项。 In particular -- If you're not tied to your procedure at all.
特别是 - 如果您根本不依赖于您的程序。
Both options give you a simpler querying method within your repository.这两个选项都为您在存储库中提供了一种更简单的查询方法。 Personally -- I would go with the value conversion but... here are both options in more detail.
就个人而言——我会选择价值转换,但......这里有更详细的两种选择。
Value conversion: you would want to use your UserDTO
and actually map it.价值转换:您可能想要使用您的
UserDTO
并实际映射它。 Your model would look like so:您的模型如下所示:
public class User : IEntity
{
public int Id { get; set; }
public UserDTO Properties { get; set; }
public DateTime? CreatedAt { get; set; }
public DateTime? UpdatedAt { get; set; }
}
No need for an extra property.不需要额外的财产。 Then your db context would be updated to use a conversion for
Properties
:然后您的数据库上下文将被更新以使用
Properties
的转换:
// somewhere within OnModelCreating
modelBuilder.Entity<User>(entity =>
{
entity.Property(r => r.Properties)
.HasConversion(
v => JsonConvert.SerializeObject(v),
v => JsonConvert.DeserializeObject<UserDTO>(v)
);
});
Now in your repository you can search with linq on your Users
.现在在您的存储库中,您可以使用 linq 在您的
Users
上进行搜索。
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();
-- OR -- - 或者 -
The other option (but slightly different from just a keyless entity) - if you want to make use of the query that you already have in your procedure.另一个选项(但与无键实体略有不同) - 如果您想使用您的程序中已有的查询。
Create a view ( UserDTO_Properties
) with your query w/o the concatenation of first/last fields and then a keyless entity to go with your view.使用您的查询创建一个视图 (
UserDTO_Properties
), UserDTO_Properties
连接第一个/最后一个字段,然后创建一个与您的视图一起使用的无键实体。
You would take your UserDTO
and create the keyless entity to map to your view.您将使用
UserDTO
并创建无键实体以映射到您的视图。 In your db context:在您的数据库上下文中:
// context prop for dbset
DbSet<UserDTO> UserDTOs { get; set; }
// and then somewhere in OnModelCreating
modelBuilder.Entity<UserDTO>(entity =>
{
entity.ToTable("UserDTO_Properties");
entity.HasNoKey();
}
Now a similar query to get your results:现在一个类似的查询来获得你的结果:
var result = await _context.UserDTOs
.Where(u => u.FirstName.Contains(term) ||
u.LastName.Contains(term) ||
u.Email.Contains(term)).ToListAsync();
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.