Parse JSON array ke model .NET Core menyebabkan set kosong

Nov 08 2020

Dengan memanggil DbSet<T>.FromSqlRaw()saya bisa memanggil Prosedur Tersimpan di Database saya, yang mengembalikan hasil yang ditetapkan seperti ini:

Id VARCHAR(36)
FirstName VARCHAR(255)
LastName VARCHAR(255) NULL
Email VARCHAR(255) NULL
Numbers VARCHAR(?) NULL

Numbersadalah VARCHARbidang yang berisi larik JSON dari SearchContactsNumber:

public sealed class SearchContactsNumber
{
    public Guid IdNumber { get; set; }
    public string Type { get; set; }
    public string Number { get; set; }
}

Jadi, misalnya, kumpulan hasil bisa seperti ini:

"34f8d20f-21da-11eb-a249-de3268ec1e72" | "Paul" | "Newman" | "[email protected]" | "[{"IdNumber":"481d2957-21da-11eb-a249-de3268ec1e72","Type":"Telephone","Number":"+440001122333"},{...},{...}]"

Debugging titik akhir TestController.Index:

public sealed class SearchContacts
{
    public Guid IdContact { get; set; }
    public string FirstName { get; set; }
    public string LastName { get; set; }
    public string Company { get; set; }
    public IEnumerable<SearchContactsNumber> Numbers { get; set; }
}

public class TestController : Controller
{
    private readonly DbContext _context;

    public TestController(DbContext context)
    {
        _context = context;
    }

    public IActionResult Index()
    {
        var set = _context.SearchContacts.FromSqlRaw<SearchContacts>($"CALL `SearchContacts`()");
        return Ok(set.ToList());
    }
}

kembali:

Bagaimana saya bisa mendapatkan pengikatan penuh string json?

Saya menggunakan Pomelo.EntityFrameworkCore.MySql (3.2.3)dengan Database MySQL 8 dalam Proyek ASP.NET Core 3.1 MVC.

Jawaban

4 lauxjpn Nov 08 2020 at 20:21

Pomelo memperkenalkan dukungan JSON tumpukan penuh beberapa minggu yang lalu, yang tersedia dalam versi Pomelo terbaru dan akan digunakan di masa mendatang (pendekatan sebelumnya, seperti JsonObject<T>sekarang sudah tidak digunakan lagi dan tidak lagi didukung secara resmi di versi 5.0+).

Untuk menggunakannya, Anda perlu menambahkan salah satu dari paket berikut, tergantung pada tumpukan mana yang ingin Anda gunakan di bawah tenda:

  • Pomelo.EntityFrameworkCore.MySql.Json.Microsoft
  • Pomelo.EntityFrameworkCore.MySql.Json.Newtonsoft

Paket ini mendukung kelas POCO, API DOM khusus tumpukan, dan pemetaan string sederhana.

Kami juga mendukung apa pun mulai dari pelacakan perubahan khusus tingkat atas (sangat cepat) hingga penuh (lebih lambat) untuk entitas JSON (dapat dikontrol melalui optionsparameter metode UseMicrosoftJson()dan UseNewtonsoftJson()).

Berikut adalah contoh proyek konsol yang berfungsi penuh, yang mendemonstrasikan cara menggunakan dukungan JSON tumpukan penuh dari Pomelo untuk kasus khusus Anda (di sini menggunakan tumpukan Microsoft):

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Logging;
using Pomelo.EntityFrameworkCore.MySql.Infrastructure;

namespace IssueConsoleTemplate
{
    //
    // EF Core Entities:
    //
    
    public sealed class SearchContact
    {
        public Guid IdContact { get; set; }
        public string FirstName { get; set; }
        public IEnumerable<SearchContactsNumber> Numbers { get; set; }
    }
    
    //
    // JSON Entities:
    //
    
    public sealed class SearchContactsNumber
    {
        public Guid IdNumber { get; set; }
        public string Type { get; set; }
        public string Number { get; set; }
    }
    
    //
    // DbContext:
    //
    
    public class Context : DbContext
    {
        public DbSet<SearchContact> SearchContacts { get; set; }

        protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
        {
            optionsBuilder
                .UseMySql(
                    "server=127.0.0.1;port=3306;user=root;password=;database=So64741089",
                    b => b.ServerVersion("8.0.21-mysql")
                          .CharSetBehavior(CharSetBehavior.NeverAppend)
                          .UseMicrosoftJson()) // <-- needed when using the Microsoft JSON stack (System.Text.Json)
                .UseLoggerFactory(
                    LoggerFactory.Create(
                        b => b
                            .AddConsole()
                            .AddFilter(level => level >= LogLevel.Information)))
                .EnableSensitiveDataLogging()
                .EnableDetailedErrors();
        }

        protected override void OnModelCreating(ModelBuilder modelBuilder)
        {
            modelBuilder.Entity<SearchContact>(
                    entity =>
                    {
                        entity.HasKey(e => e.IdContact);
                        
                        entity.Property(e => e.Numbers)
                            .HasColumnType("json"); // <-- simple way to serialize any property from/to JSON
                    });
        }
    }

    internal class Program
    {
        private static void Main()
        {
            using var context = new Context();

            SetupDatabase(context);

            var searchContacts = context.SearchContacts
                .FromSqlInterpolated($"CALL `SearchContacts`()")
                .ToList();

            Debug.Assert(searchContacts.Count == 1);
            Debug.Assert(searchContacts[0].Numbers.Count() == 1);
            Debug.Assert(searchContacts[0].Numbers.First().IdNumber == new Guid("481d2957-21da-11eb-a249-de3268ec1e72"));
        }

        private static void SetupDatabase(Context context)
        {
            context.Database.EnsureDeleted();
            context.Database.EnsureCreated();

            var connection = context.Database.GetDbConnection();
            connection.Open();

            using var command = connection.CreateCommand();
            command.CommandText = @"CREATE PROCEDURE `SearchContacts`()
BEGIN
    SELECT '34f8d20f-21da-11eb-a249-de3268ec1e72' as `IdContact`,
           'Paul' as `FirstName`,
           '[{""IdNumber"":""481d2957-21da-11eb-a249-de3268ec1e72"",""Type"":""Telephone"",""Number"":""+440001122333""}]' as `Numbers`;
END";
            command.ExecuteNonQuery();
        }
    }
}
2 Trekco Nov 08 2020 at 19:03

Jika Anda menjadikannya JsonObject maka serialisasi akan dilakukan secara otomatis. JsonObject berada di bawah System namespace dan Pomelo.EntityFrameworkCore.MySql mendukungnya 100%

lihat kode di bawah ini

using System;

public sealed class SearchContacts
{
    public Guid IdContact { get; set; }
    public string FirstName { get; set; }
    public string LastName { get; set; }
    public string Company { get; set; }
    public JsonObject<SearchContactsNumber[]> Numbers { get; set; }

    public SearchContactsNumber[] GetNumbers()
    {
        return Numbers.Object;
    }
}

lihat: Cara menggunakan JsonObject dari Pomelo.EntityFramework

MestreDosMagros Nov 08 2020 at 19:11

Anda dapat mencoba sesuatu seperti ini:

    public sealed class SearchContacts
    {
        public Guid IdContact { get; set; }
        public string FirstName { get; set; }
        public string LastName { get; set; }
        public string Company { get; set; }
        public IEnumerable<SearchContactsNumber> Numbers { get; private set; }
        private string NumbersJson { set => Numbers = JsonConvert.DeserializeObject<IEnumerable<SearchContactsNumber>>(value); }
    }

Pastikan Anda memetakan properti Numbers ke NumbersJson