Fix more anonymous-object serialization crashes under trimming

GoogleMap (createMap/updateMap JS interop) and AIChatService (chat API
payload via ConversationSession) both serialized anonymous types, which
fail with System.Text.Json ConstructorContainsNullParameterNames under
trimming. Replaced with named types and preserved them in LinkerConfig.xml.
This commit is contained in:
Vladimir Enchev
2026-06-24 18:30:22 +03:00
parent 8497e33f60
commit 9019bfba64
4 changed files with 52 additions and 10 deletions

View File

@@ -53,13 +53,13 @@ public class AIChatService(IServiceProvider serviceProvider, IOptions<AIChatServ
// Get formatted messages including conversation history
var messages = session.GetFormattedMessages(systemPrompt ?? Options.SystemPrompt);
var payload = new
var payload = new ChatCompletionRequest
{
model = model ?? Options.Model,
messages = messages,
temperature = temperature ?? Options.Temperature,
max_tokens = maxTokens ?? Options.MaxTokens,
stream = true
Model = model ?? Options.Model,
Messages = messages,
Temperature = temperature ?? Options.Temperature,
MaxTokens = maxTokens ?? Options.MaxTokens,
Stream = true
};
using var request = new HttpRequestMessage(HttpMethod.Post, url)
@@ -231,3 +231,30 @@ public class AIChatService(IServiceProvider serviceProvider, IOptions<AIChatServ
}
}
}
internal class ChatCompletionMessage
{
[JsonPropertyName("role")]
public string? Role { get; set; }
[JsonPropertyName("content")]
public string? Content { get; set; }
}
internal class ChatCompletionRequest
{
[JsonPropertyName("model")]
public string? Model { get; set; }
[JsonPropertyName("messages")]
public List<object>? Messages { get; set; }
[JsonPropertyName("temperature")]
public double Temperature { get; set; }
[JsonPropertyName("max_tokens")]
public int? MaxTokens { get; set; }
[JsonPropertyName("stream")]
public bool Stream { get; set; }
}

View File

@@ -78,12 +78,12 @@ public class ConversationSession
var messages = new List<object>();
// Add system message
messages.Add(new { role = "system", content = systemPrompt });
messages.Add(new ChatCompletionMessage { Role = "system", Content = systemPrompt });
// Add conversation messages
foreach (var message in Messages)
{
messages.Add(new { role = message.Role, content = message.Content });
messages.Add(new ChatCompletionMessage { Role = message.Role, Content = message.Content });
}
return messages;

View File

@@ -13,6 +13,12 @@
<type fullname="Radzen.DropDownItem`1" preserve="all" />
<!-- Preserve OData service result types for JSON deserialization -->
<type fullname="Radzen.ODataServiceResult`1" preserve="all" />
<!-- Preserve Google Map marker types serialized to JS interop -->
<type fullname="Radzen.Blazor.GoogleMapMarkerData" preserve="all" />
<type fullname="Radzen.GoogleMapPosition" preserve="all" />
<!-- Preserve AI chat request types serialized to the chat API -->
<type fullname="Radzen.ChatCompletionRequest" preserve="all" />
<type fullname="Radzen.ChatCompletionMessage" preserve="all" />
<!-- Preserve all types with [JSInvokable] methods (called from JS, invisible to trimmer) -->
<type fullname="Radzen.DialogService" preserve="methods" />
<type fullname="Radzen.Blazor.RadzenChart" preserve="methods" />

View File

@@ -211,12 +211,12 @@ namespace Radzen.Blazor
if (firstRender)
{
_jsRef = await JSRuntime.InvokeAsync<IJSObjectReference>("Radzen.createMap", Element, Reference, UniqueID, ApiKey, MapId, Zoom, Center,
data.Select(m => new { Title = m.Title, Label = m.Label, Position = m.Position }), Options, FitBoundsToMarkersOnUpdate, Culture.TwoLetterISOLanguageName);
data.Select(m => new GoogleMapMarkerData { Title = m.Title, Label = m.Label, Position = m.Position }), Options, FitBoundsToMarkersOnUpdate, Culture.TwoLetterISOLanguageName);
}
else
{
await JSRuntime.InvokeVoidAsync("Radzen.updateMap", UniqueID, ApiKey, null, null,
data.Select(m => new { Title = m.Title, Label = m.Label, Position = m.Position }), Options, FitBoundsToMarkersOnUpdate, Culture.TwoLetterISOLanguageName);
data.Select(m => new GoogleMapMarkerData { Title = m.Title, Label = m.Label, Position = m.Position }), Options, FitBoundsToMarkersOnUpdate, Culture.TwoLetterISOLanguageName);
}
}
}
@@ -235,4 +235,13 @@ namespace Radzen.Blazor
GC.SuppressFinalize(this);
}
}
internal class GoogleMapMarkerData
{
public string? Title { get; set; }
public string? Label { get; set; }
public GoogleMapPosition? Position { get; set; }
}
}