Ich habe eine MySQL-Datenbank mit den Spalten Id int und Name: json

Orts-Tabelle Beispiel

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

C # Klasse platzieren

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

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

Ich verbinde mich mit EntityFramework 6 und dem Verbindungserfolg und rufe solche Daten ab

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


Was ich möchte, wie man den Namen new Object nicht JSON string zuordnet

Etwas wie das

Klasse platzieren

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

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

Lokalisierte Klasse

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
}

Wenn ich dies tue, kommt die Eigenschaft Name mit dem Wert NULL


Code im Repository

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

Ich möchte AutoMapper nicht verwenden.

Ich möchte, dass etwas in EntityFramework nur eine Sprache in Datenbankebene auswählt, ohne alle anderen Daten abzurufen und sie dann zuzuordnen

Wie kann man das beheben?

4
Abdelrahman Gobarah 21 Feb. 2020 im 11:24

6 Antworten

Beste Antwort

devart.com/dotconnect/mysql/docs/EF-JSON-Support.html

Wie das, was @Nkosi gesagt hat

In diesem Fall werfen Sie einen Blick auf diesen Artikel devart.com/dotconnect/mysql/docs/EF-JSON-Support.html

Es kann wahrscheinlich vorausgesetzt werden, dass die Bibliothek diese Funktion einbauen konnte. Sie müssten herausfinden, was sie getan haben (Reverse Engineering).

1
Community 20 Juni 2020 im 09:12
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;

Wir können dies mit einem expliziten Operator erreichen, ohne Auto Mapper zu verwenden

1
Abdelrahman Gobarah 1 März 2020 im 05:48

Ich möchte, dass in EntityFramework nur eine Sprache ausgewählt wird Datenbankebene , ohne alle anderen Daten abzurufen und dann zuzuordnen

Wenn Sie möchten, dass es auf Datenbankebene erstellt wird, können Sie jederzeit eine Ansicht erstellen und diese Ansicht dann in Ihr Projekt aufnehmen. Beispiel:

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

Dies würde eine Modellklasse erstellen.

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

    public string en {get; set;}

    public string ar {get; set;}

    public string fr {get; set;}
}

Dann können Sie Folgendes tun:

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

Wenn Sie jedoch nicht über genügend Berechtigungen verfügen, um dies auf Datenbankebene zu tun, müssen Sie die Abfrage in Ihrem EF angeben. Es gibt keine einfache Möglichkeit, die Ausführungslogik von Entity Framework nur für bestimmte Klassen zu ändern. Aus diesem Grund enthielt Entity Framework die Methode SqlQuery, die mehr Flexibilität bietet, um bei Bedarf benutzerdefinierte Abfragen (wie Ihre) durchzuführen.

Wenn Sie also die Lokalisierung in Entity Framework angeben müssen, führen Sie eine Repository-Klasse aus, um alle benutzerdefinierten Abfragen anzugeben, die Sie benötigen, einschließlich der Erstellung der erforderlichen DTO.

Der grundlegende Weg wäre ungefähr so:

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);
    }
}

Jetzt können Sie dies tun:

using(var repo = new PlaceRepo())
{
    var places = repo.GetPlacesLocalized(Localized.Arabic);
}
1
iSR5 1 März 2020 im 00:24

Normalerweise verwende ich nur JSON.Net. Ich stelle fest, dass eine andere Antwort auf JObject verweist. Ohne jedoch darauf einzugehen, ob Ihr Datenmodell das richtige Modell ist, können Sie im Allgemeinen Folgendes tun:

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

Ich stelle fest, dass Ihre Klasse ComponentModel-Attribute enthält. Ich weiß nicht sofort, wie viele dieser JSon.Net-Programme unterstützt werden, und das müssten Sie untersuchen. Es unterstützt definitiv einige Attribute aus der XML-Serialisierung und hat auch einige eigene.

Beachten Sie, dass Sie ein JSOn-Array auch in eine Liste konvertieren können:

var MyObjectList = JArray.Parse(myJsonString).ToObject<IEnumerable<MyObjectType>();
1
Mark Rabjohn 27 Feb. 2020 im 11:03

Zunächst beschränken Sie sich durch die Verwendung einer Klasse mit einer Eigenschaft pro Sprache. Sie müssten immer neue Eigenschaften hinzufügen, wenn Sie neue Sprachen hinzufügen, was natürlich machbar, aber unnötig kompliziert wäre. Darüber hinaus haben Sie normalerweise die Sprache als string-ish Objekt (oder können konvertieren), daher würde dies zu Code wie diesem führen

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

Das ist fehleranfällig und zu kompliziert. Für Ihr Problem würde ich mich für eine Art Wörterbuch entscheiden

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

Dies ist leicht erweiterbar, indem einfach mehr Übersetzungen zum Wörterbuch hinzugefügt werden.

Um Ihren JSON string in einen IDcitionary<string, string> zu konvertieren, können Sie den folgenden Code verwenden

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

Aus Ihrer Klasse heraus wäre dies effektiv

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());
        }
    }
}

Auf die lokalisierten Werte kann wie zugegriffen werden

var localizedPlaceName = place.LocalizedNames[language];

Bitte beachten Sie : Abhängig von Ihren Anforderungen und Anwendungsfällen sollten Sie die folgenden Punkte berücksichtigen:

Caching

In meinem Snippet wird der JSON string bei jedem Zugriff auf die lokalisierten Namen analysiert. Abhängig davon, wie oft Sie darauf zugreifen, kann dies die Leistung beeinträchtigen, was durch das Zwischenspeichern des Ergebnisses verringert werden kann (vergessen Sie nicht, den Cache zu löschen, wenn Name festgelegt ist).

Trennung von Bedenken

Die Klasse wie sie ist soll eine reine Modellklasse sein. Möglicherweise möchten Sie Domänenklassen einführen, die die dargestellte Logik kapseln, anstatt die Logik der Modellklasse hinzuzufügen. Eine Fabrik, die leicht lokalisierte Objekte basierend auf dem lokalisierbaren Objekt und der Sprache erstellt, könnte ebenfalls eine Option sein.

Fehlerbehandlung

In meinem Code gibt es keine Fehlerbehandlung. Abhängig von der Zuverlässigkeit der Eingabe sollten Sie eine zusätzliche Fehlerbehandlung in Betracht ziehen.

3
Paul Kertscher 21 Feb. 2020 im 09:21

Sie können versuchen, die Erweiterungsmethode aus Ihrem Entitätstyp zuzuordnen.

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
}

Erweiterungsmethode 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;
    }
}

Verwendung

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}");
3
Krishna Muppalla 21 Feb. 2020 im 09:18