Developer's format helpers
Tags: csharp, dotnetcore, productivity, codenewbie
Published: 2024-07-07 19:26:27

## Introduction

Learn how to provide assistance for allowing other developers to specify formatting of objects from your code no different than Intellisense in Microsoft Visual Studio.

### Applies to

| Product        |   Version    |
|:------------- |:-------------|
| .NET | 7,8,9 |

{% cta https://github.com/karenpayneoregon/csharp-11-ef-core-7-features/tree/master/StringSyntaxWithEntityFrameworkCore %} Sample project {% endcta %}

## Samples overview

Using Entity Framework Core (but not limited to EF Core), read data using the following model.

```csharp
public partial class BirthDay
{
    public int Id { get; set; }
    public string FirstName { get; set; }
    public string LastName { get; set; }
    public DateOnly? BirthDate { get; set; }
    // computed column
    public int? YearsOld { get; set; }
}
```

To produce the following output.


![Shows output](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/iibcnuh2axy2567zs68p.png)

This is done by hardcoding each properties format e.g. D2, MM/dd/yyyy

Using the following code.

```csharp
public static async Task Conventional()
{
    await using var context = new Context();
    var list = await context.BirthDays.ToListAsync();

    foreach (var item in list)
    {
        // line broken for readability
        Debug.WriteLine($"{item.Id,-5:D2}" +
                        $"{item.FirstName,-15}" +
                        $"{item.LastName,-15}" +
                        $"{item.BirthDate!.Value,-12:MM/dd/yyyy}" +
                        $"{item.YearsOld!.Value:D3}");
    }
}
```

## StringSyntaxAttribute Class

The [StringSyntaxAttribute class](https://learn.microsoft.com/en-us/dotnet/api/system.diagnostics.codeanalysis.stringsyntaxattribute?view=net-8.0) provides the ability to allow the caller to a method to specify their own format e.g. rather than D2, D5 etc.

Revised code from the first code sample.

StringSyntax(NumericFormat) means to present number formats.

![Dropdown with number formats](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/l0vyc34w8ztur7d8xpl9.png)


StringSyntax(DateOnlyFormat) means to present DateOnly formats.


![Shows Dropdown for DateOnly formats](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/iukuttg2y9k4sn6rwnbl.png)



```csharp
public static async Task Syntax(
    [StringSyntax(NumericFormat)] string iDFormat,
    [StringSyntax(DateOnlyFormat)] string dateFormat,
    [StringSyntax(NumericFormat)] string ageFormat)
{
    
    await using var context = new Context();
    var list = await context.BirthDays.ToListAsync();

    foreach (var item in list)
    {
        // line broken for readability
        Debug.WriteLine($"{item.Id.ToString(iDFormat),-5}" +
                        $"{item.FirstName,-15}" +
                        $"{item.LastName,-15}" +
                        $"{item.BirthDate!.Value.ToString(dateFormat),-12}" +
                        $"{item.YearsOld!.Value.ToString(ageFormat)}");
    }
}
```

### Other formats supported

- DateTimeFormat
- EnumFormat
- GuidFormat
- Json
- Regex
- TimeOnlyFormat
- TimeSpanFormat
- Uri
- Xml

## Create your own

Another idea is to setup formatting from a source such as appsettings.json

In the following example the formats are in appsettings.json.

```json
{
  "ApplicationSetting": {
    "IdentifierFormat": "D2",
    "DateOnlyFormat": "MM/dd/yyyy",
    "AgeFormat": "D3"
  } 
}
```

Using the following model.

```csharp
public class ApplicationSetting
{
    public string IdentifierFormat { get; set; }
    public string AgeFormat { get; set; }
    public string DateOnlyFormat { get; set; }
}
```

In the provided code sample the following is used to get our formats.

```csharp
public static ServiceCollection ConfigureServices()
{
    static void ConfigureService(IServiceCollection services)
    {

        services.Configure<ConnectionStrings>(Config.Configuration.JsonRoot()
            .GetSection(nameof(ConnectionStrings)));

        services.Configure<EntityConfiguration>(Config.Configuration.JsonRoot()
            .GetSection(nameof(EntityConfiguration)));

        services.Configure<ApplicationSetting>(Config.Configuration.JsonRoot()
            .GetSection(nameof(ApplicationSetting)));

        services.AddTransient<SetupServices>();
    }

    var services = new ServiceCollection();
    ConfigureService(services);

    return services;

}
```

Create out own Attribute to store multiple formats.

```csharp
internal class Samples
{
    /// <summary>
    /// Get formats from appsettings.json, see <see cref="SetupServices"/>
    /// </summary>
    [AttributeUsage(AttributeTargets.Parameter)]
    public class AppSyntaxAttribute : Attribute
    {
        public static string IdentifierFormat = FormatSettings.Instance.Items.IdentifierFormat;
        public static string AgeFormat = FormatSettings.Instance.Items.AgeFormat;
        public static string DateOnlyFormat = FormatSettings.Instance.Items.DateOnlyFormat;
    }

    public async Task SpecialSyntax([AppSyntax()] string numbers = "")
    {
        var idFormat = AppSyntaxAttribute.IdentifierFormat;
        var dateFormat = AppSyntaxAttribute.DateOnlyFormat;
        var yearsOldFormat = AppSyntaxAttribute.AgeFormat;

        await using var context = new Context();
        var list = await context.BirthDays.ToListAsync();

        foreach (var item in list)
        {
            // line broken for readability
            Debug.WriteLine($"{item.Id.ToString(idFormat),-5}" +
                            $"{item.FirstName,-15}" +
                            $"{item.LastName,-15}" +
                            $"{item.BirthDate!.Value.ToString(dateFormat),-12}" +
                            $"{item.YearsOld!.Value.ToString(yearsOldFormat)}");


        }
    }
}
```

## Summary

Code has been presented to provide a single developer or a team of developers to have intellisense for method parameters.