Sr. Content Developer at Microsoft, working remotely in PA, TechBash conference organizer, former Microsoft MVP, Husband, Dad and Geek.
150404 stories
·
33 followers

Microsoft finally has a better looking Run dialog for Windows 11

1 Share

Microsoft is finally updating the design of the Run dialog prompt in Windows after more than 30 years. After committing to adding a dark mode to Run, Microsoft is now testing a refreshed design that makes it fit with the aesthetics in Windows 11. X user Phantomofearth discovered the modern Run dialog in the latest preview builds of Windows 11.

The overhauled Run prompt looks a lot more like a modern launcher, but it functions exactly as Run always has. The Run command has been part of Windows since Windows 95, allowing power users to launch apps with shortcuts like “mspaint” for Microsoft Paint, “calc” for calculator, and “dxdiag” for the DirectX Diagnostic Tool.

If you’re not a fan of the overhaul you’ll even be able to toggle the modern Run dialog on and off in the advanced system settings of Windows 11. Microsoft hasn’t officially announced this modern Run prompt, but I’d expect we’ll see it show up officially in builds in the coming weeks.

Raycast just launched on Windows recently, providing Microsoft’s operating system with a powerful launcher, clipboard manager, shortcut system, and more. I’m hoping it’s enough to push Microsoft into taking the time to turn Run into a modern launcher like it has been doing with Command Palette and PowerToys Run in recent years.

Read the whole story
alvinashcraft
35 minutes ago
reply
Pennsylvania, USA
Share this story
Delete

Netflix wins the bidding war for Warner Bros.

1 Share

Netflix is poised to purchase Warner Bros.’ studio and streaming business after being selected as the winner of the bidding war for the media giant. The two companies will now move forward into exclusive deal talks, TheWrap reports, likely including a $5 billion breakup fee in case regulators block the buyout.

Netflix reportedly offered $30 a share for the studio and its streaming assets, which include HBO Max and the rights to brands including Harry Potter and DC Comics. Netflix’s bid won out over interest from Comcast and Paramount, fresh from its own merger with Skydance, though early interest had been reported from Amazon and Apple, too.

Warner Bros. Discovery announced that it was open to an acquisition in October, months after announcing plans to split the company in two: the studio and streaming business on one side, and cable on the other. While Paramount, which had three early bids rejected, had reportedly hoped to buy both halves of the company, Netflix is focused on the studio side.

Any deal will have to clear regulatory hurdles, with opposition already reported from the Department of Justice. And if it goes through, Netflix will have to adapt to a new role in Hollywood, running one of the largest and oldest studios, including the theatrical business it’s historically shied away from.

Read the whole story
alvinashcraft
35 minutes ago
reply
Pennsylvania, USA
Share this story
Delete

AGL 446: Tamara Laine

1 Share

About Tamara

Tamara Laine is an Emmy Award-winning journalist, fintech founder, and business growth strategist. As the founder and CEO of MPWR, she is transforming how credit and capital are accessed by gig workers, women, and underserved communities through AI and alternative data. A former gig worker herself, Tamara brings lived experience to her mission of building a more inclusive financial system. She has launched and scaled eight-figure tech ventures, advised banks on equitable capital access, and is a recognized expert in financial inclusion, ethical tech, and economic innovation. Tamara also co-hosts What the Frac?, a podcast on startup growth.


Today We Talked About

  • Ethical AI
  • Innovative Leadership
  • Journalism vs. Entrepreneurship
  • Leadership
  • Start with Why?
  • Emotional Leadership
  • Grounded in a good foundation
  • Product first, then Vision
  • Executive Coaching
  • Empathy as a leader
  • Empathy takes Time
  • Being a Mentor
  • Your network, and how you treat your community
  • What the Fract show

Connect With Tamara


Leave me a tip $
Click here to Donate to the show


I hope you enjoyed this show, please head over to Apple Podcasts and subscribe and leave me a rating and review, even one sentence will help spread the word.  Thanks again!





Download audio: https://media.blubrry.com/a_geek_leader_podcast__/mc.blubrry.com/a_geek_leader_podcast__/AGL_446_Tamara_Laine.mp3?awCollectionId=300549&awEpisodeId=11821867&aw_0_azn.pgenre=Business&aw_0_1st.ri=blubrry&aw_0_azn.pcountry=US&aw_0_azn.planguage=en&cat_exclude=IAB1-8%2CIAB1-9%2CIAB7-41%2CIAB8-5%2CIAB8-18%2CIAB11-4%2CIAB25%2CIAB26&aw_0_cnt.rss=https%3A%2F%2Fwww.ageekleader.com%2Ffeed%2Fpodcast
Read the whole story
alvinashcraft
36 minutes ago
reply
Pennsylvania, USA
Share this story
Delete

Microsoft 365 Copilot Business: The future of work for small businesses

1 Share

We’re excited to announce the general availability of Microsoft 365 Copilot Business—a comprehensive, full-featured AI solution built for SMBs.

The post Microsoft 365 Copilot Business: The future of work for small businesses appeared first on Microsoft 365 Blog.

Read the whole story
alvinashcraft
36 minutes ago
reply
Pennsylvania, USA
Share this story
Delete

Advancing Microsoft 365: New capabilities and pricing update

1 Share

Today we are announcing expanded availability of AI, security, and management capabilities coming to Microsoft 365 offerings in 2026.

The post Advancing Microsoft 365: New capabilities and pricing update appeared first on Microsoft 365 Blog.

Read the whole story
alvinashcraft
36 minutes ago
reply
Pennsylvania, USA
Share this story
Delete

.NET 10 Validation

1 Share

Note: this is an update to my original post.

Introduction

I wrote about entity validation in the past, the reason I'm coming back to it is that there are some changes in .NET 10, related to validation. Let's cover the original validation API and then explain what has changed.

.NET Validation

Validation is implemented in the System.ComponentModel.DataAnnotations namespace. I'll describe it first and then move to the small changes in .NET 10.

.NET has featured a way to validate classes and their properties for a long time, the API is called Data Annotations Validation, and it integrates nicely with ASP.NET Core MVC and other frameworks (not Entity Framework Core, as I've talked recently, nor ASP.NET Core Minimal APIs). It is essentially, but not just, based on a set of attributes, one interface, and a class that performs validations on a target class and its properties, from the validation attributes it contain.

The base class for attribute validation is called, surprise, surprise, ValidationAttribute, and there are already a few attributes that implement some common validations. I will talk about them now.

The abstract ValidationAttribute has two IsValid methods: one that takes a ValidationContext object as its parameter and returns a ValidationResult, and a simpler version that has no parameters and just returns a boolean. Why the two, I hear you ask? Well, it depends on which version of the Validate method is called, the one that takes the ValidationContext parameter will call the appropriate IsValid overload. More on this later on.

The way to perform validation is through the Validator class, through static methods, and it returns a collection of ValidationResult objects.

Validation Through Attributes

Required

Maybe one of the most used validation attribute is [Required]. It is used to tell Data Annotations that a specific field or property is, well, required, which means that it must have a non-default value. This means, for reference types, that it cannot be null, for strings, that it cannot be also empty or with blanks. Value types are not affected by it.

Example usage:

[Required(AllowEmptyStrings = false)]
public string? Name { get; set; }

The AllowEmptyStrings property does exactly what it says: if set, it does not consider a failure if the string is empty, only if it is null.

Maximum and Minimum String Length

There are a couple of ways by which we can define the minimum and maximum limits for a string property: one is the [MinLength] and [MaxLength] attributes. As you can imagine, they allow you to set, respectively, the minimum and maximum allowed length for a string. Note that they don't do anything else, meaning, if the string is null, they are just ignored. You can apply any or both at the same time:

[MinLength(3)]
[MaxLength(50)]
public string? Name { get; set; }

Maximum and Minimum Collection or String Size

There is another attribute that can also be used for defining the minimum and maximum limits for a string, but, also for a collection of any kind: the [Length] attribute. Here's an example for a collection:

[Length(5, 10)]
public List<string> Items { get; set; } = [];

Numeric Range

It is also possible to specify the lower and/or higher limits for a numeric value, through the [Range] attribute:

[Range(0, 10, MinimumIsExclusive = true, MaximumIsExclusive = false)]
public int Position { get; set; }

We can control wether or not the lower and higher limits are exclusive or not through the MinimumIsExclusive and MaximumIsExclusive optional properties. This attribute can be applied to any numeric field or property.

Allowed and Disallowed Values

If we want our field or property to only accept/do not accept a set of predefined values, we can use the [AllowedValues] and [DeniedValues]:

[AllowedValues("Red", "Green", "Blue")]
[DeniedValues("Black", "White", "Grey")]
public string? Colour { get; set; }

The values are checked as-is, meaning, for strings, there is no way to compare case-insensitive. These attributes were introduced in .NET 8.

Comparing Members

What if you want to compare a value of a member (property, field) with that of another member, as for password confirmations? Enter the [Compare] attribute:

[Required]
public string Password { get; set; }

[Compare(nameof(Password))]
public string Confirmation { get; set; }

Data Type

If we want to say that the type of a property must be one of a list of defined types, we can use [DataType]. In this case, we need to pass either a DataType or a name that defines a custom type:

[DataType(DataType.Currency)]
public string Price { get; set; }

The allowed types for checking (DataType) are:

  • Custom
  • DateTime
  • Date
  • Time
  • Duration
  • PhoneNumber
  • Currency
  • Text
  • Html
  • MultilineText
  • EmailAddress
  • Password
  • Url
  • ImageUrl
  • CreditCard
  • PostalCode
  • Upload

For the Custom data type, the CustomDataType property must be supplied.

Please note that this attribute does not actually perform any validation, but it is used to define the type that the target member should comply to.

Enumeration Values

Sometimes we want to set to a string property a value that must match an enumeration value. For that we have [EnumDataType], and we need to pass it the desired enumeration type:

[EnumDataType(typeof(DayOfWeek)]
public string DayOfWeek { get; set; }

Regular Expressions

Regular expressions in .NET are quite powerful and there is a way to validate a string against a regular expression by using the [RegularExpression] attribute:

[RegularExpression(@"\w{5,10}")]
public string Password { get; set; }

URL

You can use the regular expression validator for validating a property for a valid URL, but an alternative is to use the [Url] attribute:

[Url]
public string ClientUrl { get; set; }

Note: this validator attribute will only check if the string starts by one of the following protocols, all case-insensitive:

  • http://
  • https://
  • ftp://

Email Address

Similar to URL, you can use a regular expression to validate an email address, but there is the [EmailAddress] attribute exactly for that purpose:

[EmailAddress]
public string ClientEmail { get; set; }

In case you are wondering, here is the regular expression that is used:

^((([a-z]|\d|[!#\$%&'\*\+\-\/=\?\^_`{\|}~]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])+(\.([a-z]|\d|[!#\$%&'\*\+\-\/=\?\^_`{\|}~]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])+)*)|((\x22)((((\x20|\x09)*(\x0d\x0a))?(\x20|\x09)+)?(([\x01-\x08\x0b\x0c\x0e-\x1f\x7f]|\x21|[\x23-\x5b]|[\x5d-\x7e]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(\\([\x01-\x09\x0b\x0c\x0d-\x7f]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF]))))*(((\x20|\x09)*(\x0d\x0a))?(\x20|\x09)+)?(\x22)))@((([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))\.)+(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))\.?$

Credit Card

For credit card number validation we have [CreditCard]:

[CreditCard]
public string CreditCard { get; set; }

This will check if the card is valid according to the standard Luhn algorithm.

Phone Number

In order to validate a phone number, we can use the [Phone] attribute:

[Phone]
public string ClientMobile { get; set; }

This supports:

  • An optional prefix starting with +
  • A set of numbers separated by -, .
  • An optional numeric extension separated by * following x/ext.

This is the actual regular expression that is used:

^(\+\s?)?((?<!\+.*)\(\+?\d+([\s\-\.]?\d+)?\)|\d+)([\s\-\.]?(\(\d+([\s\-\.]?\d+)?\)|\d+))*(\s?(x|ext\.?)\s?\d+)?$

File Extensions

To check if a string ends in one of a possible file extensions, we have the [FileExtensions] attribute:

[FileExtensions(Extensions = "gif,png,jpg,jpeg,tiff,bmp")]
public string ImageUrl { get; set; }

The Extensions property can take a comma-separated list of file extensions. If no extensions are supplied, the default value is "png,jpg,jpeg,gif".

Base64 String

To check if a string is a valid Base64-encoded string, we have the [Base64String] attribute, introduced in .NET 8:

[Base64String]
public string? ImageData { get; set; }

Custom Validation Attributes

Yet another option is to roll out your own validation attribute. It must inherit from ValidationAttribute and implement the IsValid method, like this one is doing:

[Serializable]
[AttributeUsage(AttributeTargets.Property, AllowMultiple = false, Inherited = true)]
public class IsEvenAttribute : ValidationAttribute
{
    protected override ValidationResult? IsValid(object? value, ValidationContext validationContext)
    {
        if (IsValid(value))
        {
            return ValidationResult.Success;
        }

        return new ValidationResult($"Value {value} is not even", new string[] { validationContext.MemberName! });
    }

    protected override bool IsValid(object? value)
    {
        if (IsNumber(value?.GetType()!))
        {
            var number = (long) Convert.ChangeType(value, TypeCode.Int64)!;
 
            if ((number % 2) == 0)
            {
                return true;
            }
        }

        return false;
    }

    private static bool IsNumber(Type? type)
    {
        if (type == null)
        {
            return false;
        }

        switch (Type.GetTypeCode(type))
        {
            case TypeCode.Byte:
            case TypeCode.Decimal:
            case TypeCode.Double:
            case TypeCode.Int16:
            case TypeCode.Int32:
            case TypeCode.Int64:
            case TypeCode.SByte:
            case TypeCode.Single:
            case TypeCode.UInt16:
            case TypeCode.UInt32:
            case TypeCode.UInt64:
                return true;
            case TypeCode.Object:
                if (type.IsGenericType && type.GetGenericTypeDefinition() == typeof(Nullable<>))
                {
                    return IsNumber(Nullable.GetUnderlyingType(type)!);
                }
            return false;
        }

        return false;
    }
}

In this example, we're making it applicable just for properties (AttributeTargets.Property), but we could also make one that is applicable to classes (AttributeTargets.Class) or structs (AttributeTargets.Struct). It checks if the target value is of a numeric type, and if so, converts it into a long and checks if it is even. Note that you should override both IsValid methods. Here's how to apply it:

[IsEven]
public long NumberOfWheels { get; set; }

You can also set if your custom validation attribute requires being called with a ValidationContext, this is achieved by returning true or false on the RequiresValidationContext virtual property, which by default returns false.

Adding Validation Attributes in an External Class

Some of you may know about the [MetadataType] attribute. This attribute can be applied to classes when we don't want to place metadata/validator attributes directly in it, or to partial classes, where at least one part is automatically generated and we can't change it. For example:

[MetadataType(typeof(Data.DataMetadata))]
public class Data
{
    public string Name { get; set; }

    public class DataMetadata
    {
        [Required]
        public string Name { get; set; }
    }
}

What this means is, information for Data class will come, exclusively or not (you can mix), from the attributes in DataMetadata, for properties with identical names and types.

Now, the problem is: if we try to validate, it won't work:

var foo = new Data();
var results = new List<ValidationResult>();
var isValid = Validator.TryValidateObject(foo, new ValidationContext(foo), results);  //true, even if Data.Name wasn't supplied

Alas, there is a way to make it work: we just need to add a metadata provider to the class through TypeDescriptor.AddProviderTransparent and AssociatedMetadataTypeTypeDescriptionProvider:

TypeDescriptor.AddProviderTransparent(new AssociatedMetadataTypeTypeDescriptionProvider(typeof(Data), typeof(Data.DataMetadada)), typeof(Data));

And now it'll work:

var isValid = Validator.TryValidateObject(foo, new ValidationContext(foo), results);  //false, and result is populated with an invalid ValidationResult

It is a known issue, but there are no plans to address it, as far as I know.

If we want to make it dynamic, by looking at some random type and checking if it has the [MetadataType] attribute:

var entityType = typeof(Data);
var attr = Attribute.GetCustomAttribute(entityType, typeof(MetadataTypeAttribute)) as MetadataTypeAttribute;
            
if (attr != null)
{
    TypeDescriptor.AddProviderTransparent(new AssociatedMetadataTypeTypeDescriptionProvider(entityType, attr.MetadataClassType), entityType);
}

I know it's not practical, but as of now, seems to be the only option, if we want to use [MetadataType] for validation attributes defined externally.

Other Validation Types

Custom Validation Using a Method

And now for something completely different: what if you have a method that already performs the validation that you're interested in? You can use it if you apply the [CustomValidation] attribute! Here's an example:

[CustomValidation(typeof(CustomValidator), nameof(NumbersValidator.IsEven))]
public long NumberOfWheels { get; set; }

The class that holds the method that will do the validation can either be:

  • A static class
  • A public non-abstract class with a public parameterless constructor

As for the validation method, it needs to have one of two possible signatures:

  • ValidationResult Validate(object entity, ValidationContext context)
  • bool Validate(object entity)

As you can see, these match the Validate and IsValid methods we talked about early on. The method can be static or instance, as long as it's accessible and non-abstract. One example, for the same validation shown previously (is even):

public static class NumbersValidator
{
    public static ValidationResult IsEven(object entity, ValidationContext context)
    {
        if (IsNumber(entity?.GetType()!))
        {
            var number = (long) Convert.ChangeType(entity, TypeCode.Int64)!;
 
            if ((number % 2) == 0)
            {
                return ValidationResult.Success;
            }
        }

        return new ValidationResult($"Value {entity} is not even", new string[] { validationContext.MemberName! });
    }

    private static bool IsNumber(Type? type)
    {
        if (type == null)
        {
            return false;
        }

        switch (Type.GetTypeCode(type))
        {
            case TypeCode.Byte:
            case TypeCode.Decimal:
            case TypeCode.Double:
            case TypeCode.Int16:
            case TypeCode.Int32:
            case TypeCode.Int64:
            case TypeCode.SByte:
            case TypeCode.Single:
            case TypeCode.UInt16:
            case TypeCode.UInt32:
            case TypeCode.UInt64:
                return true;
           case TypeCode.Object:
               if (type.IsGenericType && type.GetGenericTypeDefinition() == typeof(Nullable<>))
               {
                   return IsNumber(Nullable.GetUnderlyingType(type)!);
               }
               return false;
           }

       return false;
    }
}

Returning a ValidationResult object allows for more information than just a boolean, you can also return the member names that were involved in the validation failure, as well as an error message.

Class Self-Validation

And what if you need to perform validations that envolve multiple properties? Well, one possible option is to apply a validation attribute to the class that contains the properties and then check the value that is being validated for the appropriate class and extract the properties to check. Other option is to have the class implement IValidatableObject! This interface is also part of the Data Annotations API and it is used for classes that self-validated. Here is an example:

public class Contract : IValidatableObject
{
    public DateTime? StartDate { get; set; }
    public DateTime? EndDate { get; set; }
    public string? Name { get; set; }

    public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
    {
        if (string.IsNullOrWhiteSpace(Name))
        {
            yield return new ValidationResult("Missing Name", new string[] { nameof(Name) });
        }

        if (StartDate == null)
        {
            yield return new ValidationResult("Missing Start Date", new string[] { nameof(StartDate) });
        }

        if ((EndDate != null) && (StartDate != null) && (EndDate < StartDate))
        {
            yield return new ValidationResult("End Date before Start Date", new string[] { nameof(EndDate) });
        }
    }
}

This is a simple example that shows three validations:

  • Name is not null or empty
  • StartDate is not null
  • If StartDate and EndDate are both supplied, EndDate must come after StartDate
You can return as many ValidationResult as you want, they will all count as a validation failure.

Performing Validation

Now, for actually performing the validation, we have a few options:

  • Validating the whole instance and all of its properties (this includes class self-validation)
  • Validating a single property

And then some more:

  • Use the existing class and property validation attributes
  • Supply our own validation attributes

We can also choose how we want the results:

The helper class that performs all the validation operations is, unsurprisingly, Validator. Let's see how we can validate the whole entity and return all the validation errors:

var results = new List<ValidationResult>();
var isValid = Validator.TryValidateObject(entity, new ValidationContext(entity), results, validateAllProperties: true);

The TryValidateObject method will never throw an exception, instead, it will populate the results collection with all the ValidationResult objects returned from all the validation attributes that were found for the class and its properties, if the validateAllProperties was set to true, otherwise it will just return the first failed result. TryValidateObject returns a boolean value that says if the object was found to be valid (true) or not (false), but alternatively you can also look at the results collection: if it's empty, then the object is valid.

If instead we want to validate a specific property we use TryValidateProperty:

var isValidProperty = Validator.TryValidateProperty(entity.NumberOfWheels, new ValidationContext(entity) { MemberName = "NumberOfWheels" }, results);

Here, on the ValidationContext, we need to pass the property name that we are validating as the MemberName.

If we prefer to have a ValidationException thrown at the first validation error, just use ValidateObject, for the whole object:

Validator.ValidateObject(entity, new ValidationContext(entity), validateAllProperties: true);

Or ValidateProperty, for a single property:

Validator.ValidateProperty(entity.NumberOfWheels, new ValidationContext(entity) { MemberName = "NumberOfWheels" });

The other thing I mentioned was, if you want to specify your own validation attributes, you can do so using the TryValidateValue/ValidateValue methods and just pass any collection of validation attributes:

var isValid = Validator.TryValidateValue(entity.NumberOfWheels, new ValidationContext(entity) { MemberName = "NumberOfWheels" }, results, new [] { new IsEvenAttribute() });
Validator.ValidateValue(entity.NumberOfWheels, new ValidationContext(entity) { MemberName = "NumberOfWheels" }, new [] { new IsEvenAttribute() });

One important aspect is, the validation methods do not perform recursive validation, until .NET 10. This is explicitly stated in the documentation.

Validation also occurs automatically or manually on an ASP.NET Core app, for models submitted (POST, PUT, PATCH) from a payload.

Validation Results

A validation result is just an instance of ValidationResult which contains the error message (ErrorMessage), and the member names that were validated (MemberNames) and were found to be invalid. There is a static property called Success, which has value null, and just represents the no-error validation result.

Validation Contexts

The validation context is an instance of ValidationContext that needs to be passed to the TryValidateProperty/TryValidateObject/ValidateObject/ValidateProperty methods and which contains:

  • The target object/value to validate (ObjectInstance)
  • The member to validate, which can be null, for the whole object (MemberName)
  • An optional collection of key-values to provide context for the validation (Items)

Inside custom validation methods we can make use of all these properties as we wish.

Error Messages in Validation Attributes

A final word: all of the default attributes provide their own default error messages, but you can override them in one of two ways:

  • By providing a static error message
  • Or by providing the name of a type and property that provide the error message at runtime

For the first option, the ErrorMessage property is what we use, here's a quick example:

[Required(ErrorMessage = "The {0} field is mandatory!")]
public string? Name { get; set; }

As you can see, we can put {0} placeholders on the string, and they will be replaced by the actual property name. Actually, each validation attribute can support other placeholders, which are always optional, for example:

[StringLength(50, MinimumLength: 3, ErrorMessage = "The size of the {0} field must be between {2} and {1}")]
public string? Name { get; set; }

For [StringLength], the MinimumLength will go in placeholder {2} and MaximumLength in {1}, besides, of course, the member name in {0}.

The other option is to use ErrorMessageResourceType and ErrorMessageResourceName properties from ValidationAttribute. These are used typically with resource files, and so both must be set simultaneously. One example:

[Required(ErrorMessageResourceType = typeof(Resource), ErrorMessageResourceName = "RequiredName")]
public string? Name { get; set; }

Yet another option is to override the FormatErrorMessage method. It takes as its name parameter the name of the property where the validation attribute is being called (from the ValidationContext's MemberName), which will be empty if it's a whole class, and we just need to return an appropriate error message:

public override string ReturnErrorMessage(string name) => $"Missing {name}!";

.NET 10 Validation Changes

There is nothing substantially new in regards to validation, but there is a new validation API in .NET 10 that brings validation to ASP.NET Core Minimal APIs and Blazor. It is available on the Microsoft.Extensions.Validation namespace, which lives on the Microsoft.Extensions.Validation NuGet package.

Validation has now been enhanced with support nested objects and element collections and is opt-in. There are two validation-related attributes which we will talk about now. 

This validation API needs to be registered explicitly to the Dependency Injection by calling AddValidation:

builder.Services.AddValidation();

Validation Attributes

Validatable Types

The [ValidatableType] attribute is used to mark a type or nested property for automatic validation on an Minimal API endpoint or Blazor. [SkipValidation] can then be used to exclude parts of it:

[ValidatableType]
public class MyModel { ... }

Skip Validation

The [SkipValidation] attribute is used to tell Minimal API or Blazor not to validate a specific type or member:

[ValidatableType]
public class MyModel
{
    [SkipValidation]
    public SomeType SomeProperty { get; set; }
}

Performing Validation

When some type for which validation is enabled reaches an ASP.NET Core Minimal API endpoint, validation is triggered:

app.MapPost((HttpContext ctx, MyModel obj) =>
{
    //...
};

Same goes when a form in Blazor is validated, all of its bound objects are validated, if so enabled.

Conclusion

As you can see, the Data Validations API is quite powerful, especially if you add class self-validation, use custom validation methods and/or implement your own custom validation attributes. .NET 10 filled in important gap, when it comes to Minimal APIs and Blazor.

As always, I'd like to hear your thoughts on this, any comments, questions, corrections, are always welcome! Happy validating!

Read the whole story
alvinashcraft
36 minutes ago
reply
Pennsylvania, USA
Share this story
Delete
Next Page of Stories