Chloe: a small .NET ORM with LINQ-style queries and a database per package
A lightweight and high-performance Object/Relational Mapping(ORM) library for .NET --C#
At a glance
- What is it?
- Chloe is an MIT-licensed C# ORM for .NET whose query interface resembles LINQ and which ships one NuGet package per database, including SqlServer, MySql, Oracle, SQLite, PostgreSQL, Dameng and KingbaseES. It supports joins, group queries, aggregates, raw SQL and sharding.
- Who is it for?
- Chloe fits a .NET team that wants a small ORM with a query syntax close to LINQ, that needs to target a domestic database such as Dameng or KingbaseES alongside the usual ones, and that is willing to work from a wiki rather than a full documentation site. It does not fit a team that needs an actively maintained dependency graph, since the last push was on 2026-10-14 but the newest release tag is from December 2021.
- Can I use it commercially?
- Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
- Is it still maintained?
- Yes. The repository last received commits 9 days ago.
- What is it written in?
- Mainly C#, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on October 23, 2026, and from our analysis. They are not legal advice.
Star history
- shuxinqin/Chloe1,541 ★ · +0
| Repository | 2026-10-16 | 2026-10-23 |
|---|---|---|
| shuxinqin/Chloe | 1.5k | 1.5k |
0 Stars in the last 7 days, measured from the daily GitHub snapshot. Compare with other projects
Editorial analysis
One package per database
Chloe is a lightweight Object/Relational Mapping library for .NET, and the query interface is similar to LINQ. The README's summary is that you can query data like LINQ and do joins, group queries, aggregate queries, inserts, batch updates, batch deletes and sharding by lambda with `Chloe.ORM`.
The packaging is the first thing to notice, because it decides whether the library is usable for you at all. There is a separate NuGet package for each database:
Install-Package Chloe.SqlServerInstall-Package Chloe.PostgreSQLThe full list in the table covers SqlServer, MySql, Oracle, SQLite, PostgreSQL, Dameng and KingbaseES, the last two being Chinese database vendors, 达梦 and 人大金仓 respectively.
That last pair is the differentiator. An ORM that ships first-class packages for Dameng and KingbaseES is built for a market where those databases are deployed, and it means you are not going to be the first person to run into their dialect quirks. For a team on a mainstream database, it is simply a longer list of packages to choose from.
The split into per-database packages is also what makes the library lightweight, since you only pull the provider you use. The trade-off is that the shared abstraction and the provider are separate packages, so a version mismatch between them is a thing to watch.
The licence is MIT, the primary language is C#, and the default branch is master. The last push was on 2026-10-14, and the visible release tags are v4.17.0, v4.18.0 and v4.19.0, all from 2021.
Entities as plain classes with attributes
The mapping model is the conventional attribute-based one, and the README's example is a good summary of the whole style.
[Table("Users")]
public class User
{
[Column(IsPrimaryKey = true)]
[AutoIncrement]
public int Id { get; set; }
public string Name { get; set; }
public Gender? Gender { get; set; }
public int? Age { get; set; }
public int? CityId { get; set; }
public DateTime? OpTime { get; set; }
}There is a `Table` attribute for the table name, a `Column` attribute with an `IsPrimaryKey` property, and an `AutoIncrement` attribute for identity columns. Nullable types are used where the column is nullable, and the `Gender` enum is nullable too, which is the usual C# way of modelling a nullable enum column.
The `City` and `Province` classes follow the same pattern, with a plain integer foreign key, `City.ProvinceId`, rather than a navigation property. That is a deliberate-looking choice, and it connects to the join syntax in the next section.
The context is created with a connection string and gives you a typed query:
IDbContext context = new MsSqlContext(DbHelper.ConnectionString);
IQuery<User> q = context.Query<User>();So the surface is an `IDbContext`, a per-database context implementation such as `MsSqlContext`, and `IQuery<T>` for the queries. The interface types are what your application code holds, and the concrete context is what carries the provider.
The query syntax and the SQL it produces
The README is unusually good at showing the generated SQL alongside each query, which is the only way to evaluate an ORM honestly.
A simple filter and list:
q.Where(a => a.Id > 0).ToList();which becomes a `SELECT` of the mapped columns `FROM [Users] AS [Users] WHERE [Users].[Id] > 0`, with a `LIKE` for a `Contains` call.
Ordering, paging and projection compose, and the generated SQL uses `ROW_NUMBER() OVER(...)` in a subquery to do the paging, which is the standard way to express `Skip` and `Take` in T-SQL:
q.Where(a => a.Id > 0).OrderBy(a => a.Age).ThenByDesc(a => a.Id).Select(a => new { a.Id, a.Name }).Skip(20).Take(10).ToList();The projection is worth noting. Because the select is an anonymous type over specific properties, only those columns are fetched, and the README's SQL confirms that the outer query selects only `Id` and `Name`.
Joins use an explicit `InnerJoin<T>` with a lambda rather than a navigation property:
var user_city_province = context.Query<User>()
.InnerJoin<City>((user, city) => user.CityId == city.Id)
.InnerJoin<Province>((user, city, province) => city.ProvinceId == province.Id);The lambda takes one parameter per table in order, so the second join receives the accumulated set. The result is a query object you can project from, and the README shows both projecting directly to an anonymous type and projecting to a nested shape, `new { User = user, City = city, Province = province }`, which you can then filter on as `a.User.Id`.
The README notes that left join, right join and full join are also supported, and points to the `IQuery` source file for details. The join syntax is more verbose than EF Core's navigation property approach, and the trade-off is that it does not require a mapped relationship to be declared anywhere.
Group queries, aggregates and raw SQL
Group queries go through an `IGroupingQuery<T>`, and the `Having` clause is part of the chain rather than a separate step:
IGroupingQuery<User> g = q.Where(a => a.Id > 0).GroupBy(a => a.Age);
g = g.Having(a => a.Age > 1 && Sql.Count() > 0);
g.Select(a => new { a.Age, Count = Sql.Count(), Sum = Sql.Sum(a.Age), Max = Sql.Max(a.Age), Min = Sql.Min(a.Age), Avg = Sql.Average(a.Age) }).ToList();The `Sql` static class is the interesting part. `Sql.Count()`, `Sql.Sum(a.Age)` and the rest are marker calls that the ORM translates into the corresponding SQL aggregate, and the generated SQL shows `COUNT(1)`, `SUM([Users].[Age])`, `MAX`, `MIN` and `CAST(AVG(...) AS FLOAT)` for the average.
Aggregates also work directly on a query without a group:
q.Select(a => Sql.Count()).First();
var sum = q.Sum(a => a.Age);which produce `SELECT TOP (1) COUNT(1) AS [C] FROM ...` and `SELECT SUM([Users].[Age]) AS [C] FROM ...`. Note the `TOP (1)` and the single-column projection, which is the shape you want from a scalar query rather than a row.
Raw SQL is available too, through `SqlQuery<T>`, with parameters created by `DbParam.Create`:
context.SqlQuery<User>("select Id,Name,Age from Users where Name=@name", DbParam.Create("@name", "lu")).ToList();That form works for a scalar type as well, `context.SqlQuery<int>("select Id from Users").ToList()`, so the escape hatch is not limited to mapped entities.
The README also lists inserts, batch updates, batch deletes and sharding among the supported operations, and there is a Method section in the documentation for the procedural API, which is the part of an ORM that is usually the least interesting and the most used.
What the repository actually contains
The repository is small in a way that tells you where the documentation lives. The top level entries are `.gitattributes`, `.gitignore`, `LICENSE`, `README.md`, `src/` and `更新日志.md`, which is the changelog in Chinese.
That is it. There is no `docs/` directory, no `test/` directory, no `samples/` directory, no CI configuration, no issue templates, no contributing guide and no Dockerfile. The documentation is on the project wiki at github.com/shuxinqin/Chloe/wiki, and the README is largely an excerpt of that wiki content.
So the code is in `src/`, and everything else about how to use it is on a wiki page. That is a meaningful difference in how a project is maintained and how you would work with it. A wiki is easy to update, which is why it stays current for a project like this, and it is also easy to lose track of, and it is not versioned alongside the code, so a wiki page describing a feature may describe a version that no longer exists.
The absence of a test project in the repository is worth flagging too. The generated SQL in the README is effectively the specification of the library's behaviour, and the absence of committed tests means that if the project were to change its translation, the only way to know would be to write your own tests against your own database.
The changelog file being in Chinese is consistent with the rest of the project's origin and audience, and the packages for Dameng and KingbaseES point the same way.
Version history and the maintenance question
The release tags are the part of the picture that needs care. The visible versions are v4.17.0 from 2021-08-04, v4.18.0 from 2021-12-12 and v4.19.0 from 2021-12-19, and the topic list on the repository includes c-sharp, chloe, dotnet, dotnetcore and the database names.
The last push was on 2026-10-14, so the repository is not abandoned and code is still landing on master. But there have been no release tags since December 2021, and the README carries no release notes for anything newer. The practical question is which build you would actually install from NuGet, and the answer for a new project is the 4.19.0 packages from four years ago unless the author has pushed newer packages to the feed that the tags do not reflect.
That is the thing to check before committing to this library, and it is checkable: look at the package version on NuGet for your database, compare it with the tags, and read the changelog file in the repository root to see how far the work has actually progressed since.
The library's own scope is the other consideration. It is described as lightweight, and the API surface is small and legible: attributes on plain classes, an `IDbContext`, `IQuery<T>`, the `Sql` marker class and a raw SQL escape hatch. There is no change tracking, no lazy loading and no migrations in the description of what it does, so a team that wants a full ORM with migrations and unit of work would be assembling those separately.
Editorial conclusion
Chloe fits a .NET team that wants a small ORM with a query syntax close to LINQ, that needs to target a domestic database such as Dameng or KingbaseES alongside the usual ones, and that is willing to work from a wiki rather than a full documentation site. It does not fit a team that needs an actively maintained dependency graph, since the last push was on 2026-10-14 but the newest release tag is from December 2021. Before adopting it, check the wiki for the package for your database, and confirm the generated SQL for your most-used queries matches what you expect, since the ORM's shape is close enough to LINQ that a subtle translation difference is easy to miss.
Frequently asked questions
What is Chloe?
It is a lightweight Object/Relational Mapping library for .NET whose query interface is similar to LINQ. You can do join queries, group queries, aggregate queries, inserts, batch updates, batch deletes and sharding by lambda with Chloe.ORM. It is MIT licensed.
Which databases does Chloe support?
There is a separate NuGet package for each: Chloe.SqlServer, Chloe.MySql, Chloe.Oracle, Chloe.SQLite, Chloe.PostgreSQL, Chloe.Dameng for Dameng and Chloe.KingbaseES for KingbaseES. You install only the package for the database you use.
How do I write a join query with Chloe?
With an explicit InnerJoin on the query object, where the lambda takes one parameter per table in order. The result is a query you can project from, either directly to an anonymous type or to a nested shape that you can then filter on.
Where is the Chloe documentation?
On the project wiki at github.com/shuxinqin/Chloe/wiki, which the README links as the documentation. The repository itself contains only the source under src/, the README and a Chinese changelog file, with no docs, test or samples directories.
Is Chloe still maintained?
The repository is not archived and the last push was on 2026-10-14, but the newest visible release tag is v4.19.0 from 2021-12-19. Check the package version on NuGet and the changelog file in the repository before committing to it.
Official sources
Add this badge to your README
If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.
[](https://hysenlabs.com/projects/shuxinqin-chloe)Star history chart for your README
[](https://hysenlabs.com/projects/shuxinqin-chloe)Was this useful?