RSCG – Unflat

RSCG – Unflat
 
 

name Unflat
nuget https://www.nuget.org/packages/Unflat/
link https://github.com/pstlnce/unflat
author pstlnce

DataReader to Object Model

 

This is how you can use Unflat .

The code that you start with is


<Project Sdk="Microsoft.NET.Sdk">

  <ItemGroup>
    <PackageReference Include="Unflat" Version="0.0.1" OutputItemType="Analyzer" ReferenceOutputAssembly="false"  />
  </ItemGroup>


  <PropertyGroup>
    <OutputType>Exe</OutputType>
    <TargetFramework>net9.0</TargetFramework>
    <ImplicitUsings>enable</ImplicitUsings>
    <Nullable>enable</Nullable>
  </PropertyGroup>
	<PropertyGroup>
		<EmitCompilerGeneratedFiles>true</EmitCompilerGeneratedFiles>
		<CompilerGeneratedFilesOutputPath>$(BaseIntermediateOutputPath)\GX</CompilerGeneratedFilesOutputPath>
	</PropertyGroup>

</Project>


The code that you will use is


using System;
using System.Data;
using Unflat;

namespace UnflatDemo
{

    class Program
    {
        static void Main(string[] args)
        {
            // Create a DataTable and fill with sample data
            var table = new DataTable();
            table.Columns.Add("Id", typeof(int));
            table.Columns.Add("Name", typeof(string));
            table.Columns.Add("Age", typeof(int));

            table.Rows.Add(1, "Andrei", 30);
            table.Rows.Add(2, "Ignat", 55);

            using var reader = table.CreateDataReader();

            var persons = PersonParser.ReadList(reader);
            foreach (var person in persons)
            {
                Console.WriteLine($"Id: {person.Id}, Name: {person.Name}, Age: {person.Age}");
            }
        }
    }
}



namespace UnflatDemo
{
    [Unflat.UnflatMarker]
    public partial class Person
    {
        public int Id { get; set; }
        public string Name { get; set; } = string.Empty;
        public int Age { get; set; }
    }
}


 

The code that is generated is

using System;

namespace Unflat
{
    [AttributeUsage(AttributeTargets.Class)]
    internal sealed class UnflatMarkerAttribute : Attribute
    {
        public string? ClassName { get; set; }
        public MatchCase Case { get; set; } = MatchCase.All;
    }

    [AttributeUsage(AttributeTargets.Field | AttributeTargets.Property, AllowMultiple = false)]
    internal sealed class SettableParserAttribute : Attribute
    {
        /// <summary>
        /// <list type="bullet">
        /// <item> {0} - reader[i] </item>
        /// <item> {1} - i </item>
        /// <item> i - related column index </item>
        /// <item> reader - reader... </item>
        /// </list>
        /// </summary>
        /// <param name="callFormat"> an argument for string.Format(callFormat, "reader[i]", i) </param>
        public SettableParserAttribute(string callFormat) {}
    }

    [AttributeUsage(AttributeTargets.Field | AttributeTargets.Property, AllowMultiple = false)]
    internal sealed class UnflatPrefixAttribute : Attribute
    {
        public UnflatPrefixAttribute(string prefix) { }
    }

    [AttributeUsage(AttributeTargets.Field | AttributeTargets.Property)]
    internal sealed class UnflatSourceAttribute : Attribute
    {
        public UnflatSourceAttribute(params string[] fields) {}
        public UnflatSourceAttribute(int fieldOrder) {}
    }

    [Flags]
    public enum MatchCase : int
    {
        None                 = 0,
        IgnoreCase           = 1,
        MatchOriginal        = 1 << 1,
        SnakeCase            = 1 << 2,
        CamalCase            = 1 << 3,
        PascalCase           = 1 << 4,
        ApplyOnOverritenName = 1 << 5,
        All = IgnoreCase | MatchOriginal | SnakeCase | CamalCase | PascalCase | ApplyOnOverritenName
    }

    &#91;AttributeUsage(AttributeTargets.Method, AllowMultiple = false)&#93;
    internal sealed class UnflatParserAttribute : Attribute
    {
        /// <summary>
        /// <list type="bullet">
        /// <item> {0} - reader[i] </item>
        /// <item> {1} - i </item>
        /// <item> i - related column index </item>
        /// <item> reader - reader... </item>
        /// </list>
        /// </summary>
        /// <param name="callFormat"> an argument for string.Format(callFormat, "reader[i]", i) </param>
        public string CallFormat { get; set; }

        /// <summary>
        /// If true than this would be a fallback way
        /// for parsing reader's column value to returning type
        /// if not found parsers in closest namespaces
        /// </summary>
        public bool IsDefault { get; set; }

        /// <summary>
        /// If set this value will replace the namespace that contains this parser.
        /// Parser searcher will look to closest parser available to the model.
        /// Parser in Test namespace, the target in Test.Test1 => matched.
        /// Parser in Test.Test1.Test2, the target in Test.Test1 => not matched.
        /// Parser in Test.Test1, the target in Test.Test2 => not matched, etc...
        /// </summary>
        public string NamespaceScope { get; set; }
    }

    [Serializable]
    internal sealed class NotEnoughFieldsForRequiredException : Exception
    {
        public NotEnoughFieldsForRequiredException(int expected, int actual)
            : base($"Required field/properties count: {expected}, actual reader's fields count: {actual}")
        {
            Expected = expected;
            Actual = actual;
        }
        
        public int Expected { get; init; }
        public int Actual { get; init; }
    }

    [Serializable]
    public class MissingRequiredFieldOrPropertyException : System.Exception
    {
        public MissingRequiredFieldOrPropertyException(string[] propertiesOrFields)
            : base("There is no matched data for required properties or fields")
        {
            PropertiesOrFields = propertiesOrFields;
        }

        public string[] PropertiesOrFields { get; init; }
    }
}

using System;
using System.Data;
using System.Data.Common;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Runtime.CompilerServices;
using System.Threading;
using System.Threading.Tasks;

namespace UnflatDemo
{
internal sealed partial class PersonParser
{
internal static List ReadList(TReader reader)
where TReader : IDataReader
{
var result = new List();

if(!reader.Read())
{
return result;
}

ReadSchemaIndexes(reader, out int col_Age, out int col_Name, out int col_Id);

do
{
UnflatDemo.Person parsed = new UnflatDemo.Person();

if(col_Age != -1)
{
parsed.Age = reader[col_Age] is int pcol_Age ? pcol_Age : default;
}

if(col_Name != -1)
{
parsed.Name = reader[col_Name] is string pcol_Name ? pcol_Name : default;
}

if(col_Id != -1)
{
parsed.Id = reader[col_Id] is int pcol_Id ? pcol_Id : default;
}

result.Add(parsed);
} while(reader.Read());

return result;
}

internal static IEnumerable ReadUnbuffered(TReader reader)
where TReader : IDataReader
{
if(!reader.Read())
{
yield break;
}

ReadSchemaIndexes(reader, out int col_Age, out int col_Name, out int col_Id);

do
{
UnflatDemo.Person parsed = new UnflatDemo.Person();

if(col_Age != -1)
{
parsed.Age = reader[col_Age] is int pcol_Age ? pcol_Age : default;
}

if(col_Name != -1)
{
parsed.Name = reader[col_Name] is string pcol_Name ? pcol_Name : default;
}

if(col_Id != -1)
{
parsed.Id = reader[col_Id] is int pcol_Id ? pcol_Id : default;
}

yield return parsed;
} while(reader.Read());
}

internal static async Task> ReadListAsync(TReader reader, CancellationToken token = default)
where TReader : DbDataReader
{
var result = new List();

if(!(await reader.ReadAsync(token).ConfigureAwait(false)))
{
return result;
}

ReadSchemaIndexes(reader, out int col_Age, out int col_Name, out int col_Id);

Task reading;

while(true)
{
UnflatDemo.Person parsed = new UnflatDemo.Person();

if(col_Age != -1)
{
parsed.Age = reader[col_Age] is int pcol_Age ? pcol_Age : default;
}

if(col_Name != -1)
{
parsed.Name = reader[col_Name] is string pcol_Name ? pcol_Name : default;
}

if(col_Id != -1)
{
parsed.Id = reader[col_Id] is int pcol_Id ? pcol_Id : default;
}

reading = reader.ReadAsync(token);

result.Add(parsed);

if(!(await reading.ConfigureAwait(false)))
{
break;
}
}

return result;
}

internal static async ValueTask> ReadListAsyncValue(TReader reader, CancellationToken token = default)
where TReader : DbDataReader
{
var result = new List();

if(!(await reader.ReadAsync(token).ConfigureAwait(false)))
{
return result;
}

ReadSchemaIndexes(reader, out int col_Age, out int col_Name, out int col_Id);

Task reading;

while(true)
{
UnflatDemo.Person parsed = new UnflatDemo.Person();

if(col_Age != -1)
{
parsed.Age = reader[col_Age] is int pcol_Age ? pcol_Age : default;
}

if(col_Name != -1)
{
parsed.Name = reader[col_Name] is string pcol_Name ? pcol_Name : default;
}

if(col_Id != -1)
{
parsed.Id = reader[col_Id] is int pcol_Id ? pcol_Id : default;
}

reading = reader.ReadAsync(token);

result.Add(parsed);

if(!(await reading.ConfigureAwait(false)))
{
break;
}
}

return result;
}

internal static async IAsyncEnumerable ReadUnbufferedAsync(TReader reader, [EnumeratorCancellationAttribute] CancellationToken token = default)
where TReader : DbDataReader
{
if(!(await reader.ReadAsync(token).ConfigureAwait(false)))
{
yield break;
}

ReadSchemaIndexes(reader, out int col_Age, out int col_Name, out int col_Id);

Task reading;

while(true)
{
UnflatDemo.Person parsed = new UnflatDemo.Person();

if(col_Age != -1)
{
parsed.Age = reader[col_Age] is int pcol_Age ? pcol_Age : default;
}

if(col_Name != -1)
{
parsed.Name = reader[col_Name] is string pcol_Name ? pcol_Name : default;
}

if(col_Id != -1)
{
parsed.Id = reader[col_Id] is int pcol_Id ? pcol_Id : default;
}

reading = reader.ReadAsync(token);

yield return parsed;

if(!(await reading.ConfigureAwait(false)))
{
break;
}
}
}

public static void ReadSchemaIndexes(TReader reader, out int col_Age, out int col_Name, out int col_Id)
where TReader : IDataReader
{
col_Age = -1;
col_Name = -1;
col_Id = -1;

var fieldCount = reader.FieldCount;

for(int i = 0; i < fieldCount; i++) { ReadSchemaIndex(reader.GetName(i), i, ref col_Age, ref col_Name, ref col_Id); } } public static void ReadSchemaIndex(string name, int i, ref int col_Age, ref int col_Name, ref int col_Id) { switch(name.Length) { case 2: if(col_Id == -1 && string.Equals("Id", name, StringComparison.OrdinalIgnoreCase)) { col_Id = i; } break; case 3: if(col_Age == -1 && string.Equals("Age", name, StringComparison.OrdinalIgnoreCase)) { col_Age = i; } break; case 4: if(col_Name == -1 && string.Equals("Name", name, StringComparison.OrdinalIgnoreCase)) { col_Name = i; } break; default: break; } } } } [/code] Code and pdf at
https://ignatandrei.github.io/RSCG_Examples/v2/docs/Unflat


Posted

in

,

by

Tags: