CoreLMS: To DTO or Not to DTO, That is the Question (Among Other Thoughts)
Tags: csharp, dotnet, webdev
Published: 2020-08-20 22:59:33

---
title: "CoreLMS: To DTO or Not to DTO, That is the Question (Among Other Thoughts)"
published: true
description: "This week I hit a decision point: do I utilize DTOs for the UI and Business layers or just stick to the core entities, and if I do use them, where should property decorators (validators like required, string length constraints, etc.) go? Also: Codewars katas!"
tags: csharp,dotnet,webdev
series: CoreLMS
canonical_url: https://fct.dev/posts/core-lms-to-dto-or-not-to-dto-that-is-the-question
---

REALLY wanted to get the `CourseService` wrapped up this week, but ended up getting to one of those major decision points that will drive how things are done in the project from now on, and I didn't want to rush the decision.

The choice now is: to use DTOs (data transfer objects) or not.

I've never used them before, and I just can't get past the feeling that it ultimately amounts to a lot of code duplication (core entity + near-exact-copy DTO). 

I just don't know enough about the pattern (or how to do things efficiently/properly without it) yet.

Also: new fun was had! I think I'll start every stream with a [Codewars](https://www.codewars.com/) kata or two to warm up and fill in some syntax gaps I still have in ol' C#. Lots of fun.

## Get the Git

{% github FitzyCodesThings/core-lms no-readme %}

## Watch the Replay

{% youtube o8avmr8ywMA %}

## Skip to the Good Stuff

- Codewars Katas! [00:02:06](https://youtu.be/o8avmr8ywMA?t=126)
- `List<Course>` vs `IQueryable<Course>` [00:42:00](https://youtu.be/o8avmr8ywMA?t=2520)
- `AddCourseAsync` with TDD [01:17:00](https://youtu.be/o8avmr8ywMA?t=4620)
- To DTO or not to DTO? [01:41:39](https://youtu.be/o8avmr8ywMA?t=6099) 

## Project Update

I did get a few things done (including figuring out why I like a certain way of using the "repositoryish" pattern over another).

1. Updated eager-loading `.Include()`s on the Course entity (I'm used to lazy loading in EF and not having to think about it)
2. Added `SaveChanges()` and `SaveChangesAsync()` overrides to handle soft deletes (with a caveat (see below)) and auto-filling `DateCreated`, `DateUpdated`, and `DateDeleted` fields on `IAuditableEntity`s
3. Added the `AddCourseAsync` method to `CourseService` business logic (starting with the test, of course; really loving TDD as I get used to it)

## Soft Deletes in Entity Framework Core

**Now: the caveat on soft deletes.**

Implementing softdeletes in EF Core is pretty simple on the surface.

1. Use the new global query filter mechanism to filter soft-deletable objects with the `IsDeleted` flag set to true (or a DateDeleted, etc.):
```csharp
    protected override void OnModelCreating(ModelBuilder modelBuilder)
    {
        modelBuilder.Entity<Student>().HasQueryFilter(e => e.IsDeleted == false);
        modelBuilder.Entity<Address>().HasQueryFilter(e => e.IsDeleted == false);
    }
```

2. Override `SaveChanges` and `SaveChangesAsync` to intercept any entities in the Deleted state and change them to modified with the IsDeleted flag set:
```csharp
    foreach (var entry in ChangeTracker.Entries())
    {
        // Only process soft-deletables (implements interface ISoftDeletable)
        if (typeof(ISoftDeletable).IsAssignableFrom(entry.Entity.GetType()))
        {
            switch (entry.State)
            {
                case EntityState.Added:
                    entry.CurrentValues["IsDeleted"] = false;
                    break;

                case EntityState.Deleted:
                    entry.State = EntityState.Modified;
                    entry.CurrentValues["IsDeleted"] = true;
                    break;
            }
        }
    }

    return await SaveChangesAsync();
```

Seems simple enough, but I encountered an odd scenario if that's all you do to update deleted entities in an already-built project that I'm updating to .Net Core.

The long and short of it is: if an entity you soft delete is contained in an `ICollection` navigation property of a parent object (only scenario I've tested so far), the soft-deleted entry **will not be removed from the parent collection** as long as you're using the same scoped instance of the DbContext (so during the same web request, in the same `using` block, etc.).

I've detailed the issue and linked a demonstration project in the [GitHub issue here](https://github.com/dotnet/efcore/issues/22103), but let me know if you've run into this and how you solved it.

## Using Data Transfer Objects (DTOs)

Then, as mentioned in the intro, I got stuck on whether or not to use DTOs (then, if I do, whether I should add format and validation decorators on the DTOs or the core entities).

I'm gonna put up another post in the next few days as I explore best practices for using DTOs (or not using them) and what makes sense for me.

>Question: do you use DTOs? Why or why not? Any good articles/videos/courses I can check out that address it?

## Return `List` or Return `IQueryable`?

Otherwise: I took a look at the `GetCoursesAsync` method and considered whether to just return `List<Course>` like I had been versus `IQueryable<Course>`.

My argument for using `IQueryable` was that it just made things simpler for implementing various where clauses, limits, etc. 

The problem became very apparent, though, when I realized that there's no `ToListAsync` method on standard LINQ (it's an EF Core extension). This would mean I'd have to add an extra dependency on Entity Framework when I don't really want to (or give up Async operation, which I definitely don't want to do).

There may be another solution to the problem that I didn't see, but for now, my plan is to look at using custom predicates to get the dynamic querying that I want without having to just return an `IQueryable`.


## Wrapping Up

Have I mentioned this is a learning project?

And that it's mostly a learning project for ME? 🤣

Pace WILL pick up soon, but these are big decisions, and I want to get this (as) right (as possible) from the beginning.

Off to the Google I go to learn more about DTOs!

Until next time!

\- John