Tools
Tools: Stop using magic strings in your Firestore .NET queries
2026-03-08
0 views
admin
What it looks like ## Updates are type-safe too ## How it works ## Getting started If you use Firestore with .NET, you've probably written code like this: That "Location.home_country" is a string. Nobody checks it at compile time. If you typo it, rename a property, or forget that your C# property Country maps to home_country in Firestore, you won't know until runtime. I kept running into this in my own projects, so I wrote a typed wrapper around Google's official Firestore client that uses lambda expressions instead of strings. The library reads [FirestoreProperty] attributes automatically, so you always use the C# property name and it resolves the Firestore storage name for you. With the official client, you can pass any object as a field value. With the typed client, the value type is inferred from the property: Multi-field updates also work: Compare that to the official client: The library uses a MemberExpression visitor to walk the lambda expression tree, check each property for [FirestoreProperty] attributes, and build the correct Firestore field path. Simple fields resolve in about 450ns, nested fields in about 1μs. In practice, this is invisible next to the network call to Firestore. Everything else (transactions, listeners, batched writes, subcollections) is delegated directly to the official Google.Cloud.Firestore client. You're not giving up any functionality. Targets .NET Standard 2.0, so it works with .NET Framework 4.6.1+ through .NET 10. Source: https://github.com/mihail-brinza/firestore-dotnet-typed-client Templates let you quickly answer FAQs or store snippets for re-use. Are you sure you want to hide this comment? It will become hidden in your post, but will still be visible via the comment's permalink. Hide child comments as well For further actions, you may consider blocking this person and/or reporting abuse CODE_BLOCK:
query.WhereEqualTo("Location.home_country", "Portugal"); Enter fullscreen mode Exit fullscreen mode CODE_BLOCK:
query.WhereEqualTo("Location.home_country", "Portugal"); CODE_BLOCK:
query.WhereEqualTo("Location.home_country", "Portugal"); COMMAND_BLOCK:
// Before — string-based, no compile-time checking
query.WhereEqualTo("Locaiton.home_country", "Portugal"); // typo, compiles fine // After — lambda-based, compiler catches errors
query.WhereEqualTo(u => u.Locaiton.Country, "Portugal");
// CS1061: 'User' does not contain a definition for 'Locaiton' Enter fullscreen mode Exit fullscreen mode COMMAND_BLOCK:
// Before — string-based, no compile-time checking
query.WhereEqualTo("Locaiton.home_country", "Portugal"); // typo, compiles fine // After — lambda-based, compiler catches errors
query.WhereEqualTo(u => u.Locaiton.Country, "Portugal");
// CS1061: 'User' does not contain a definition for 'Locaiton' COMMAND_BLOCK:
// Before — string-based, no compile-time checking
query.WhereEqualTo("Locaiton.home_country", "Portugal"); // typo, compiles fine // After — lambda-based, compiler catches errors
query.WhereEqualTo(u => u.Locaiton.Country, "Portugal");
// CS1061: 'User' does not contain a definition for 'Locaiton' COMMAND_BLOCK:
// won't compile — Age is int
await doc.UpdateAsync(u => u.Age, "eighteen"); // works
await doc.UpdateAsync(u => u.Age, 18); Enter fullscreen mode Exit fullscreen mode COMMAND_BLOCK:
// won't compile — Age is int
await doc.UpdateAsync(u => u.Age, "eighteen"); // works
await doc.UpdateAsync(u => u.Age, 18); COMMAND_BLOCK:
// won't compile — Age is int
await doc.UpdateAsync(u => u.Age, "eighteen"); // works
await doc.UpdateAsync(u => u.Age, 18); COMMAND_BLOCK:
var update = new UpdateDefinition<User>() .Set(u => u.Age, 18) .Set(u => u.Location.Country, "Spain"); await document.UpdateAsync(update); Enter fullscreen mode Exit fullscreen mode COMMAND_BLOCK:
var update = new UpdateDefinition<User>() .Set(u => u.Age, 18) .Set(u => u.Location.Country, "Spain"); await document.UpdateAsync(update); COMMAND_BLOCK:
var update = new UpdateDefinition<User>() .Set(u => u.Age, 18) .Set(u => u.Location.Country, "Spain"); await document.UpdateAsync(update); CODE_BLOCK:
var updates = new Dictionary<FieldPath, object>
{ { new FieldPath("Age"), 18 }, { new FieldPath("Location.home_country"), "Spain" }
};
await document.UpdateAsync(updates); Enter fullscreen mode Exit fullscreen mode CODE_BLOCK:
var updates = new Dictionary<FieldPath, object>
{ { new FieldPath("Age"), 18 }, { new FieldPath("Location.home_country"), "Spain" }
};
await document.UpdateAsync(updates); CODE_BLOCK:
var updates = new Dictionary<FieldPath, object>
{ { new FieldPath("Age"), 18 }, { new FieldPath("Location.home_country"), "Spain" }
};
await document.UpdateAsync(updates); CODE_BLOCK:
dotnet add package Firestore.Typed.Client Enter fullscreen mode Exit fullscreen mode CODE_BLOCK:
dotnet add package Firestore.Typed.Client CODE_BLOCK:
dotnet add package Firestore.Typed.Client COMMAND_BLOCK:
FirestoreDb db = await FirestoreDb.CreateAsync("your-project-id");
TypedCollectionReference<User> collection = db.TypedCollection<User>("users"); TypedQuery<User> query = collection .WhereGreaterThanOrEqualTo(u => u.Age, 18) .WhereEqualTo(u => u.Location.Country, "Portugal") .OrderBy(u => u.Age); TypedQuerySnapshot<User> results = await query.GetSnapshotAsync();
foreach (TypedDocumentSnapshot<User> doc in results.Documents)
{ User user = doc.Object;
} Enter fullscreen mode Exit fullscreen mode COMMAND_BLOCK:
FirestoreDb db = await FirestoreDb.CreateAsync("your-project-id");
TypedCollectionReference<User> collection = db.TypedCollection<User>("users"); TypedQuery<User> query = collection .WhereGreaterThanOrEqualTo(u => u.Age, 18) .WhereEqualTo(u => u.Location.Country, "Portugal") .OrderBy(u => u.Age); TypedQuerySnapshot<User> results = await query.GetSnapshotAsync();
foreach (TypedDocumentSnapshot<User> doc in results.Documents)
{ User user = doc.Object;
} COMMAND_BLOCK:
FirestoreDb db = await FirestoreDb.CreateAsync("your-project-id");
TypedCollectionReference<User> collection = db.TypedCollection<User>("users"); TypedQuery<User> query = collection .WhereGreaterThanOrEqualTo(u => u.Age, 18) .WhereEqualTo(u => u.Location.Country, "Portugal") .OrderBy(u => u.Age); TypedQuerySnapshot<User> results = await query.GetSnapshotAsync();
foreach (TypedDocumentSnapshot<User> doc in results.Documents)
{ User user = doc.Object;
}
how-totutorialguidedev.toainetworkgitgithub