FeatBit

Poor Experience with Claude Sonnet 3.7 in .NET Core

Before reading: If you're looking for a good experience with AI, this article is not for you.

Very disappointing—it doesn't save time or effort. I still need to refactor the code for readability and maintainability.

Scenario - Translating from MongoDB to PostgreSQL

In our open-source feature flag project, FeatBit, we originally used MongoDB as our primary database. Based on customer feedback, we are now adding support for PostgreSQL. We used the MongoDB C# Driver SDK for queries with MongoDB, but we are transitioning to Entity Framework to support multiple databases within the same codebase.

This article demonstrates how we translated MongoDB C# Driver code to support PostgreSQL with Entity Framework using VSCode Copilot Edits and the Claude Sonnet 3.7 model, using a specific service called InsightService.

Service Interface and Implementation

We have an interface called IInsightService, which includes two methods: TryParse and AddManyAsync. The TryParse method is used to parse a JSON string into an object, while the AddManyAsync method adds multiple insight events to the database.

public interface IInsightService
{
    bool TryParse(string json, out object insight);
 
    Task AddManyAsync(object[] insights);
}

We had an implementation of InsightService for MongoDB using the MongoDB C# Driver. Here's the original code:

public class InsightService(MongoDbClient mongoDb) : IInsightService
{
    public bool TryParse(string json, out object? insight)
    {
        try
        {
            insight = Parse();
        }
        catch
        {
            insight = null;
        }
 
        return insight != null;
 
        object Parse()
        {
            var jsonNode = JsonNode.Parse(json)!.AsObject();
 
            // Replace uuid with _id
            jsonNode["_id"] = jsonNode["uuid"]!.GetValue<string>();
            jsonNode.Remove("uuid");
 
            // Convert properties JSON string to object
            jsonNode["properties"] = JsonNode.Parse(jsonNode["properties"]!.GetValue<string>());
 
            // Convert timestamp to UTC DateTime
            var timestampInMilliseconds = jsonNode["timestamp"]!.GetValue<long>() / 1000;
            var timestamp = DateTimeOffset.FromUnixTimeMilliseconds(timestampInMilliseconds).UtcDateTime;
            jsonNode["timestamp"] = timestamp;
 
            // Convert JSON object to BSON document
            var bsonDocument = BsonDocument.Parse(jsonNode.ToJsonString());
            // Change timestamp type to DateTime, otherwise it will be String
            bsonDocument["timestamp"] = timestamp;
 
            return bsonDocument;
        }
    }
 
    public async Task AddManyAsync(object[] insights) =>
        await mongoDb.CollectionOf("Events").InsertManyAsync(insights as BsonDocument[]);
}

Now, we need to translate the logic in the TryParse method and the database interaction code in AddManyAsync to support PostgreSQL with Entity Framework.

Translating Logical Code to Support PostgreSQL

The TryParse method in the MongoDB version of the service returned a BsonDocument, which is a MongoDB-specific type. To support PostgreSQL, we need to change it to a generic object type. Here's the prompt I used to translate the code:

I have a InsightService.cs  in /Services/mongodb folder,
another InsightService.cs in /Services/EntityFrameworkCore folder

I need to translate from `TryParse` method in InsightService.cs  in /Services/mongodb folder to `TryParse` method in InsightService.cs  in /Services/EntityFrameworkCore folder

In the TryParse method in InsightService.cs  in /Services/mongodb folder, it useed MongoDB C# Driver to convert a json string to BsonDocument type object. 

But in the InsightService.cs  in /Services/EntityFrameworkCore folder, the return value should be "object" a dynamic or expandoobject, 

The image below shows the translated code in the IDE:

TryParse Implementation - Frist Trial

It correctly updated the TryParse method in the appropriate file. However, the generated code was incorrect. So, I had to modify my prompt to get it to work:

in TryParse method, deserialize json variable from string to object. THe json string format is like below: 
{ 
    "uuid": // STRING, Primary Key 
    "distinct id": // STRING 
    "env_id": // STRING 
    "event": // STRING 
    "properties": // JSON string 
    "timestamp": // TIMESTAMP long 
}

TryParse Implementation - Second Trial

As shown in the image above, it worked, but the result was neither clean nor optimal. For example, on line 13, an unnecessary option variable was declared and assigned but never used. I had to refactor the code to improve its readability and maintainability.

Translating Database Interaction Code to Support PostgreSQL

The AddManyAsync method in the MongoDB version of the service used the InsertManyAsync method from the MongoDB C# Driver to insert multiple documents into the database. We needed to modify it to use Entity Framework to support PostgreSQL.

I am not posting any prompts or images for this section because, despite multiple attempts, I was unable to get correct code from AI. Ultimately, I wrote the implementation myself.

Final code

Here’s my final refactored code. From my perspective, this version is much cleaner and more readable than the AI-generated code. Most importantly, it works correctly and took less time than iterating with AI.

public bool TryParse(string json, out object? insight)
{
    try
    {
        insight = Parse();
    }
    catch
    {
        insight = null;
    }
 
    return insight != null;
 
    object Parse()
    {
        using var jsonDocument = JsonDocument.Parse(json);
        var root = jsonDocument.RootElement;
 
        var timestampMs = root.GetProperty("timestamp").GetInt64() / 1000;
        var timestamp = DateTimeOffset.FromUnixTimeMilliseconds(timestampMs).UtcDateTime;
 
        var item = new
        {
            uuid = root.GetProperty("uuid").GetGuid(),
            distinct_id = root.GetProperty("distinct_id").GetString(),
            env_id = root.GetProperty("env_id").GetString(),
            @event = root.GetProperty("event").GetString(),
            properties = root.GetProperty("properties").ToString(),
            timestamp
        };
 
        return item;
    }
}
 
public async Task AddManyAsync(object[] insights)
{
    var connection = dbContext.Database.GetDbConnection();
 
    const string sql = """
                        insert into events (id, distinct_id, env_id, event, properties, timestamp) 
                        values (@uuid, @distinct_id, @env_id, @event, @properties::jsonb, @timestamp)
                        """;
 
    await connection.ExecuteAsync(sql, insights);
}

Conclusion

AI did not save me time or effort in this case.

  1. It couldn't generate clean and correct code directly—I still had to refactor it for readability and maintainability.
  2. Most of the time, it provided incorrect answers. I tried to refine my prompt to be as precise as possible, but it still didn’t work.

We'll see if future AI models perform better.

Ready to use feature flags to expedite your dev and deployment process?