-
-
Notifications
You must be signed in to change notification settings - Fork 16
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Issue with ApplyObjectFromDictionary Method Handling Integer Values as Long #29
Comments
Hi! From what I understood, you're doing a manual deserialization which interprets a integer as Could you show how you are passing such object to the component? |
Could you give an update on this? |
I'm closing for now. If you get back with more info, we can reopen. |
I would like you to update the ApplyObjectFromDictionary method with the modified code. I'll show you the code. This code got the error "System.InvalidCastException: Type mismatch in uid parameter." Test.cshtml var data = new Dictionary<string, object>
{
{ "Uid", uid },
{ "Title", title }
};
//open a dialog with dispatch
Dispatch(new OpenDialog("Popup", data, dialogOptions), Scope.Global); Dialog.cshtml @if (data is null)
{
<hydro name="@name" />
}
else
{
<hydro name="@name" params="@data" />
} Popup.cshtml @model Popup
<div>
@Model.uid
@Model.title
</div> Popup.cshtml.cs public class Popup : HydroComponent
{
public int uid { get; set; }
public string title { get; set; } = string.Empty;
public override async Task MountAsync()
{
await base.MountAsync();
}
} |
Popup.cshtml.cs public class Popup : HydroComponent
{
public int uid { get; set; } // if long uid { get; set;}, it's not mismatch.
public string title { get; set; } = string.Empty;
public override async Task MountAsync()
{
await base.MountAsync();
}
} |
Is there any reason to use Dictionary instead of object? Your code: var data = new Dictionary<string, object>
{
{ "Uid", uid },
{ "Title", title }
};
//open a dialog with dispatch
Dispatch(new OpenDialog("Popup", data, dialogOptions), Scope.Global); Could be: var data = new { Uid = uid, Title = title };
Dispatch(new OpenDialog("Popup", data, dialogOptions), Scope.Global); |
I remember that the data variable was changed to Newtonsoft.Json.Linq.JObject type when the value was transferred through dispatch on the test page, and the parameter was not properly processed, so I used the dictionary type. |
How do you get your data that you use in file Passing data as a dictionary through <hydro name="@name" params="@(new { DialogData = data })" /> The reason of doing that is data in this case is an anonymous I could make it that |
[Dialogs.cshtml.cs] public class Dialogs : HydroComponent
{
//For Dialog
public Dictionary<string, IDictionary<string, object>?> DialogsDictionary { get; set; } = default!;
public bool IsOpen { get; set; }
public Dialogs()
{
Subscribe<OpenDialog>(HandleOpenDialog);
Subscribe<CloseDialog>(HandleCloseDialog);
}
private void HandleOpenDialog(OpenDialog dialog)
{
DialogsDictionary.Clear();
DialogsDictionary.TryAdd(dialog.Name, dialog.Data);
IsOpen = true;
ViewBag.DialogOptions = dialog.DialogOptions;
}
private void HandleCloseDialog(CloseDialog dialog)
{
DialogsDictionary.Clear();
IsOpen = false;
}
} [Dialogs.cshtml] @model Dialogs
@using Hydro
@using Hydro.TagHelpers
@using System.Text.Json
@using Newtonsoft.Json
@using Newtonsoft.Json.Linq
@using System.Dynamic
@{
var options = ViewBag.DialogOptions as DialogOptions ?? new DialogOptions();
var shake = !Model.ModelState.IsValid;
}
<div>
@foreach (var (name, data) in Model.DialogsDictionary)
{
<div class="overlay h-screen w-full bg-black bg-opacity-30 fixed top-0 left-0 z-10 flex items-center justify-center @(Model.IsOpen ? "" : "hidden")">
<div class="modal z-20 bg-white rounded-md absolute overflow-y-auto @options.DialogWidth @options.DialogHeight @options.DialogClass">
<div class="modal-header flex justify-between px-5 py-3 bg-gray-800 text-white font-bold @options.HeaderClass">
<p>
@options.Title
</p>
<p>
@if (!string.IsNullOrEmpty(options.RedirectUrl))
{
<button class="border px-2 py-[1px]" hydro-on:click="@(() => Model.CloseThisDialog(options.RedirectUrl))">X</button>
}
else
{
<button class="border px-2 py-[1px]" hydro-on:click="@(() => Model.CloseThisDialog(string.Empty))">X</button>
}
</p>
</div>
<div class="modal-body overflow-y-auto p-4 @options.BodyClass">
@if (data is null)
{
<hydro name="@name" />
}
else
{
<hydro name="@name" params="@data" />
}
</div>
</div>
</div>
}
</div> When I turn over the data in the way below, it's converted to a Newtonsoft.Json.Linq.JObject type. var data = new { Uid = uid, Title = title };
Dispatch(new OpenDialog("Popup", data, dialogOptions), Scope.Global); |
In order to make it work you would need to define public class Popup : HydroComponent
{
public PopupData DialogData { get; set; }
public override async Task MountAsync()
{
await base.MountAsync();
}
}
public class PopupData
{
public int Uid { get; set; }
public string Title { get; set; } = string.Empty;
} And then pass the data to it using <hydro name="@name" params="@(new { DialogData = data })" /> Such code will work. The reason is that we can't pass a generic event's data object as a set of root properties to Hydro component. You need to create a root property on component to which you assign your event's data object, in this case |
Okay, I understood. Can't you update Method ApplyObjectFromDictionary the added code below? private void ApplyObjectFromDictionary<T>(T target, IDictionary<string, object> source)
{
if (source == null || target == null)
{
return;
}
var targetType = target.GetType();
foreach (var sourceProperty in source)
{
var targetProperty = targetType.GetProperty(sourceProperty.Key, BindingFlags.Instance | BindingFlags.Public | BindingFlags.IgnoreCase);
if (targetProperty == null || !targetProperty.CanWrite)
{
continue;
}
var value = sourceProperty.Value;
if (value != null)
{
// Convert ChangeType
if (value != null && !targetProperty.PropertyType.IsInstanceOfType(value))
{
try
{
value = Convert.ChangeType(value, targetProperty.PropertyType);
}
catch (Exception ex)
{
throw new InvalidCastException($"Type mismatch in {sourceProperty.Key} parameter: {ex.Message}");
}
}
targetProperty.SetValue(target, value);
}
}
} [Existing code] if (value != null && !targetProperty.PropertyType.IsInstanceOfType(value))
{
throw new InvalidCastException($"Type mismatch in {sourceProperty.Key} parameter.");
} [Modified code] if (value != null && !targetProperty.PropertyType.IsInstanceOfType(value))
{
try
{
value = Convert.ChangeType(value, targetProperty.PropertyType);
}
catch (Exception ex)
{
throw new InvalidCastException($"Type mismatch in {sourceProperty.Key} parameter: {ex.Message}");
}
} |
Like I mentioned before, the method |
Closing for now, the issue can be fixed by suggested workaround, which is also recommended in similar situations. If you have any questions, feel free to open the discussion. |
There is an issue with the ApplyObjectFromDictionary method where integer values in the IDictionary<string, object> source are being interpreted as long during debugging. This causes an InvalidCastException when attempting to set properties on the target object if the target property is of type int.
This issue is particularly problematic when working with JSON deserialization where numeric values are commonly interpreted as long. The proposed solution ensures compatibility with various numeric types and enhances the robustness of the method.
Example Code
Modified Code
The text was updated successfully, but these errors were encountered: