Κατάσταση πρόσβασης στους προσαρμοσμένους μετατροπείς System.Text.Json – Steve Gordon

Κατάσταση πρόσβασης στους προσαρμοσμένους μετατροπείς System.Text.Json – Steve Gordon

Januar 23, 2023 0 Von admin

Σε αυτήν την ανάρτηση, περιγράφω διάφορες τεχνικές που μπορούν να παρέχουν πρόσθετη κατάσταση στους προσαρμοσμένους JsonConverters όταν χρησιμοποιείτε το System.Text.Json.

Κατά την κατασκευή του νέου Πελάτης .NET για Elasticsearch, ένας από τους βασικούς στόχους που έθεσα στον εαυτό μου ήταν να απομακρυνθώ από τον εσωτερικό σειριοποιητή που βασίζεται σε Utf8Json που χρησιμοποιείται στο v7. Η προφανής επιλογή ήταν να εξετάσουμε τον επανασχεδιασμό της σειριοποίησης χρησιμοποιώντας το System.Text.Json.

Το System.Text.Json παρουσιάστηκε ως μέρος της έκδοσης .NET Core 3.0. Από εκείνη την κυκλοφορία, αποστέλλεται „in the box“ ως μέρος των βιβλιοθηκών της βασικής κατηγορίας. Διατίθεται επίσης ως πακέτο NuGet συμβατό με .NET Standard, το οποίο μπορεί επίσης να καταναλωθεί από έργα .NET Framework. Αυτός ο χώρος ονομάτων και οι τύποι του υποστηρίζουν (απο)σειριοποίηση του JSON προς/από τύπους .NET. Σχεδιάστηκε για να παρέχει μια σύγχρονη βιβλιοθήκη JSON ως μέρος του BCL, με επίκεντρο την υψηλή απόδοση.

System.Text.Json

Το System.Text.Json είναι μια καλή επιλογή για τον πελάτη v8 Elasticsearch για .NET για διάφορους λόγους:

  • Περιλαμβάνεται ως μέρος του BCL για πολλές εκδόσεις και επομένως απαιτεί λιγότερες πρόσθετες εξαρτήσεις.
  • Ο σχεδιασμός υψηλής απόδοσης είναι κατάλληλος για τους μεγάλους φόρτους εργασίας JSON.
  • Η Microsoft το υποστηρίζει πλήρως.
  • Μπορούμε να αφαιρέσουμε πολύπλοκο και δύσκολο στη συντήρηση κώδικα από τη διάταξη πελάτη μας.

Μερικοί τύποι που χρησιμοποιούνται στον πελάτη είναι αρκετά περίπλοκοι στη μοντελοποίηση και παρουσιάζουν επιπλέον προκλήσεις για σειριοποίηση. Συγκεκριμένα, το Elasticsearch χρησιμοποιεί αιτήματα και απαντήσεις που μπορούν να περιλαμβάνουν πολυμορφικά δεδομένα. Για παράδειγμα, ένα από τα πολλά πιθανά ερωτήματα μπορεί να σταλεί κατά την εκτέλεση μιας αναζήτησης. Κάθε ερώτημα μπορεί να έχει διαφορετικές ιδιότητες. Ομοίως, οι απαντήσεις αναζήτησης μπορεί να περιλαμβάνουν μια ποικιλία διαφορετικών συναθροίσεων.

Η λύση για αυτούς τους πιο σύνθετους τύπους στο πρόγραμμα-πελάτη Elasticsearch .NET είναι η αξιοποίηση προσαρμοσμένων μετατροπέων. Με τους προσαρμοσμένους μετατροπείς, έχουμε τον πλήρη έλεγχο της ανάγνωσης και της εγγραφής του JSON για τη σειριοποίησή του προς και από τα αντικείμενά μας. Το πρόγραμμα-πελάτης v8 Elasticsearch .NET περιλαμβάνει πολλούς προσαρμοσμένους μετατροπείς, μερικούς κατασκευασμένους με μη αυτόματο τρόπο και μερικούς που δημιουργούνται με κώδικα.

Σε ορισμένες περιπτώσεις, αυτοί οι μετατροπείς απαιτούν πρόσβαση σε επιπλέον κατάσταση για τη σωστή σειριοποίηση των τύπων. Ένα χαρακτηριστικό παράδειγμα είναι όταν έχουμε τύπους που περιλαμβάνουν ονόματα πεδίων. Στον πελάτη, υποστηρίζουμε μια έννοια συμπερασμάτων, όπου οι ιδιότητες σε έναν τύπο μπορούν να χρησιμοποιηθούν για να συμπεράνουμε το όνομα πραγμάτων, όπως πεδία. Αυτό μειώνει τη χρήση μαγικών χορδών που μπορεί να είναι αρκετά εύθραυστες. Χρησιμοποιούμε πληροφορίες από το στιγμιότυπο ElasticsearchClientSettings για να συμπεράνουμε σωστά τα ονόματα πεδίων με βάση οποιαδήποτε διαμόρφωση μπορεί να έχει δώσει ο χρήστης.

Το αποτέλεσμα είναι ότι χρειαζόμαστε πρόσβαση στην παρουσία ElasticsearchClientSettings σε πολλούς προσαρμοσμένους μετατροπείς. Ευτυχώς, υπάρχουν μερικοί τρόποι για να το πετύχετε αυτό. Θα ξεκινήσουμε με τον τεχνικά σωστό τρόπο να το χειριστούμε πριν μάθουμε για μερικά από τα μειονεκτήματα και τους περιορισμούς που επιβάλλει.

Εγγραφή μετατροπέων πελατών στο JsonSerializerOptions

Οι προσαρμοσμένοι μετατροπείς μπορούν να εγγραφούν για τύπους με διάφορους τρόπους. Μπορούμε να προσθέσουμε ένα χαρακτηριστικό σε τύπους ή ιδιότητες καθορίζοντας τον συγκεκριμένο μετατροπέα που πρέπει να χρησιμοποιηθεί. Σε αυτό το σενάριο, μια παρουσία του μετατροπέα δημιουργείται κατ‘ απαίτηση την πρώτη φορά που ένας τύπος (απο)σειριοποιείται και αποθηκεύεται στην προσωρινή μνήμη για επαναχρησιμοποίηση σε επόμενες λειτουργίες σειριοποίησης. Όταν χρησιμοποιείται αυτή η προσέγγιση, οι μετατροπείς πρέπει να περιλαμβάνουν έναν προεπιλεγμένο κατασκευαστή χωρίς παραμέτρους, έτσι ώστε να μπορεί να δημιουργηθεί μια παρουσία όταν χρειάζεται. Αυτή η απαίτηση καθιστά δύσκολη την παροχή πρόσθετης κατάστασης για τον μετατροπέα.

Η λύση είναι να καταχωρήσετε μια παρουσία του μετατροπέα με την παρουσία JsonSerializerOptions. Χρησιμοποιώντας αυτόν τον μηχανισμό, μπορούμε να ελέγξουμε τη δημιουργία των στιγμιότυπων του μετατροπέα και να καλέσουμε άλλους κατασκευαστές, περνώντας τους επιπλέον κατάσταση μέσω των ορισμάτων τους.

Πάρτε, για παράδειγμα, αυτόν τον απλοποιημένο FieldConverter:

internal sealed class FieldConverter : JsonConverter<Field>
{
	private readonly IElasticsearchClientSettings _settings;

	public FieldConverter(IElasticsearchClientSettings settings) => _settings = settings;

	public override Field? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
	{
		// Omitted for brevity
	}

	public override void Write(Utf8JsonWriter writer, Field value, JsonSerializerOptions options)
	{
		if (value is null)
		{
			writer.WriteNullValue();
			return;
		}

		var fieldName = _settings.Inferrer.Field(value);

		if (string.IsNullOrEmpty(value.Format))
		{
			writer.WriteStringValue(fieldName);
		}
		else
		{
			writer.WriteStartObject();
			writer.WritePropertyName("field");
			writer.WriteStringValue(fieldName);
			writer.WritePropertyName("format");
			writer.WriteStringValue(value.Format);
			writer.WriteEndObject();
		}
	}
}

Ο κατασκευαστής αποδέχεται και αποθηκεύει μια παρουσία του IElasticsearchClientSettings. Η μέθοδος Write αποκτά πρόσβαση στο στιγμιότυπο ρυθμίσεων για να συμπεράνει το όνομα του πεδίου για το στιγμιότυπο πεδίου που είναι σε σειρά. Συνεπώς, δεν μπορώ να αντιστοιχίσω αυτόν τον μετατροπέα στον τύπο πεδίου χρησιμοποιώντας ένα χαρακτηριστικό.

Αντίθετα, πρέπει να καταχωρήσω το στιγμιότυπο με το JsonSerializerOptions για το σειριακό πρόγραμμα αιτήματος/απόκρισης.

public DefaultRequestResponseSerializer(IElasticsearchClientSettings settings)
{
	Options = new JsonSerializerOptions
	{	
		DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull,
		IncludeFields = true,
		Converters =
			{
				new FieldConverter(settings),
				// Many other converters
			},
		PropertyNamingPolicy = JsonNamingPolicy.CamelCase
	};

	_settings = settings;
}

Αυτό δεν είναι πολύ επώδυνο όταν ο αριθμός των προσαρμοσμένων μετατροπέων παραμένει μικρός. Είναι αναμφισβήτητα λίγο πιο προβληματικό όταν υπάρχουν πολλοί μετατροπείς. Το κύριο μέλημα είναι ότι με αυτήν την προσέγγιση, δημιουργούμε ένα παράδειγμα κάθε μετατροπέα εκ των προτέρων πριν χρησιμοποιηθεί ποτέ. Εφόσον δεν μπορούμε να γνωρίζουμε εκ των προτέρων ποιες δυνατότητες της βιβλιοθήκης μπορούν να χρησιμοποιήσουν οι καταναλωτές, ενδέχεται να εκχωρούμε ορισμένους μετατροπείς που δεν απαιτούνται ποτέ. Αλλά αυτό δεν είναι το κύριο πρόβλημα με αυτήν την προσέγγιση.

Δημιουργημένοι μετατροπείς πελατών

Ο τύπος πεδίου που χρησιμοποιεί το FieldConverter που εμφανίζεται παραπάνω είναι μια κλάση που δημιουργήθηκε με μη αυτόματο τρόπο στη βιβλιοθήκη. Η καταχώριση του προσαρμοσμένου μετατροπέα είναι απλή, επειδή μπορώ επίσης να βεβαιωθώ με μη αυτόματο τρόπο ότι προστίθεται στις επιλογές. Ωστόσο, τώρα δημιουργούμε τους περισσότερους τύπους βιβλιοθήκης πελατών Elasticsearch .NET από μια προδιαγραφή. Αυτό σημαίνει ότι τυχόν δημιουργημένοι τύποι που απαιτούν μετατροπέα που χρησιμοποιεί τις ρυθμίσεις IElasticsearchClientSettings θα πρέπει επίσης να εγγραφούν με την παρουσία επιλογών. Είχα μερικές ιδέες για να το λύσω αυτό, αλλά καμία δεν ήταν ιδιαίτερα ευχάριστη. Είχα επίσης επίγνωση της προσπάθειάς μου να αποφύγω μια έκρηξη περιπτώσεων μετατροπέα στις επιλογές που μπορεί να μην χρειαστούν ποτέ. Προσγειώθηκα σε ένα αρκετά έξυπνο hack που λειτούργησε αρκετά καλά.

Κακή χρήση του μετατροπέα λήψης

Η προσέγγισή μου ήταν σχετικά απλή. Καθορίστε έναν προσαρμοσμένο μετατροπέα που δεν πραγματοποιεί πραγματική μετατροπή, αλλά μπορεί να ανακτηθεί όταν απαιτείται για να αρπάξει τις IElasticsearchClientSettings.

internal sealed class ExtraSerializationData : JsonConverter<ExtraSerializationData>
{
	public ExtraSerializationData(IElasticsearchClientSettings settings) => Settings = settings;

	public IElasticsearchClientSettings Settings { get; }

	public override ExtraSerializationData? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) => throw new NotImplementedException();
	public override void Write(Utf8JsonWriter writer, ExtraSerializationData value, JsonSerializerOptions options) => throw new NotImplementedException();
}

Αυτός ο μετατροπέας ορίζεται ως μετατροπέας για τον δικό του τύπο. Αυτό είναι περίεργο αλλά απολύτως εντάξει για το πώς σκόπευα να το χρησιμοποιήσω. Οι μέθοδοι ανάγνωσης και εγγραφής του δεν εφαρμόζονται. Αποδέχεται ένα στιγμιότυπο IElasticsearchClientSettings στον κατασκευαστή του και το εκθέτει μέσω μιας ιδιότητας μόνο για ανάγνωση.

Στη συνέχεια, αυτό μπορεί να καταχωρηθεί με το JsonSerializerOptions, όπως και πριν.

public DefaultRequestResponseSerializer(IElasticsearchClientSettings settings)
{
	Options = new JsonSerializerOptions
	{	
		DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull,
		IncludeFields = true,
		Converters =
			{
				new FieldConverter(settings),
				new ExtraSerializationData(settings)
				// Many other converters
			},
		PropertyNamingPolicy = JsonNamingPolicy.CamelCase
	};

	_settings = settings;
}

Τώρα το έξυπνο κομμάτι! Μπορώ να ανακτήσω τον μετατροπέα ExtraSerializationData από τις επιλογές σε μετατροπείς που δημιουργούνται από κώδικα που απαιτούν πρόσβαση στις ρυθμίσεις.

internal sealed class SortOptionsConverter : JsonConverter<SortOptions>
{
	public override SortOptions Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
	{
		// Omitted for brevity
	}

	public override void Write(Utf8JsonWriter writer, SortOptions value, JsonSerializerOptions options)
	{
		writer.WriteStartObject();
		if (value.AdditionalPropertyName is IUrlParameter urlParameter)
		{
			var extraData = options.GetConverter(typeof(ExtraSerializationData)) as ExtraSerializationData;
			var propertyName = urlParameter.GetString(extraData.Settings);
			writer.WritePropertyName(propertyName);
		}
		else
		{
			writer.WritePropertyName(value.VariantName);
		}

		// Omitted for brevity

		writer.WriteEndObject();
	}
}

[JsonConverter(typeof(SortOptionsConverter))]
public sealed partial class SortOptions
{
	// Omitted for brevity
}

Το JsonSerializerOptions εκθέτει μια μέθοδο GetConverter που δέχεται έναν Τύπο ως όρισμα. Αυτό επιλύει μια παρουσία ενός JsonConverter για τον τύπο. Η μέθοδος εγγραφής καλεί αυτήν τη μέθοδο για να πάρει τον μετατροπέα για τον τύπο ExtraSerializationData (που είναι στην πραγματικότητα ο ίδιος). Καθώς ο τύπος επιστροφής της μεθόδου GetConverter είναι JsonConverter, πρέπει να μεταδοθεί ως ExtraSerializationData.

Με την ανάκτηση του μετατροπέα, ο κώδικας μπορεί να χρησιμοποιήσει την ιδιότητα Ρυθμίσεις για πρόσβαση στην παρουσία IElasticsearchClientSettings που απαιτείται για την εκτέλεση της μετατροπής του. Όπως περιέγραψα στο Twitterαυτή είναι μια τεχνική για την οποία ήμουν εξίσου περήφανος και ντρεπόμουν ταυτόχρονα!

Πρόταση API

Μετά από αυτή την εμπειρία και μερικές απαντήσεις στο tweet μου, ήταν σαφές ότι δεν ήμουν το μόνο άτομο με αυτήν την απαίτηση. Αποφάσισα να ανοίξω ένα θέμα για το Αποθετήριο χρόνου εκτέλεσης .NET να προτείνει την επίλυση αυτού στο δημόσιο API.

Δύο λύσεις μου ήρθαν στο μυαλό. Το πρώτο ήταν να αποσφραγίσουμε τον τύπο JsonSerializerOptions, τον οποίο καταναλωτές όπως εγώ θα μπορούσαμε στη συνέχεια να επεκτείνουμε με πρόσθετες ιδιότητες. Στη συνέχεια, θα μπορούσαμε να ρίξουμε τα JsonSerializerOptions στον παράγωγο τύπο μας μέσα στους μετατροπείς για πρόσβαση στην επιπλέον κατάσταση. Μια δεύτερη επιλογή (την οποία έκανα την κύρια πρόταση) ήταν η υποστήριξη μιας τσάντας ιδιοκτησίας στον υπάρχοντα τύπο JsonSerializerOptions. Αυτό θα επέτρεπε στους καταναλωτές να αποθηκεύουν αντικείμενα σε ένα λεξικό για μελλοντική ανάκτηση.

Κατά τη στιγμή της σύνταξης, αυτή η πρόταση έχει κλείσει υπέρ του ένα άλλο ζήτημα του GitHub παρακολούθηση αυτής της απαίτησης. Ευτυχώς, Eirik Tsarpalisένας από τους προγραμματιστές της Microsoft που συμμετέχουν στο System.Text.Json, έδωσε μια άλλη λύση.

Εκσυγχρονίζω!! Το θέμα της πρότασής μου έχει πλέον ανοίξει ξανά, καθώς η εναλλακτική πρόταση δεν έλυσε την ίδια απαίτηση που προσδιορίστηκε σε αυτήν την ανάρτηση. Ας ελπίσουμε ότι και οι δύο προτάσεις μπορούν να γίνουν σε μελλοντική κυκλοφορία.

Αξιοποίηση ενός ConditionalWeakTable

Ο Eirik ρώτησε αν είχα σκεφτεί να χρησιμοποιήσω έναν ConditionalWeakTable για να επισυνάψω δυναμικά δεδομένα σε στιγμιότυπα επιλογών. Απάντησα ότι δεν είχα, καθώς αγνοούσα την ύπαρξη αυτού του τύπου! Αναφέρθηκα γρήγορα στο τεκμηρίωση για να μάθετε για το είδος.

Η τεκμηρίωση περιλαμβάνει μόνο τον ορισμό του API με μία μόνο πρόταση που συνοψίζει τον σκοπό αυτού του τύπου.

Επιτρέπει στους μεταγλωττιστές να προσαρτούν δυναμικά πεδία αντικειμένων σε διαχειριζόμενα αντικείμενα.

Υπάρχει λίγη περισσότερη λεπτομέρεια στα παραδείγματα και τις παρατηρήσεις. Στην ουσία, αυτός ο τύπος παρέχει έναν τρόπο σύνδεσης δύο στιγμιότυπων αντικειμένων χρόνου εκτέλεσης, αλλά με τέτοιο τρόπο ώστε αυτές οι παρουσίες να μην είναι ριζωμένες στο GC για πάντα. Το κλειδί είναι ένα διαχειριζόμενο αντικείμενο στο οποίο θέλουμε να επισυνάψουμε πρόσθετες ιδιότητες κατά το χρόνο εκτέλεσης.

Οι αναφορές που χρησιμοποιούνται μέσα στην υλοποίηση είναι αδύναμες και τα κλειδιά που είναι αποθηκευμένα στον πίνακα δεν διατηρούνται όταν καταστραφούν οι αναφορές στο αντικείμενο έξω από τον πίνακα. Αυτό θα ήταν ανησυχητικό μόνο στην περίπτωση της βιβλιοθήκης μου εάν ένας καταναλωτής ακυρώσει όλες τις αναφορές στο ElasticsearchClient ή/και το απορρίψει στον κώδικά του. Εάν χρησιμοποιήθηκε ένα κανονικό λεξικό αντί του ConditionalWeakTable, τότε τα JsonSerializerOptions δεν θα απορριφθούν ποτέ σε μια τέτοια περίπτωση. Στην πραγματικότητα, αυτή δεν είναι η πιθανή συμπεριφορά για τους καταναλωτές που γενικά θα πρέπει να έχουν μια μοναδική παρουσία του ElasticsearchClient για όλη τη διάρκεια της εφαρμογής τους, αλλά αυτό δεν είναι εγγυημένο.

Ακούστηκε πολλά υποσχόμενο ως μια πιο καθαρή λύση για την εργασία μου στο πρόγραμμα-πελάτη .NET, οπότε προχώρησα στην εφαρμογή του στον κώδικά μου. Θα χρειαζόμουν πρώτα μια παρουσία ενός ConditionalWeakTable για να κρατήσω τις ρυθμίσεις μου. Πρόσθεσα μια στατική ιδιότητα στον τύπο ElasticsearchClient που θα επέτρεπε στον εσωτερικό μου κώδικα να ανακτήσει τις IElasticsearchClientSettings που αντιστοιχούν σε μια παρουσία του JsonSerializerOptions που χρησιμοποιείται από τον προεπιλεγμένο σειριοποιητή αιτήματος/απόκρισης για τον πελάτη. Ονόμασα αυτήν την ιδιότητα SettingsTable.

public sealed partial class ElasticsearchClient
{
	internal static ConditionalWeakTable<JsonSerializerOptions, IElasticsearchClientSettings> SettingsTable { get; } = new();
	
	// Omitted for brevity
}

Μετά τη δημιουργία των επιλογών JsonSerializer για το σειριακό πρόγραμμα, φροντίζω επίσης να προσθέσω μια καταχώρηση στο SettingsTable, συνδέοντας αδύναμα τις επιλογές στις ρυθμίσεις IElasticsearchClient για τον πελάτη.

public DefaultRequestResponseSerializer(IElasticsearchClientSettings settings)
{
	Options = new JsonSerializerOptions
	{	
		DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull,
		IncludeFields = true,
		Converters =
			{
				new FieldConverter(settings),
				// Many other converters
			},
		PropertyNamingPolicy = JsonNamingPolicy.CamelCase
	};

	ElasticsearchClient.SettingsTable.Add(Options, settings);

	_settings = settings;
}

Στη συνέχεια, χρειαζόμουν έναν τρόπο πρόσβασης στις ρυθμίσεις κατ‘ απαίτηση μέσα στους μετατροπείς. Καθώς αυτή η εργασία θα εμφανιζόταν σε πολλά μέρη, αποφάσισα να εισαγάγω μια μέθοδο επέκτασης για αυτό:

internal static class JsonSerializerOptionsExtensions
{
	public static bool TryGetClientSettings(this JsonSerializerOptions options, out IElasticsearchClientSettings settings) =>
		ElasticsearchClient.SettingsTable.TryGetValue(options, out settings);
}

Αυτή η μέθοδος επέκτασης ορίζεται για τον τύπο JsonSerializerOptions και χρησιμοποιεί το SettingsTable για να προσπαθήσει να αναζητήσει την παρουσία ρυθμίσεων που σχετίζεται με το JsonSerializerOptions. Οι μετατροπείς, συμπεριλαμβανομένων αυτών που δημιουργούνται από κώδικα, μπορούν εύκολα να έχουν πρόσβαση στις ρυθμίσεις που χρειάζονται.

internal sealed class WildcardQueryConverter : JsonConverter<WildcardQuery>
{
	public override WildcardQuery Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
	{
		// Omitted for brevity
	}

	public override void Write(Utf8JsonWriter writer, WildcardQuery value, JsonSerializerOptions options)
	{
		if (value.Field is null)
			throw new JsonException("Unable to serialize WildcardQuery because the `Field` property is not set. Field name queries must include a valid field name.");
		if (options.TryGetClientSettings(out var settings))
		{
			writer.WriteStartObject();
			writer.WritePropertyName(settings.Inferrer.Field(value.Field));
			writer.WriteStartObject();
			// Omitted for brevity
			writer.WriteEndObject();
			writer.WriteEndObject();
			return;
		}

		throw new JsonException("Unable to retrieve client settings required to infer field.");
	}
}

Το παραπάνω παράδειγμα, που λαμβάνεται από έναν μετατροπέα που δημιουργείται από κώδικα, καλεί το TryGetClientSettings στο JsonSerializerOptions που μεταβιβάστηκε στη μέθοδο Write. Πάντα αναμένουμε ότι οι ρυθμίσεις θα είναι προσβάσιμες μέσω του ConditionalWeakTable, αλλά θα κάνουμε μια εξαίρεση εάν αυτό δεν συμβαίνει για κάποιο λόγο. Μόλις η μέθοδος μετατροπέα έχει πρόσβαση στις ρυθμίσεις, μπορεί να ολοκληρώσει τη σειριοποίησή της.

Δεδομένου ότι αυτός ο μετατροπέας δεν απαιτεί πλέον από έναν κατασκευαστή να αποδέχεται τις IElasticsearchClientSettings, δεν χρειάζεται να προστεθεί απευθείας στη συλλογή μετατροπέων που είναι καταχωρημένοι στο JsonSerializerOptions και μπορεί να δημιουργηθεί κατ‘ απαίτηση από τη βιβλιοθήκη System.Text.Json, εάν απαιτείται το.

[JsonConverter(typeof(WildcardQueryConverter))]
public sealed partial class WildcardQuery : Query
{
	// Omitted for brevity
}

Το WildcardQuery αποδίδεται με το JsonConverterAttribute για τον ορισμό του μετατροπέα του. Η παρουσία μετατροπέα δημιουργείται μόνο εάν η καταναλωτική εφαρμογή ορίζει ένα WildcardQuery, το οποίο πρέπει να σειριοποιηθεί ως μέρος του αιτήματος αναζήτησης.

ΣΗΜΕΙΩΣΗ: Ο Eric από τη Microsoft τόνισε ότι για εξαιρετικά αποτελεσματική αποφυγή της αναζήτησης ConditionalWeakTable ανά λειτουργία σειριοποίησης, θα μπορούσε να χρησιμοποιηθεί ένα ConverterFactory για την εκτέλεση αυτής της εργασίας μία φορά ανά μετατροπέα. Είναι μια καλή πρόταση την οποία θα εξετάσω, θα μετρήσω και πιθανόν να την εφαρμόσω κάποια στιγμή. Μόλις το κάνω, θα προσπαθήσω να ακολουθήσω μια νέα ανάρτηση σχετικά με αυτήν τη βελτίωση.

Περίληψη

Είμαι αρκετά ικανοποιημένος με το τελικό αποτέλεσμα που επιτρέπει η προσέγγιση ConditionalWeakTable. Επιλύει τις δύο κύριες προκλήσεις μου, επιτρέποντάς μου να αποφύγω τη δημιουργία δυνητικά αχρησιμοποίητων στιγμιότυπων μετατροπέων, απλώς και μόνο για την εγγραφή τους στο JsonSerializerOptions. Απλοποιεί τον κώδικα που δημιουργείται για μετατροπείς που μπορούν να αξιοποιήσουν τη μέθοδο επέκτασής μου για πρόσβαση στις ρυθμίσεις εάν τις απαιτούν για συμπέρασμα.

Η τάξη ConditionalWeakTable ήταν άγνωστη σε μένα και δεν ήταν μια προφανής επιλογή. Εξακολουθώ να πιστεύω ότι η βιβλιοθήκη System.Text.Json μπορεί και πρέπει να το λύσει αυτό με έναν πιο ανιχνεύσιμο τρόπο για τους καταναλωτές. Αν και είναι μια πιο προηγμένη απαίτηση, είμαι βέβαιος ότι άλλοι μπορεί να απαιτήσουν πρόσθετη στατική κατάσταση για τους μετατροπείς πελατών τους. Μέχρι αυτό Πρόβλημα χρόνου εκτέλεσης .NET επιλύεται και το .NET αποστέλλεται με μια in-the-box λύση, ίσως σκεφτείτε να χρησιμοποιήσετε έναν ConditionalWeakTable για την εργασία.