DEV Community

Gregory Gofman
Gregory Gofman

Posted on

A tutorial on using entity framework core with polymorphism

This is a tutorial project dedicated to replacing conditionals with polymorphism in a real-world scenario. Please note that this is a fairly advanced tutorial, so one should have at least some experience with the specific technologies involved (aspnet.core and ef)

I will be using my github to provide samples.

The example provided above demonstrates how to use subclasses to avoid if cases, but what if we were to use polymorphism with objects that are fetched from the database, without having to specify classes inside our code?

So let's set a goal for a mock project and see how this is achieved.

An example domain where polymorphism can visibly improve the code quality could look something like this.

A backend server handles incoming data (Datapoints) from several IoT devices (Sensors). Incoming data is then resolved against a certain Threshold.

A Threshold is an object, that defines a rule that the Datapoint must meet. If the threshold is not met - we can that it has been triggered. If it has been met - the threshold has been resolved. Hence, the thresholds implement an IResolvable interface. (I'll provide a little bit more info on that later on, but for now - a good start would be checking out the Criteria Pattern.

This is an overall scheme of how this looks in the code. Take a look and let's go over its main details!
Image description

Code

This is a simple asp.net core project with Entity framework. Any template should be sufficient to start. This is mostly focused on describing the main concepts, layers, and peculiarities, so to get down to the core details one should take a stab at the code.

There is a test project included that mocks the whole cycle of receiving and resolving incoming data against an in-memory database.

Database and domain setup
Let's start out by creating our database and mapping out our domain.

To start let's take a look at our Models folder

As you can see. It contains an Abstract class as the base of each hierarchy (The aforementioned Sensors, Datapoints, and Thresholds). A concrete Sensor represents a concrete model of a sensor that is mapped to the base abstract class of another hierarchy.

This allows us to support relationship mapping between their respective concrete implementations.

One interesting thing to note is the properties of the Threshold class, Since it has a High and Low Threshold Value. These are typically the upper and lower limits on the incoming data points has to be in between of.

Our abstract classes will be used in the Context class to configure using one table per hierarchy

Using an abstract class will be tremendously helpful in keeping our code minimalistic since one of the aims here is to avoid referencing concrete implementations. (This will be discussed in the Logics and Data Resolution Chapter)

Please note that the context also features some data seeding, in case you want to create a database and give it a run.

Controllers and incoming data
This part should be the simplest, we have one controller that has a separate method for each type of data point, and maps (via automapper) the DTO to the respective model. Which is then handled by the Service layer.

Services and data resolving
So here's where things get a bit more interesting. That should be looked at here.

If you take a look at the classes that inherit the abstract Threshold class - they all implement the IResolvable interface.

As you can see. This interface means, that any threshold can Resolve a certain type of incoming data.

So when a Datapoint comes into our system the IncomingDataResolutionService is used to handle the data with the

public async Task ResolveDataPoint<T>(T dataPoint) where T : BaseDataPoint method.
Enter fullscreen mode Exit fullscreen mode

As you can see. this method does a couple of things.

1) Adds the data point to the database. This is possible due to our DbSet using the Base Datapoint class as its type parameter. (and the previously mentioned Ef table per hierarchy approach)

2) Gets all the thresholds for the sensor, from which the datapoint is coming, and for each threshold that has been triggered - saves that value in the triggered thresholds table. This is possible due to all retrieved thresholds implementing the IResolvable interface with the incoming datapoint type, so we don't really need to know what kind of threshold we are dealing with.

Aaand that's pretty much it! So this simple approach with using Polymorphism and Ef core's generic DbSets with the Table per hierarchy approach allows us to Handle incoming data without worrying about the concrete types involved in it's handling while enjoying the many perks of polymorphism!

Top comments (0)