Сопоставить столбец JSON с MySql База данных с C# Классом из Web Api - PullRequest
6 голосов
/ 21 февраля 2020

У меня есть MySql база данных со столбцами Id int и Name: json

Таблица мест Образец

Id      Name
1       {"en":"Sphinx","ar":"أبو الهول","fr":"Le sphinx"}

C# Класс места

public class Place
{
        [Key, Column("id")]
        public int Id { get; set; }

        [Column("name")]
        public string Name { get; set; }
}

Я соединяюсь с EntityFramework 6 и успешно соединяюсь и получаю данные, подобные этому

{Id = 1, Name = "{\" en \ ": \" Sphinx \ ", \" ar \ ": \" أبو الهول \ ", \" fr \ ": \" Le sphinx \ " } "}


Что я хочу, чтобы привязать имя к new Object не JSON string

как-то так

Place class

public class Place
{
        [Key, Column("id")]
        public int Id { get; set; }

        [Column("name")]
        public Localized<string> Name { get; set; }
}

Локализованный класс

public class Localized<T>
{
        public T en { get; set; } // english localization
        public T ar { get; set; } // arabic localization
        public T fr { get; set; } // french localization
}

, когда я делаю это свойство Name с NULL значением


Код в репозитории

using (var context = new PlacesEntityModel())
{
     return context.Places.Take(5).ToList();
}

Я не хочу использовать AutoMapper,

Я хочу что-то в EntityFramework выбрать только один язык в Уровень базы данных без извлечения всех других данных и последующего сопоставления их

как это исправить?

Ответы [ 6 ]

3 голосов
/ 21 февраля 2020

Прежде всего, используя класс с свойством для каждого языка, вы ограничиваете себя. Вам всегда придется добавлять новые свойства, если вы добавляете новые языки, которые, конечно, были бы выполнимы, но излишне сложны. Кроме того, вы обычно имеете язык в виде объекта string-i sh (или сможете конвертировать), следовательно, это приведет к такому коду

Localized<string> name = ...;
switch(language)
{
    case "en":
        return name.en;
    case "ar":
        return name.ar;
    case "fr":
        return name.fr;
    default:
        throw new LocalizationException();
}

, который подвержен ошибкам и слишком сложен , Для вашей проблемы, я думаю, я бы решил использовать какой-то словарь

IDictionary<string, string> names = ...;
if(names.ContainsKey(language))
{
    return names[language];
}
else 
{
    throw new LocalizationException();
}

, который легко расширяется, просто добавляя дополнительные переводы в словарь.

Для преобразования вашего JSON string до IDcitionary<string, string>, вы можете использовать следующий код

localizedNames = JObject.Parse(Name)
                        .Children()
                        .OfType<JProperty>()
                        .ToDictionary(property => property.Name, 
                                      property => property.Value.ToString());

В вашем классе это будет эффективно

public class Place
{
    [Key, Column("id")]
    public int Id { get; set; }

    [Column("name")]
    public string Name { get; set; }

    public Dictionary<string, string> LocalizedNames 
    {
        get
        {
            return JObject.Parse(Name)
                          .Children()
                          .OfType<JProperty>()
                          .ToDictionary(property => property.Name, 
                                        property => property.Value.ToString());
        }
    }
}

Доступ к локализованным значениям можно получить, например,

var localizedPlaceName = place.LocalizedNames[language];

Обратите внимание : В зависимости от ваших потребностей и вариантов использования вы должны рассмотреть следующие вопросы:

Кэширование

В моем фрагменте JSON string анализируется каждый раз доступ к локализованным именам. В зависимости от того, как часто вы обращаетесь к нему, это может отрицательно сказаться на производительности, которая может быть уменьшена путем кэширования результата (не забудьте удалить кэш, если установлено значение Name).

Разделение интересов

Предполагается, что класс как таковой является классом чистой модели. Возможно, вы захотите ввести классы домена, которые инкапсулируют представленные logi c, вместо добавления logi c к классу модели. Наличие фабрики, которая создает легко локализованные объекты на основе локализуемого объекта и языка, также может быть вариантом.

Обработка ошибок

В моем коде нет обработки ошибок. В зависимости от надежности ввода следует учитывать дополнительную обработку ошибок.

3 голосов
/ 21 февраля 2020

Вы можете попробовать метод расширения для сопоставления с вашим типом сущности.

public class Place
{
    [Key, Column("id")]
    public int Id { get; set; }

    [Column("name")]
    public string Name { get; set; }
}

public class PlaceDTO
{
    [Key, Column("id")]
    public int Id { get; set; }

    [Column("name")]
    public Localized<string> Name { get; set; }
}

public class Localized<T>
{
    public T en { get; set; } // english localization
    public T ar { get; set; } // arabic localization
    public T fr { get; set; } // french localization
}

Метод расширения ToDto

public static class Extensions
{
    public static PlaceDTO ToDto(this Place place)
    {
        if (place != null)
        {
            return new PlaceDTO
            {
                Id = place.Id,
                Name = JsonConvert.DeserializeObject<Localized<string>>(place.Name)
            };
        }

        return null;
    }
}

Использование

var place = new Place() { Id = 1, Name = "{\"en\":\"Sphinx\", \"ar\":\"أبو الهول\", \"fr\":\"Le sphinx\"}" };
var placeDTO = place.ToDto();

Console.WriteLine($"{placeDTO.Id}-{placeDTO.Name.ar}-{placeDTO.Name.en}-{placeDTO.Name.fr}");
2 голосов
/ 25 февраля 2020

devart.com / dotconnect / mysql / docs / EF- JSON -Поддержка. html

Как и то, что сказал @Nkosi

В таком случае, взгляните на эту статью devart.com/dotconnect/mysql/docs/EF-JSON-Support.html

Вероятно, это возможно, учитывая, что библиотека смогла встроить эту функцию. Вам нужно будет выяснить, что они сделали (обратный инжиниринг)

1 голос
/ 01 марта 2020

Я хочу, чтобы что-то в EntityFramework выбрало только один язык на Уровень базы данных без извлечения всех других данных и затем сопоставило бы это

, если Вы хотите, чтобы это было на уровне базы данных, вы всегда можете создать представление и затем включить это представление в свой проект. Пример:

CREATE VIEW `PlacesLocalized` AS
SELECT 
    Id
,   TRIM(REPLACE(name->'$.en', '"','')) AS en   
,   TRIM(REPLACE(name->'$.ar', '"','')) AS ar
,   TRIM(REPLACE(name->'$.fr', '"','')) AS fr
FROM 
    places

Это создаст класс модели Как:

public class PlacesLocalized
{
    public int Id { get; set; }

    public string en {get; set;}

    public string ar {get; set;}

    public string fr {get; set;}
}

Затем вы можете сделать:

var places = context.PlacesLocalized.Where(x=> x.en == "Sphinx");

Но если у вас нет достаточно прав, чтобы сделать это на уровне базы данных, тогда вам нужно будет указать запрос в EF. Нет простого способа изменить логи выполнения c Entity Framework только для определенных c классов. Вот почему в Entity Framework был включен метод SqlQuery, который обеспечивал бы большую гибкость при создании пользовательских запросов при необходимости (например, у вас).

Итак, если вам нужно указать локализацию из Entity Framework, вы должны сделать класс репозитория, чтобы указать все необходимые пользовательские запросы, включая создание любых DTO необходимых.

Основы c путь будет примерно таким:

public enum Localized
{
    English,
    Arabic,
    French
}

public class PlaceRepo : IDisposable
{
    private readonly PlacesEntityModel _context = new PlacesEntityModel();

    public List<Place> GetPlacesLocalized(Localized localized = Localized.English)
    {
        string local = localized == Localized.Arabic ? "$.ar"
                    : localized == Localized.French ? "$.fr"
                    : "$.en";

        return _context.Places.SqlQuery("SELECT Id, name-> @p0 as Name FROM places", new[] { local })
            .Select(x=> new Place { Id = x.Id, Name = x.Name.Replace("\"", string.Empty).Trim() })
            .ToList();
    }


    private bool _disposed = false;
    public void Dispose()
    {
        Dispose(true);
        GC.SuppressFinalize(this);
    }

    protected virtual void Dispose(bool disposing)
    {
        if (!_disposed)
        {
            if (disposing)
            {
                _context.Dispose();
            }
            _disposed = true;
        }
    }

    ~PlaceRepo()
    {
        Dispose(false);
    }
}

Теперь вы можете сделать это:

using(var repo = new PlaceRepo())
{
    var places = repo.GetPlacesLocalized(Localized.Arabic);
}
1 голос
/ 28 февраля 2020
public class Place
  {
    [Key, Column("id")]
    public int Id { get; set; }

    [Column("name")]
    public string Name { get; set; }

    public static explicit operator Place(PlaceDTO dto)
    {
      return new Place()
      {
        Id = dto.Id,
        Name = dto.Name
      };
    }
  }

  public class PlaceDTO
  {
    [Key, Column("id")]
    public int Id { get; set; }

    [Column("name")]
    public Localized<string> Name { get; set; }

    public static explicit operator PlaceDTO(Place pls)
    {
      return new PlaceDTO()
      {
        Id = pls.Id,
        Name = pls.Name
      };
    }
  }


  var placeDTO = (placeDto)place;

мы можем достичь этого, используя явный оператор, не используя auto mapper

1 голос
/ 27 февраля 2020

Я обычно просто использую JSON. Net, я замечаю, что другой ответ ссылается на JObject, но, не вдаваясь в то, является ли ваша модель данных правильной моделью, я обычно нахожу, что вы можете сделать:

var MyObjectInstance = JObject.Parse(myJsonString).ToObject<MyObjectType>();

Я заметил, что у вас есть атрибуты ComponentModel в вашем классе. Я не знаю, сколько из этих JSon. Net поддерживает, и вам придется исследовать это. Он определенно поддерживает некоторые атрибуты сериализации XML, а также имеет некоторые свои собственные.

Обратите внимание, что вы также можете преобразовать массив JSOn в список:

var MyObjectList = JArray.Parse(myJsonString).ToObject<IEnumerable<MyObjectType>();
...