Scott Hanselman

Exploring the .NET open source hybrid ORM library RepoDB

July 16, '20 Comments [8] Posted in Open Source
Sponsored By

ShutterstockIt's nice to explore alternatives, especially in open source software. Just because there's a way, or an "official" way doesn't mean it's the best way.

Today I'm looking at RepoDb. It says it's "a hybrid ORM library for .NET. It is your best alternative ORM to both Dapper and Entity Framework." Cool, let's take a look.

Michael Pendon, the author puts his micro-ORM in the same category as Dapper and EF. He says "RepoDb is a new hybrid micro-ORM for .NET designed to cater the missing pieces of both micro-ORMs and macro-ORMs (aka full-ORMs). Both are fast, efficient and easy-to-use. They are also addressing different use-cases."

Dapper is a great and venerable library that is great if you love SQL. Repo is a hybrid ORM and offers more than one way to query, and support a bunch of popular databases:

Here's some example code:

/* Dapper */
using (var connection = new SqlConnection(ConnectionString))
{
var customers = connection.Query<Customer>("SELECT Id, Name, DateOfBirth, CreatedDateUtc FROM [dbo].[Customer];");
}

/* RepoDb - Raw */
using (var connection = new SqlConnection(ConnectionString))
{
var customers = connection.ExecuteQuery<Customer>("SELECT Id, Name, DateOfBirth, CreatedDateUtc FROM [dbo].[Customer];");
}

/* RepoDb - Fluent */
using (var connection = new SqlConnection(ConnectionString))
{
var customers = connection.QueryAll<Customer>();
}

I like RepoDB's strongly typed Fluent insertion syntax:

/* RepoDb - Fluent */
using (var connection = new SqlConnection(connectionString))
{
var id = connection.Insert<Customer, int>(new Customer
{
Name = "John Doe",
DateOfBirth = DateTime.Parse("1970/01/01"),
CreatedDateUtc = DateTime.UtcNow
});
}

Speaking of inserts, it's BulkInsert (my least favorite thing to do) is super clean:

using (var connection = new SqlConnection(ConnectionString))
{
var customers = GenerateCustomers(1000);
var insertedRows = connection.BulkInsert(customers);
}

The most interesting part of RepoDB is that it formally acknowledges 2nd layer caches and has a whole section on caching in the excellent RepoDB official documentation. I have a whole LazyCache subsystem behind my podcast site that is super fast but added some complexity to the code with more Func<T> that I would have preferred.

This is super clean, just passing in an ICache when you start the connection and then mention the key when querying.

var cache = CacheFactory.GetMemoryCache();
using (var connection = new SqlConnection(connectionString).EnsureOpen())
{
var products = connection.QueryAll<Product>(cacheKey: "products", cache: cache);
}

using (var repository = new DbRepository<Product, SqlConnection>(connectionString))
{
var products = repository.QueryAll(cacheKey: "products");
}

It also shows how to do generated cache keys...also clean:

// An example of the second cache key convention:
var cache = CacheFactory.GetMemoryCache();
using (var connection = new SqlConnection(connectionString).EnsureOpen())
{
var productId = 5;
Query<Product>(product => product.Id == productId,
cacheKey: $"product-id-{productId}",
cache: cache);
}

And of course, if you like to drop into SQL directly for whatever reason, you can .ExecuteQuery() and call sprocs or use inline SQL as you like. So far I'm enjoying RepoDB very much. It's thoughtfully designed and well documented and fast. Give it a try and see if you like it to?

Why don't you head over to https://github.com/mikependon/RepoDb now and GIVE THEM A STAR. Encourage open source. Try it on your own project and go tweet the author and share your thoughts!


Sponsor: Have you tried developing in Rider yet? This fast and feature-rich cross-platform IDE improves your code for .NET, ASP.NET, .NET Core, Xamarin, and Unity applications on Windows, Mac, and Linux.

About Scott

Scott Hanselman is a former professor, former Chief Architect in finance, now speaker, consultant, father, diabetic, and Microsoft employee. He is a failed stand-up comic, a cornrower, and a book author.

facebook twitter subscribe
About   Newsletter
Sponsored By
Hosting By
Dedicated Windows Server Hosting by SherWeb
Monday, July 20, 2020 10:57:57 AM UTC
Very interesting.

I prefer to use Dapper + Entity2SQL
Monday, July 20, 2020 12:38:13 PM UTC
I saw this the other and really like it. I've always found EF a bit heavy for my needs and have been a user of Dapper in the past. But there's a lot of boilerplate you end up writing and this library seems to fill that sweet spot. I'd love to use it but the problem is Microsoft and open source - will the former release an alternative if it becomes popular?
Monday, July 20, 2020 8:38:06 PM UTC
This seems great. I've been working with Dapper and for Selects is great and simple, but for Insert/Update/Delete is a bit more messy, this looks like a better overall Api. Will give it a try, thanks!
Fabricio
Tuesday, July 21, 2020 12:18:11 PM UTC
Good to know new options available.

We are using Linq2db for a while now (https://github.com/linq2db/linq2db), and very pleased with it. I'm not sure if it gets enough attention, so I've dropped this line in case readers here are not aware of it.
J. Hudler
Wednesday, July 22, 2020 12:50:42 AM UTC
Dapper never die)
I used this one for many years for the same reasons - not write SQL manually

Dapper.SimpleCRUD
More Docs

Looks like RepoDB with cache support is an interesting thing
Alexander
Thursday, July 23, 2020 10:19:15 AM UTC
I have created a simple application with ASP.NET Core and use RepoDb to work with SQL Server database.

https://github.com/Arnab-Developer/repodb-demo
Saturday, July 25, 2020 6:02:57 PM UTC
Thank you for sharing. Glad to know about new things.
Friday, July 31, 2020 9:49:27 PM UTC
Prefer SQL query strings for all operations with parameters.

Dislike "failing the first unit test" of fluent interfaces (no contract between method called and code calling that method).

Fluent fails to have a contract between the user of the fluent interface and the fluent interface.

Consider this mess from the Azure C# samples on creating a virtual machine on how much extra work a fluent interface puts on the client developer. 19 extra calls to do what should be one or two calls.
Fluent:
- Easier on the API developer, easier to write simple methods, easier to document, easier to unit test, and easier to return a generic uninformative error when there is a problem
- MUCH HARDER on the developer using the API
- What needs to be called, what order to call them, how to test, how to debug,

From Azure samples: https://github.com/Azure-Samples/compute-dotnet-create-virtual-machines-from-generalized-image-or-specialized-vhd/blob/master/Program.cs

Utilities.Log("Creating a Linux VM");

var linuxVM = azure.VirtualMachines.Define(linuxVmName1)
.WithRegion(Region.USWest)
.WithNewResourceGroup(rgName)
.WithNewPrimaryNetwork("10.0.0.0/28")
.WithPrimaryPrivateIPAddressDynamic()
.WithNewPrimaryPublicIPAddress(publicIpDnsLabel) .WithPopularLinuxImage(KnownLinuxVirtualMachineImage.UbuntuServer16_04_Lts)
.WithRootUsername(UserName)
.WithRootPassword(Password)
.WithUnmanagedDisks()
.WithSize(VirtualMachineSizeTypes.StandardD3V2)
.DefineNewExtension("CustomScriptForLinux")
.WithPublisher("Microsoft.OSTCExtensions")
.WithType("CustomScriptForLinux")
.WithVersion("1.4")
.WithMinorVersionAutoUpgrade()
.WithPublicSetting("fileUris", ApacheInstallScriptUris)
.WithPublicSetting("commandToExecute", ApacheInstallCommand)
.Attach()
.Create()

This fluent API lets the MS dev documentation be generated from the method headers with one line of text for the method explanation and one line of text for the parameter and no mention of the exceptions for each method.

First unit test of programming: Let the compiler detect problems when passing arguments to a function (check types, check number of arguments, maybe even semantic checking - cannot pass null literals)

The above fluent API example is more difficult to use than writing the API as a set of well defined contracts.

Azure SDK needs a re-think and not be dumbed down
Dapper
Comments are closed.

Disclaimer: The opinions expressed herein are my own personal opinions and do not represent my employer's view in any way.