i like to use the anotations of my datamodel for validating reasons, anotation is in Mode & Metadata defined

i like to use the anotations of my datamodel for validating reasons, the anotation is in Model+Metadata defined,

because i used ef-power-tools for revers generating my datamodel.

The sfgrid only works with anotations direct in the original model class not with the Metadata anotations.

I would be very cool to have a way, using anotations in my way.

BR 

Frank

>>

[MetadataType(typeof(DN_ABXREMetadata))]

public partial class DN_ABXRE

{

// dummy, all the columndefinitions are in the original Class generated

}

public class DN_ABXREMetadata

{

[Required]

[Editable(false)]

[Display(Name = "Re ID(int)", Description = "wird vom System automatisch vergeben", AutoGenerateField = false)]

public string ID { get; set; }


[Display(Name = "NG", Description = "Die Nutzergruppe muss existieren", AutoGenerateField = false)]

public string NG { get; set; }


[Display(Name = "Kost-Art", Description = "Kostenart HZ, WW, KW, BK", AutoGenerateField = false)]

public string KART { get; set; }

[Display(Name = "KP-ART", Description = "Kostenposition")]

public string KPART { get; set; }

[StringLength(50)]

[Editable(false)]

[Display(Name = "Service", Description = "in der Regel ist das hier der ABR1 Service", AutoGenerateField = false)]

public string SERVICE { get; set; }


[Range(1, 100)]

[Editable(true)]

[Display(Name = "Pos", Description = "legt die Darstellungsreihenfolge fest")]

<<


3 Replies

VN Vignesh Natarajan Syncfusion Team June 12, 2025 11:41 AM UTC

Hi Prank,


Greetings from Syncfusion Support.


We apologize for the delay in getting back to you and thank you for your continued patience.


We understand your requirement to utilize the DataAnnotation feature in combination with MetadataType for both UI display and validation in the Blazor Grid during CRUD operations. However, we would like to inform you that Blazor Framework itself does not currently support MetadataType for validation or UI rendering. This limitation has been confirmed in the official community forums. Please find a relevant reference below for your review:


Blazor validation not working when using Metadata class! - Microsoft Q&A 


To further validate this behavior, we created a sample using standard Blazor HTML elements and attempted to use DisplayName and validation attributes from a metadata class within an EditForm. Unfortunately, we were able to confirm that these annotations do not get applied unless they are directly declared on the model class directly.


Below is a simplified example that demonstrates this limitation:



Home.razor

@using System.ComponentModel.DataAnnotations

@using System.ComponentModel

 

<EditForm FormName="MyForm" Model="@user" OnValidSubmit="HandleValidSubmit">

    <DataAnnotationsValidator />

    <ValidationSummary />

 

    <div>

        <label>@GetDisplayName(nameof(User.Name))</label>

        <InputText id="Name" class="form-control" @bind-Value="user.Name" />

        <ValidationMessage For="@(() => user.Name)" />

    </div>

 

    <div>

        <label>@GetDisplayName(nameof(User.Email))</label>

        <InputText id="Email" class="form-control" @bind-Value="user.Email" />

        <ValidationMessage For="@(() => user.Email)" />

    </div>

 

    <div>

        <label>@GetDisplayName(nameof(User.Age))</label>

        <InputNumber id="Age" class="form-control" @bind-Value="user.Age" />

        <ValidationMessage For="@(() => user.Age)" />

    </div>

 

    <button type="submit">Submit</button>

</EditForm>

 

@code {

    private User user = new();

 

    private void HandleValidSubmit()

    {

        // Handle form submission

    }

 

    string GetDisplayName(string propertyName)

    {

        var prop = typeof(User).GetProperty(propertyName);

        var attr = prop?.GetCustomAttributes(typeof(DisplayNameAttribute), true)

                        .FirstOrDefault() as DisplayNameAttribute;

        return attr?.DisplayName ?? propertyName;

    }

}

 

User.cs

User.Cs

UserMetaData

[MetadataType(typeof(UserMetadata))]

public partial class User

{

    public string Name { get; set; }

    public string Email { get; set; }

    public int Age { get; set; }

}

 

public class UserMetadata

{

    [Required(ErrorMessage = "Name is required.")]

    [StringLength(100)]

    [DisplayName("Full Name")]

    [Editable(true)]

    public string Name { get; set; }

 

    [EmailAddress(ErrorMessage = "Invalid email format.")]

    [DisplayName("Email Address")]

    [Editable(true)]

    public string Email { get; set; }

 

    [Range(18, 100)]

    [DisplayName("Age (in years)")]

    [Editable(true)]

    public int Age { get; set; }

}

 


When the form is submitted, no validation messages are shown, and DisplayName attributes are not reflected in the UI — because the annotations are not recognized from the metadata class.


Since Blazor does not currently support reading validation or UI-related attributes from a MetadataType, we recommend applying your DataAnnotations (like [Required], [DisplayName], etc.) directly to the model class. When this is done, Blazor validation will function as expected, including in Syncfusion Grid components.


Unfortunately, due to this framework limitation, the requested behavior cannot currently be achieved via metadata classes — either in native Blazor or in Syncfusion components.


Please let us know if you would like any further assistance 


Regards,

Vignesh Natarajan


Attachment: BlazorApp1_673dad62.zip


FP Frank Pawellek June 13, 2025 02:43 PM UTC

ich created a solution for my issue:

put it in the createhandler of sfgrid (


)

(+) in programs.cs:

TypeDescriptor.AddProvider(

new AssociatedMetadataTypeTypeDescriptionProvider(typeof( MyModel ), typeof(MyModelMetadata)),

typeof( MyModel ));

=>

public void ReadAnotationsToGrid ( object args )

{

// Ermittle den tatsächlichen Modeltyp aus dem generischen TItem

Type actualType = typeof(TItem);

string className = actualType.FullName.Split('.').Last();

string fullTypeName = $"{actualType.Namespace}.{className}"; // z. B. "MeineAnwendung.Models.MyEntity"


// Versuche, den Typ mittels vollständig qualifiziertem Namen zu ermitteln

Type modelType = Type.GetType(fullTypeName) ?? actualType.Assembly.GetType(fullTypeName);

if (modelType == null)

{

throw new Exception("Modeltyp konnte nicht ermittelt werden");

}

// Verwende den TypeDescriptor, um alle Properties zusammen – inklusive MetadataAttribute – zu erhalten

PropertyDescriptorCollection properties = TypeDescriptor.GetProperties(modelType);


// Iteriere über alle Spalten des Grids

foreach (var col in myGrid.Columns)

{

// Default, when nothing ist set

if (col.Width == null || col.Width == "0" || col.Width == "0px" || col.Width == "0.00px" || col.Width == "0.00" || col.Width == "0.00%")

{

col.Width = "150"; // Setzt die Breite auf 150px, wenn keine Breite gesetzt ist

}

if (col.MinWidth == null || col.MinWidth == "0" || col.MinWidth == "0px" || col.MinWidth == "0.00px" || col.MinWidth == "0.00" || col.MinWidth == "0.00%")

{

col.MinWidth = "150"; // Setzt die Mindestbreite auf 150px, wenn keine Mindestbreite gesetzt ist

}


// Suche das PropertyDescriptor anhand des Spalten-Feldnamens (muss exakt übereinstimmen)

PropertyDescriptor propertyDescriptor = properties.Find(col.Field, true);

if (propertyDescriptor != null)

{

// Lese das DisplayAttribute aus der zusammengeführten Attributliste

DisplayAttribute displayAttr = propertyDescriptor.Attributes[typeof(DisplayAttribute)] as DisplayAttribute;

// Console.WriteLine($"Processing column: {col.Field}" );

if (displayAttr != null && !string.IsNullOrEmpty(displayAttr.Name))

{

Console.WriteLine($"Processing column: {col.Field} Header: {displayAttr.Name}");

col.HeaderText = displayAttr.Name;

}

// Lese das EditableAttribute aus – so kannst du die Editierbarkeit festlegen

EditableAttribute editableAttr = propertyDescriptor.Attributes[typeof(EditableAttribute)] as EditableAttribute;

if (editableAttr != null)

{

col.AllowEditing = editableAttr.AllowEdit;

}


// Falls das Property ein boolescher Typ ist, setze den spezifischen Editor

if (propertyDescriptor.PropertyType == typeof(bool) || propertyDescriptor.PropertyType == typeof(bool?))

{

col.EditType = EditType.BooleanEdit;

}

// Lese das Custom GridColumnAttribute aus

GridColumnAttribute gridColumnAttr = propertyDescriptor.Attributes[typeof(GridColumnAttribute)] as GridColumnAttribute;

if (gridColumnAttr != null)

{

if (!string.IsNullOrEmpty(gridColumnAttr.Width))

col.Width = gridColumnAttr.Width;


if (!string.IsNullOrEmpty(gridColumnAttr.MinWidth))

col.MinWidth = gridColumnAttr.MinWidth;


if (gridColumnAttr.EditType != null)

col.EditType = gridColumnAttr.EditType; // Beispiel: Spalten als Dropdown bearbeiten

}


// ####################################################################### fp 06/2025

// Hole das RangeAttribute aus der zusammengeführten Attributliste

// Erzeuge ein neues ValidationRules-Objekt, das von ValidationRuleBase erbt.

ValidationRules rules = new ValidationRules();

// 1. Prüfe, ob ein Required-Attribut vorhanden ist.

RequiredAttribute reqAttr = propertyDescriptor.Attributes[typeof(RequiredAttribute)] as RequiredAttribute;

if (reqAttr != null)

{

rules.Required = true;

}


// 2. Range-Attribut: Setze Min, Max und den Array-Bereich.

RangeAttribute rangeAttr = propertyDescriptor.Attributes[typeof(RangeAttribute)] as RangeAttribute;

if (rangeAttr != null)

{

// Konvertiere Minimum und Maximum in int, da in deiner Definition Min und Max als int? definiert sind.

int minValue = Convert.ToInt32(rangeAttr.Minimum);

int maxValue = Convert.ToInt32(rangeAttr.Maximum);


rules.Min = minValue;

rules.Max = maxValue;

// Speichere den Bereich zusätzlich als Array in der Eigenschaft Range.

rules.Range = new object[] { minValue, maxValue };

Console.WriteLine($"Range for {col.Field}: {minValue} - {maxValue}");

}


// 3. StringLength-Attribut: Falls vorhanden, setze RangeLength, MinLength und MaxLength.

StringLengthAttribute stringLengthAttr = propertyDescriptor.Attributes[typeof(StringLengthAttribute)] as StringLengthAttribute;

if (stringLengthAttr != null)

{

rules.RangeLength = new object[] { stringLengthAttr.MinimumLength, stringLengthAttr.MaximumLength };

rules.MinLength = stringLengthAttr.MinimumLength;

rules.MaxLength = stringLengthAttr.MaximumLength;

}


// Alternativ: Falls separate [MinLength] und [MaxLength] Attribute existieren.

MinLengthAttribute minLengthAttr = propertyDescriptor.Attributes[typeof(MinLengthAttribute)] as MinLengthAttribute;

if (minLengthAttr != null)

{

rules.MinLength = minLengthAttr.Length;

}

MaxLengthAttribute maxLengthAttr = propertyDescriptor.Attributes[typeof(MaxLengthAttribute)] as MaxLengthAttribute;

if (maxLengthAttr != null)

{

rules.MaxLength = maxLengthAttr.Length;

}


// 4. Regular Expression: Lese das Pattern aus.

RegularExpressionAttribute regexAttr = propertyDescriptor.Attributes[typeof(RegularExpressionAttribute)] as RegularExpressionAttribute;

if (regexAttr != null)

{

rules.RegexPattern = regexAttr.Pattern;

}


// 5. Email: Falls das Attribut vorhanden ist, setze Email auf true.

EmailAddressAttribute emailAttr = propertyDescriptor.Attributes[typeof(EmailAddressAttribute)] as EmailAddressAttribute;

if (emailAttr != null)

{

rules.Email = true;

}


// 6. Number: Falls der Property-Typ numerisch ist, setze Number auf true.

if (propertyDescriptor.PropertyType == typeof(int) ||

 propertyDescriptor.PropertyType == typeof(double) ||

 propertyDescriptor.PropertyType == typeof(decimal))

{

rules.Number = true;

}

// Optional: Hier könnten auch noch benutzerdefinierte Nachrichten übernommen werden, sofern ein "Messages"‑Dictionary vorhanden ist.

// Beispiel: if (customMessageExists) { rules.Messages.Add("min", "Der Wert muss mind. X betragen."); }

col.ValidationRules = rules;

}

else

{

col.HeaderText = col.HeaderText.Replace("_", "-");

col.HeaderText = char.ToUpper(col.HeaderText[0]) + col.HeaderText.Substring(1).ToLower();

if (!string.IsNullOrEmpty(col.Field) && col.Field.IndexOf("_JN", StringComparison.OrdinalIgnoreCase) >= 0)

{

col.EditType = Syncfusion.Blazor.Grids.EditType.BooleanEdit; // Beispiel: Spalten als Dropdown bearbeiten

}

}

if (col.Field.Equals("ID", StringComparison.OrdinalIgnoreCase))

col.IsPrimaryKey = true;

if (!string.IsNullOrEmpty(col.Field) && col.Field.IndexOf("ID", StringComparison.OrdinalIgnoreCase) >= 0)

{

col.Visible = false; // Beispiel: Spalten ausblenden

}

}

}



VN Vignesh Natarajan Syncfusion Team June 16, 2025 05:37 AM UTC

Hi Frank, 


Thanks for the update and further details. 

We are glad to hear that you have resolved your issue on your own. 

Please get back to us if you have further queries.


Regards,
Vignesh Natarajan

Loader.
Up arrow icon