最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

entity framework core - DataAnnotations being ignored in Blazor server-side .NET 9 page - Stack Overflow

programmeradmin5浏览0评论

I have a new .NET 9 Razor page Blazor server-side project.

I used EF Core Power Tools to import my database schema and generate the DbContext and model classes.

I created a MetaData class for one of my model classes and I'm using a call to TypeDescriptor.AddProviderTransparent to attach the metadata class to the class that was generated by EF Core Power Tools.

I'm attempting to do the same thing shown here: /add-dataannotations-metadata-to-ef-core-in-a-partial-class

But, the metadata appears to be not getting used at all.

Generated model code:

[Table("AsphaltIndexPrice")]
public partial class AsphaltIndexPrice 
{
    [Key]
    public int AsphaltIndexPriceId { get; set; }

    [Column(TypeName = "datetime")]
    public DateTime EffectiveDate { get; set; }

    [Column(TypeName = "money")]
    public decimal IndexPrice { get; set; }
}

Metadata class:

public class AsphaltIndexPriceMetaData 
{
    [Display(Name = "Asphalt Index Price Id")]
    public int AsphaltIndexPriceId { get; set; }

    [Required(ErrorMessage="An Effective Date is required")]
    [Display(Name = "Effective Date")]
    public DateTime EffectiveDate { get; set; }

    [Required(ErrorMessage = "An Index Price is required")]
    [DataType(DataType.Currency)]
    [Display(Name = "Price")]
    public decimal IndexPrice { get; set; }
}

Code for attaching the metadata class to the model class:

public static void HMADBMetaDataInit() 
{
     AddMetaData(typeof(AsphaltIndexPrice), typeof(AsphaltIndexPriceMetaData));
}

private static void AddMetaData(Type baseClass, Type metaDataClass) 
{
     TypeDescriptor.AddProviderTransparent(
         new AssociatedMetadataTypeTypeDescriptionProvider(
             baseClass,
             metaDataClass),
         baseClass);
 }

The call to setup the metadata, in Program.cs:

HMADBHelper.HMADBMetaDataInit();

app.Run();

I also put the same call to HMADBMetaDataInit() in the OnInitialize of a page, when just having it in Program.cs didn't work. That made no difference.

Ways that tell me the metadata is being ignored:

On the screen where EffectiveDate and IndexPrice are shown, the labels generated are "EffectiveDate" and "IndexPrice", instead of "Effective Date" (with a space) and "Price" (no word "Index").

Also, on the Edit screen, if I blank out the EffectiveDate and the IndexPrice fields and click to save them, I get error messages that the values are not valid or not in the right range, but I do not get the specific text from above, e.g. "An Index Price is required".

I've stepped over the calls to AddProviderTransparent in the debugger and I don't see any errors or other information in the output window to explain why it doesn't seem to be working.

What am I doing wrong?

Update

Per Simon's comment, I changed Program.cs to call my metadata initialization before calling builder.Build(). No change in application behavior.

HMADBHelper.HMADBMetaDataInit();

var app = builder.Build();

I have a new .NET 9 Razor page Blazor server-side project.

I used EF Core Power Tools to import my database schema and generate the DbContext and model classes.

I created a MetaData class for one of my model classes and I'm using a call to TypeDescriptor.AddProviderTransparent to attach the metadata class to the class that was generated by EF Core Power Tools.

I'm attempting to do the same thing shown here: https://simonholman.dev/add-dataannotations-metadata-to-ef-core-in-a-partial-class

But, the metadata appears to be not getting used at all.

Generated model code:

[Table("AsphaltIndexPrice")]
public partial class AsphaltIndexPrice 
{
    [Key]
    public int AsphaltIndexPriceId { get; set; }

    [Column(TypeName = "datetime")]
    public DateTime EffectiveDate { get; set; }

    [Column(TypeName = "money")]
    public decimal IndexPrice { get; set; }
}

Metadata class:

public class AsphaltIndexPriceMetaData 
{
    [Display(Name = "Asphalt Index Price Id")]
    public int AsphaltIndexPriceId { get; set; }

    [Required(ErrorMessage="An Effective Date is required")]
    [Display(Name = "Effective Date")]
    public DateTime EffectiveDate { get; set; }

    [Required(ErrorMessage = "An Index Price is required")]
    [DataType(DataType.Currency)]
    [Display(Name = "Price")]
    public decimal IndexPrice { get; set; }
}

Code for attaching the metadata class to the model class:

public static void HMADBMetaDataInit() 
{
     AddMetaData(typeof(AsphaltIndexPrice), typeof(AsphaltIndexPriceMetaData));
}

private static void AddMetaData(Type baseClass, Type metaDataClass) 
{
     TypeDescriptor.AddProviderTransparent(
         new AssociatedMetadataTypeTypeDescriptionProvider(
             baseClass,
             metaDataClass),
         baseClass);
 }

The call to setup the metadata, in Program.cs:

HMADBHelper.HMADBMetaDataInit();

app.Run();

I also put the same call to HMADBMetaDataInit() in the OnInitialize of a page, when just having it in Program.cs didn't work. That made no difference.

Ways that tell me the metadata is being ignored:

On the screen where EffectiveDate and IndexPrice are shown, the labels generated are "EffectiveDate" and "IndexPrice", instead of "Effective Date" (with a space) and "Price" (no word "Index").

Also, on the Edit screen, if I blank out the EffectiveDate and the IndexPrice fields and click to save them, I get error messages that the values are not valid or not in the right range, but I do not get the specific text from above, e.g. "An Index Price is required".

I've stepped over the calls to AddProviderTransparent in the debugger and I don't see any errors or other information in the output window to explain why it doesn't seem to be working.

What am I doing wrong?

Update

Per Simon's comment, I changed Program.cs to call my metadata initialization before calling builder.Build(). No change in application behavior.

HMADBHelper.HMADBMetaDataInit();

var app = builder.Build();
Share Improve this question edited Mar 27 at 4:58 marc_s 756k184 gold badges1.4k silver badges1.5k bronze badges asked Mar 27 at 3:13 StuartVStuartV 1352 silver badges10 bronze badges
Add a comment  | 

2 Answers 2

Reset to default 0

Check the create/edit pages/components. Normally, we use a label to display the property name by directly writing it. If you want to display the Display Name based on the property name, you can achieve this through reflection.

Refer to the following sample code:

Classes:

[MetadataType(typeof(MyModelMetadata))]
public class MyModel
{
    public int Id { get; set; }
    public string Name { get; set; }
}

public class MyModelMetadata
{
    public int Id { get; set; }
    [Required(ErrorMessage ="User Name is required, please enter a name")] 
    [Display(Name= "User Name")]
    public string Name { get; set; }
}

Razor component:

@page "/metadata"
@using BlazorServerWithoutIdentity.Data
@using System.ComponentModel
@using System.ComponentModel.DataAnnotations
<EditForm Model="@model" OnValidSubmit="HandleValidSubmit" FormName="metadatatest">
    <DataAnnotationsValidator />
    <ValidationSummary />

    <div>
        <label for="name">@GetDisplayName(nameof(model.Name))</label>
        <InputText id="name" @bind-Value="model.Name" />
    </div>

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

@code {
    [SupplyParameterFromForm]
    private MyModel model { get; set; } = new();
    protected override void OnInitialized()
    {
        TypeDescriptor.AddProviderTransparent(new AssociatedMetadataTypeTypeDescriptionProvider(typeof(MyModel), typeof(MyModelMetadata)), typeof(MyModel));
    }

    private void HandleValidSubmit()
    {
        // Handle the valid form submission here
        Console.WriteLine("Form submitted!");
    }
    private string GetDisplayName(string propertyName)
    {
        var propInfo = typeof(MyModelMetadata).GetProperty(propertyName);
      
        var displayName = propInfo?.GetCustomAttributes(typeof(DisplayAttribute), false)
            .OfType<DisplayAttribute>()
            .FirstOrDefault()?.Name;

        return displayName ?? propertyName;
    }
}

The output as below:

The only difference I can see from your code to what I'm using (which was the basis of my blog post) is that I have my TypeDescriptor calls before my builder.Build() call, not before my app.Run()

I hope that helps.

发布评论

评论列表(0)

  1. 暂无评论