Curls, clouds and code

Entity Framework Core: insert or update, the lazy way

It seems as if I’m getting more lazy by the day. That’s great, because I didn’t really feel like manually mapping my data to my data models in order to have Entity Framework update them.

The thing is there are several options for you if you wish to run such operation:

As I said before. I’m lazy. Real lazy. Let me explain my situation.

I’ve been fiddling around quite a lot with GraphQL lately. During this process I figured out that the create and update logic I write on the client-side is quite simillar. This led me to think that, unless a special operation needed to be executed, most create and update logic could be combined in save, or in my case mutate operation. Depending on whether the primary key would be provided either an update or create operation should be executed.

So all in all I did not feel like writing stuff like this:

dbModel.FirstName = viewModel.FirstName;
dbModel.LastName = viewModel.LastName;
// etc...

Heck, I even ditched use of the viewmodels, mostly. I wanted to have an operation with which I could just pass on my object, and it would automagically determine which fields needed to be sent to the database.

Something like this is what I came up with. I discourage using this directly in production code. Use it by means of inspiration.

public static async Task Mutate<T>(
    this DbSet<T> dbSet,
    T subject,
    Guid? cursor = default) where T : class, IId
{
    var context = dbSet.GetService<ICurrentDbContext>().Context;

    bool entryExists = false;

    if (cursor != default) entryExists = await dbSet.AsNoTracking().AnyAsync(q => q.Id == cursor);

    if (entryExists == false) {
        subject.Id = cursor ?? Guid.NewGuid();
        dbSet.Add(subject);
    }
    else if (entryExists == true)
    {
        subject.Id = cursor.Value;

        var entry = dbSet.Attach(subject);

        entry.State = EntityState.Modified;

        typeof(T)
            .GetProperties()
            .Where(q =>
                q.CanWrite
                && Convert.GetTypeCode(q.GetValue(subject)) != TypeCode.Object
                && q.GetValue(subject) != q.GetType().GetDefault()
                && q.GetValue(subject) != null)
            .Select(q => q.Name)
            .ToList()
            .ForEach(property => entry.Property(property).IsModified = true);
                
        entry.Property(q => q.Id).IsModified = false;
    }
}

public static object GetDefault(this Type type)
{
    return type.IsValueType
        ? Activator.CreateInstance(type)
        : null;
}

public interface IId {
  Guid Id { get; set; }
}

It’s not as magical as it might look like. Although there are a few things to note:

I know there are certain people which are going to scream out loud about performance. Let me state this: performance was not a priority, nor did I benchmark it. If this ever becomes an issue I will improve it. If it doesn’t, well, then that’s great.

Happy hacking! And remember, life’s too short to keep writing CRUD code.

How to automatically load graph types in the DI container.

GraphQL.NET relies a lot on DI containers to get instances for certain graph types. At first it might be a bit daunting to come across an error along the likes of this:

GraphQL.ExecutionError: No service for type '...' has been registered.

It is GraphQL’s way of telling the world that it could not find an instance of a certain type in the DI container.

Solving it the easy way

The usual way to solve this problem is to register a new instance with the DI container which usually goes like this:

services.AddSingleton<SomeQuery>();

It works. But for large GraphQL api’s it doesn’t scale well, and it’s annoying for the developers to be remembered to register types with the DI container everytime a new type has been added.

Solving it for once and for all

Due to habit I tend to cluster the same types of classes together in the same folder/namespace.

My folder structure looks a bit like this:

Graph
|- Types
   |- Enum
   |- Input
   |- Interface
   |- Object

The nice thing about this is that all different types of ObjectGraphType derivatives are stored in the Graph.Types namespace, while also having a distinction between the several subtypes.

Now in order to solve the registration problem for once and for all you can add the following code block to the place where you usually register your graph types.

Assembly
    .GetExecutingAssembly()
    .GetTypes()
    .Where(q => q.IsClass
        && (q.Namespace?.StartsWith("Graph.Types") ?? false))
    .ToList()
    .ForEach(type => services.AddSingleton(type));

The code shown above invokes some reflection magic to retrieve all classes in the executing assembly (which in my case is also the assembly in which I have defined the graph types), filter them for the namespace, which is supplied as string, and add them to the DI container.

The only thing you need to do is to replace the namespace, and verify whether you pull your metadata from the correct assembly.

Use of reflection is being scrutinized because it is ‘slow’ and there are usually better alternatives. In this case it will lead to more maintainable code, and it is only ran during startup either way ¯\_(ツ)_/¯