Skip to content

Malfunctioning with SqlKata.KeyAttribute #2337

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
kwon0408 opened this issue Apr 16, 2025 · 1 comment
Open

Malfunctioning with SqlKata.KeyAttribute #2337

kwon0408 opened this issue Apr 16, 2025 · 1 comment
Labels

Comments

@kwon0408
Copy link

kwon0408 commented Apr 16, 2025

Describe the bug
I have used SqlKata's [Key] attribute without the name specified for years without any errors complaining about it, even with SqlKata.Execution.

However CsvHelper seems to be not happy with these nameless keys. When used together, some exceptions are thrown:

Unhandled exception. CsvHelper.WriterException: An unexpected error occurred. See inner exception for details.
IWriter state:
   Row: 1
   Index: 0
   HeaderRecord:
1

 ---> System.ArgumentNullException: Value cannot be null. (Parameter 'name')
   at SqlKata.ColumnAttribute..ctor(String name)
   at SqlKata.KeyAttribute..ctor(String name)
   at System.Reflection.CustomAttribute._CreateCaObject(RuntimeModule pModule, RuntimeType type, IRuntimeMethodInfo pCtor, Byte** ppBlob, Byte* pEndBlob, Int32* pcNamedArgs)
   at System.Reflection.CustomAttribute.AddCustomAttributes(ListBuilder`1& attributes, RuntimeModule decoratedModule, Int32 decoratedMetadataToken, RuntimeType attributeFilterType, Boolean mustBeInheritable, ListBuilder`1 derivedAttributes)
   at System.Reflection.CustomAttribute.GetCustomAttributes(RuntimeModule decoratedModule, Int32 decoratedMetadataToken, Int32 pcaCount, RuntimeType attributeFilterType)
   at System.Reflection.CustomAttribute.GetCustomAttributes(RuntimeFieldInfo field, RuntimeType caType)
   at System.Attribute.GetCustomAttributes(MemberInfo element, Boolean inherit)
   at CsvHelper.Configuration.ClassMap.ApplyAttributes(MemberMap memberMap)
   at CsvHelper.Configuration.ClassMap.AutoMapMembers(ClassMap map, CsvContext context, LinkedList`1 mapParents, Int32 indexStart)
   at CsvHelper.Configuration.ClassMap.AutoMap(CsvContext context)
   at CsvHelper.CsvContext.AutoMap(Type type)
   at CsvHelper.CsvWriter.WriteHeader(Type type)
   at CsvHelper.CsvWriter.WriteHeaderFromType[T]()
   at CsvHelper.CsvWriter.WriteRecords[T](IEnumerable`1 records)
   --- End of inner exception stack trace ---
   at CsvHelper.CsvWriter.WriteRecords[T](IEnumerable`1 records)
   at Program.<<Main>$>g__WriteCsv|0_0[T](IEnumerable`1 values) in C:\Users\user\source\repos\sln1\proj1\Program.cs:line 26
   at Program.<Main>$(String[] args) in C:\Users\user\source\repos\sln1\proj1\Program.cs:line 13

To Reproduce

using CsvHelper;
using CsvHelper.Configuration;
using System.Globalization;

MyRecord[] records =
[
    new() { Id = 1, Value = "A" },
    new() { Id = 2, Value = "B" },
];

var csv = WriteCsv(records);
Console.WriteLine(csv);

static string WriteCsv<T>(IEnumerable<T> values)
{
    using var writer = new StringWriter();
    var config = new CsvConfiguration(CultureInfo.InvariantCulture)
    {
        MemberTypes = MemberTypes.Properties | MemberTypes.Fields
    };
    using var csv = new CsvWriter(writer, config);

    csv.WriteRecords(values);
    return writer.ToString();
}

struct MyRecord
{
    [SqlKata.Key] // Error! 
    // [SqlKata.Key(nameof(Id))] or any other value for `name` is REQUIRED, when using with CsvHelper
    public int Id;

    public string Value;
}

Expected behavior

Id,Value
1,A
2,B

Screenshots
N/A

Additional context
An issue for the same bug is also created in SqlKata's issue tracker.

@kwon0408 kwon0408 added the bug label Apr 16, 2025
@Rob-Hague
Copy link
Contributor

This is a not an issue with CsvHelper - it happens without it:

Console.WriteLine(typeof(MyRecord).GetField(nameof(MyRecord.Id)).GetCustomAttributes().Count());

struct MyRecord
{
    [SqlKata.Key] // Error! 
    //[SqlKata.Key(nameof(Id))]
    public int Id;

    public string Value;
}

The problem is that SqlKata's KeyAttribute defines a default value of "" for the parameter:

https://github.com/sqlkata/querybuilder/blob/d20e930c8a4bd801225f96302917432fa4b3b3ea/QueryBuilder/ColumnAttribute.cs#L27

which then throws when passed to the base ColumnAttribute class due to string.IsNullOrEmpty check:

https://github.com/sqlkata/querybuilder/blob/d20e930c8a4bd801225f96302917432fa4b3b3ea/QueryBuilder/ColumnAttribute.cs#L13

SqlKata expects the parameter to be filled by the compiler (using [System.Runtime.CompilerServices.CallerMemberName]) but when the runtime does its work in GetCustomAttributes() (which apparently involves calling the constructor), it does not know or consider the value substituted by the compiler at the definition site.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

2 participants