GenericServices Masterclass: a deeper look at Create and Update

Last Updated: July 31, 2020 | Created: April 21, 2015

I have been building a new application using GenericServices and I thought it would be useful to others to reflect on the approaches I have used when working with more complex data. In this first master class article I am looking at Create and Update of data, especially via DTOs (Data Transfer Objects).

What is GenericServices??

For those of you who are not familiar with GenericServices it is an open-source .NET library designed to simplify the interface between an ASP.NET MVC application and a database handled by Microsoft’s Entity Framework (EF) data access technology. You can read more about it at https://github.com/JonPSmith/GenericServices where the Readme file has links to two example web sites. The GenericServices’ Wiki also has lots more information about the library.

If you are not familiar or interested in GenericServices then save yourself time and skip this post.

Introduction to create and update with DTOs

GenericServices has five primary data access methods: List, Detail, Create, Update and Delete. Create and Update are the most complicated as they write data back to the database. If you are working directly with the database classes then it is fairly straightforward: in this case GenericServices is executing the EF commands for you through a standardised interface that adds some extra code to check and return any validation or selected SQL errors.

Create and update become more complex when you are using DTOs. However, if you have read any of my articles, such as Why DTOs, then you will know that 95% of my accesses to the database go through DTOs. Therefore handling DTOs is critical and GenericServices is built primarily for DTO mode database accesses.

The most common use of DTO is in reading data, but for Create or Update the DTOs need to be transformed back to the database entity before that entity is written out to the database. There are four levels of complexity, starting with the simplest and working up to the more complex. I will list them here and then go into more detail on what to do later on.

  1. Simple one-to-one mapping: Each property is a simple data type, i.e not another user class or relationship, and has an equivalent DTO property. In this case GenericServices ‘just works’.
  2. One-to-one mapping, but some properties are read-only. This case is like the first, but maybe you have properties you want to show the user, but you don’t want them to update them. A obvious example might be the price of something they can buy. In this case you will use the [DoNotCopyBackToDatabase] attribute – see later for an example.
  3. Contains properties that are developer defined classes or collections. In this case you might have a relationship class, a collection or an EF complex type. In this case AutoMapper will read these property using its conventions, but by default will not write them back. You need to add extra code to handle this which I will cover later.
  4. Contains properties that must be calculated on write. This happens when the value to be written depends on the user’s input, or we need to do some drastic reformat to the data before writing back. Again, I will show you some example later.

Before I describe these four cases I would like to point out that AutoMapper and the DelegateDecompiler’s [Computed] attribute (see GenericServices Wiki page on Calculated Properties) are all designed to work when READing from the database. When writing to the database GenericServices does use AutoMapper, but it has to supplement extra commands as, by default, AutoMapper is not meant to be used for write to database actions. That is why this article and the various examples exist – to show you how to handle this write operation successfully.

So, with that background let me go through all the four cases above.

1. Simple one-to-one mapping

To be honest if you have a simple one to one mapping between the data class and the DTO then you most likely should be using GenericServices with the data class itself as it is quicker and more transparent. I can’t think of any cases that I have had a ‘simple’ mapping where I used a DTO. However it will work if you do.

2. One-to-one mapping, but some properties are read-only

[DoNotCopyBackToDatabase] is GenericServices’ version of MVC’s [Bind(Exclude = “SomeProperty”)]. Both allow a property to be marked as not being overwritten by user input. You can get more about this in the GenericServices Wiki DoNotCopyBackToDatabase attribute page.

The good example is given in the Wiki page, i.e. you want a user to see the price of their purchase, but they should not be able to change the price. I use this myself in one of my applications to hold a status flag that can be seen by the user, but can only be changed by the business logic. Therefore I mark the property in the DTO with [DoNotCopyBackToDatabase] to ensure it can not be changed either by accident or maliciously.

In SampleMvcWebApp I apply [DoNotCopyBackToDatabase] in the DetailPostDto.cs class to stop the LastUpdated property being coped back. However that is really a comment as any value would be overwritten by the code inside the SampleMvcWebApp’s .SaveChanged() method which sets that property at save time.

 3. Contains properties that are developer defined classes or collections

This is where it starts to get more complicated. There are a few possible scenarios and I will deal with each in turn:

a. A relationship class

This is where you have a class that is a relationship. An example from SampleMvcWebAppComplex is a CustomerAddress class, which has a one-to-one relationship with the Address class, which holds the address details. In SampleMvcWebAppComplex I use a top level DTO called CrudCustomerAddressDto to hold all the customer address data. This type of situation doesn’t happen often, but when they do you need a way to handle them.

Firstly let me explain why the normal mappings wouldn’t work. If we used AutoMapper’s flattening convention it could read properties in the associated Address class by concatenating the names, e.g. AddressCity would read the City property in the Address part of CustomerAddress. The problem is it can read, but it won’t write as AutoMapper doesn’t ‘un-flatten’ things, i.e. setting the property AddressCity in the DTO will not be mapped back to Address.City in the CustomerAddress class.

There are a few ways round this. One is to hand-code the copy back, but we can also use AutoMapper’s nested mappings feature, which is what I actually did in this case. To use this I need to create a sub-DTO, called CrudAddressDto. I then need to do two extra things in the CrudCustomerAddressDto class.

Firstly I have to tell GenericServices to make sure that AutoMapper knows about how to map the CrudAddressDto, otherwise I will get an error from AutoMapper if I try to do the mapping. That is achieved by overriding AssociatedDtoMapping method in CrudCustomerAddressDto to add the extra mapping as show below (note: there is a AssociatedDtoMappings version if you need multiple extra mappings) :

protected override Type AssociatedDtoMapping
{
   get { return typeof(CrudAddressDto); }
}

Secondly for the update to work I need to make sure I have loaded the CustomerAddress and the associated Address class. This is done by overriding the FindItemTrackedForUpdate method (see below). If I don’t do this then updating the CustomerAddress will create a NEW Address class rather than update the existing class. As that Address class may be used elsewhere, such as in the delivery address, then its important to get that right.

 protected override CustomerAddress FindItemTrackedForUpdate(IGenericServicesDbContext context)
 {
    return context.Set<CustomerAddress>()
                             .Where(x => x.CustomerID == CustomerID && x.AddressID == AddressID)
                             .Include(x => x.Address).SingleOrDefault();
 }

b. A Complex Type

A ‘complex type’ is the term EF uses for a reference to class which has no key  – see section entitled ‘Complex Types Convention’ in Entity Framework documentation. This is often used to simplify or separate parts of the class.

If you want to do this then you can use the nesting mapping approach shown above to do the to/from mapping of the complex types. However, as complex types are simply a way of arranging things I tend ‘flatted at design time’, i.e. I don’t use complex types. That means that AutoMapper will do both the read and the write.

If that simplification does not work for you and you want to use Complex Types then you need to create a new dto for each complex type and override the AssociatedDtoMapping or AssociatedDtoMappings to allow GenericServices to set up the AutoMapper mappings. You don’t need to override FindItemTrackedForUpdate, as the class is in fact one single row in the database.

c. A Collection

This is the most complex case because a collection refer to multiple relationships (EF does not support lists and arrays of simple data like bytes or strings). As you can imagine, updating multiple relationships is complex and has to be hand coded by overriding CreateDataFromDto and UpdateDataFromDto, which I will explain in the next section.

I should say that updating multiple relationships isn’t a simple problem anyway. This normally falls under the next section, Contains properties that must be calculated on write, as it is complex enough that GenericServices cannot make a guess as to your intentions.

Contains properties that must be calculated on write

There are times where a property in a class is either set by the user via some other means, like a dropdownlist, or the data in the DTO needs a more complicated reformatting/parsing to get the required value for the property. In these cases the developer needs to intercept the CreateDataFromDto and UpdateDataFromDto methods and write code to set the properties.

Let us start by looking at the anatomy of the two methods. The first thing to note is that they are very similar, but have a slightly different signature. The method signatures are different because update is handed an database entity to update while the create returns a new instance of the database entity class (This was done because databases using a Domain-Drive Designs (DDD) approach the creation of a entity may be done via a factory). This means that the handling of the return status is slightly different between the two methods

However when overriding these methods the code inside has a common approach. Let us take UpdateDataFromDto as an example and look at the three stages the update and create goes through.

protected override ISuccessOrErrors UpdateDataFromDto(
    IGenericServicesDbContext context, TDto source, TEntity destination)
{
   //1) Before copy/create: If you put your code here then it should
   //set properties in the source, which AutoMapper will then copy over

   //2) Call the base method which uses AutoMapper to create/copy the data
   var status = base.UpdateDataFromDto(context, source, destination);

   //3) After copy/create: If you put your code here then it should
   //set properties in the destination, ready to be written out

   return status;
}

So, your options are:

  1. Change properties in the DTO and then let AutoMapper copying them over for you.
  2. Wait until Automapper has run and change the properties in the database entity class.
  3. Don’t call the base method and handle the whole copy/conversion yourself (this is what you would need to do for DDD databases).

I have used option one and two depending on the need. Option ones is good if the property needs to be in the DTO while option 2 is useful when you want to exclude a property from the DTO and only set it via your code. In both cases I try to do any validation of the values early so that if it fails it returns immediately and therefore saves the redundant call to AutoMapper.

SampleMvcWebApp has a classic example of option one in DetailPostDto. DetailPostDto is complex because it allows the user to select the post’s author from a dropdownlist and the post’s tags from a multi-select list. If you look at the code you will see the overrides of CreateDataFromDto and UpdateDataFromDto around line 140 or so. To save you swapping pages here is a copy of the overridden methods from DetailPostDto:

protected override ISuccessOrErrors<Post> CreateDataFromDto(IGenericServicesDbContext context,
    DetailPostDto source)
{
    var status = SetupRestOfDto(context);
    return status.IsValid
        ? base.CreateDataFromDto(context, this)
        : SuccessOrErrors<Post>.ConvertNonResultStatus(status);
}

protected override ISuccessOrErrors UpdateDataFromDto(IGenericServicesDbContext context,
    DetailPostDto source, Post destination)
{
    var status = SetupRestOfDto(context, destination);
    return status.IsValid
        ? base.UpdateDataFromDto(context, this, destination)
        : status;
}

As you can see both methods call a private method called SetupRestOfDto which handles the set up of the BloggerId and the Tags properties in the DTO. The SetupRestOfDto method needs to know if its a create or update, which it knows because if the Post is not given it is set to null. On return if the status for the SetupRestOfDto method is OK, the codes then calls the base method to do the create/update. This is a typical way of handling user selections from dropdown list.

I used option two, setting the value in the database entity class, in a recent project when I wanted to make the user input much simpler. In this case the user needed to input to input a CMYK colour and a RGB colour. The colours were stored as a series of numbers but it was simpler for the user to enter the CMYK and the RGB as two strings of digits, with different options on hex/decimal etc. I therefore overrided the create/update method and parsed the two strings into the right format directly into the database entity class.

I am sure your project has other requirements that GenericServices does not do as its stands. Overriding CreateDataFromDto and UpdateDataFromDto is the go-to place to add your code and still benefit from all of GenericServices’ features.

Note: Writing this section made me realise I could have simplified the the method signatures of the CreateDataFromDto and UpdateDataFromDto as I don’t (now) need to pass in the DTO. However that would be a breaking change to the interface and I’ll leave that for the next big change of GenericServices.

Conclusion

In this article I have covered in detail what happens in a Create or Update inside GenericServices. It is complex, especially around handling relationships. Hopefully this article will help anyone who needs to implement any of the more complex use of Create and Update.

Do ask any questions or make comments here, or raise an issue on the GenericServices GitHub site if you think there is a bug. Happy to answer any questions as it helps increase the documentation for the project.

Happy coding.

 

0 0 votes
Article Rating
Subscribe
Notify of
guest
10 Comments
Newest
Oldest Most Voted
Inline Feedbacks
View all comments
Özlem Hancıoğlu Başerdem
Özlem Hancıoğlu Başerdem
3 years ago

Hi,

Can you please help me. Is it possible to make transactional operations? I need to update two tables at the same time but I don’t know how to handle it in a good way.

Jon P Smith
3 years ago

Hi Özlem,

Your question is rather wide in scope, so here are some thoughts (note: I assume you are using Entity Framework (EF), as that is what my article is about):

Firstly, in EF to can update multiple tables in one go: you just update each class and then call EF’s SvaeChanges. EF updates those tables within a transaction. The other thing to understand about EF is that if you have a relationship link between new classes, then EF is clever enough to spot that and fill in the foreign key for you in that transaction.

If you are talking about my GenericServices library, then by default you can’t do that, as GenericServices is designed to do one update at a time. This article shows you how you can create related items to the main class, but that doesn’t sound like that fits your question.

I hope that helps.

Özlem Hancıoğlu Başerdem
Özlem Hancıoğlu Başerdem
4 years ago
Reply to  Jon P Smith

Hi,

I am using GenericServices library. Tables that I want to update transactional are not relational. So I can not do what I want 🙂

Thanks for your quick reply,
Regards.

Özlem Hancıoğlu Başerdem
Özlem Hancıoğlu Başerdem
4 years ago
Reply to  Jon P Smith

Hi Jon,

I want to update all records in table where condition fits. Can I update all records in one state?

var docList = uow.listService.GetAll().Where(d => d.FolderId == 1 &&
d.Status == 1).ToList();
docList.ForEach(d => d.Status = 3);

I want to send all docList to db.

That’s the query I want to do:

Update doc
set doc.Status = 3
where FolderId = 1 and Status = 1

Thanks,

Jon P Smith
4 years ago

Hi Özlem,

Sorry, but that won’t work as GetAll returns .AsToTracked entities. GenericServices only does the basic CRUD services so you need to call EF directly to do anything more complex.

Filip Hurta
Filip Hurta
7 years ago

How would you specify member mapping for collection? (If the collection member’s name on DTO does not correspond to the relationship’s name on EF class.)

Jon Smith
7 years ago
Reply to  Filip Hurta

Hi Filip,

GenericServices’s EfGenericDto class has a method called `AddedDatabaseToDtoMapping` that you can override to provide extra mapping information to AutoMapper. AutoMapper has a method called `.ForMember` that allows you to define your own mappings. See:

1. AutoMapper’s Projections documentation for information on how to use it.
2. An example of using `.AddedDatabaseToDtoMapping` in SampleMvcWebAppComplex’s ListProductDt. In that example I am mapping a lamda function, but you can replace that with the simpler`MapFrom` as shown in the AutoMapper docs.

I hope that helps.

Filip Hurta
Filip Hurta
7 years ago
Reply to  Jon Smith

Thanks, fixed!
However, I had to downgrade AutoMapper to 4.2.1. With current (5.1.1) version I’m getting TypeLoadException. Do you plan to update GenericServices to work with AutoMapper 5? Further, there is unnecessary reference to Mono.Reflection in the package, which seems not be used anywhere.

Jon Smith
7 years ago
Reply to  Filip Hurta

Hi Filip,

That is a pity that it doesn’t work with AutoMapper 5. I though when I changed GenericServices over to version 4 my fix would work with version 5.

Could you raise an issue on GenericServices GitHub when the code that fails and a full stack trace (when you have time). Thanks.

I am really busy so I won’t be able to look at it straight away, but I will try and sort it out.

Filip Hurta
Filip Hurta
7 years ago
Reply to  Jon Smith

Done, thanks for reply.
Stack trace would not be needed as the source code does not compile with AM5 (removed interface used by GS, details in GitHub’s issue).