DEV Community

Artur Kędzior
Artur Kędzior

Posted on • Updated on

Simple .NET Core and Cloud Firestore setup

... Cloud Firestore is a flexible, scalable database for mobile, web, and server development from Firebase and Google Cloud. Like Firebase Realtime Database, it keeps your data in sync across client apps through realtime listeners and offers offline support for mobile and web so you can build responsive apps that work regardless of network latency or Internet connectivity...

Sounds cool, so how do I set it up with .Net Core (dotnet)?
It's very simple.

I won't go into detail on how to setup firebase using console, there are plenty of tutorials out there. What we will need is the service account json file.

Let's start with the important part and create FirebaseProvider for generic use.

Firebase Provider

Ah yeah and make sure you have this nuget package installed:

<PackageReference Include="Google.Cloud.Firestore" Version="2.4.0" />
Enter fullscreen mode Exit fullscreen mode
public class FirestoreProvider
{
    private readonly FirestoreDb _fireStoreDb = null!;

    public FirestoreProvider(FirestoreDb fireStoreDb)
    {
        _fireStoreDb = fireStoreDb;
    }

    public async Task AddOrUpdate<T>(T entity, CancellationToken ct) where T : IFirebaseEntity
    {
        var document = _fireStoreDb.Collection(typeof(T).Name).Document(entity.Id);
        await document.SetAsync(entity, cancellationToken: ct);
    }

    public async Task<T> Get<T>(string id, CancellationToken ct) where T : IFirebaseEntity
    {
        var document = _fireStoreDb.Collection(typeof(T).Name).Document(id);
        var snapshot = await document.GetSnapshotAsync(ct);
        return snapshot.ConvertTo<T>();
    }

    public async Task<IReadOnlyCollection<T>> GetAll<T>(CancellationToken ct) where T : IFirebaseEntity
    {
        var collection = _fireStoreDb.Collection(typeof(T).Name);
        var snapshot = await collection.GetSnapshotAsync(ct);
        return snapshot.Documents.Select(x => x.ConvertTo<T>()).ToList();
    }

    public async Task<IReadOnlyCollection<T>> WhereEqualTo<T>(string fieldPath, object value, CancellationToken ct) where T : IFirebaseEntity
    {
        return await GetList<T>(_fireStoreDb.Collection(typeof(T).Name).WhereEqualTo(fieldPath, value), ct);
    }

    // just add here any method you need here WhereGreaterThan, WhereIn etc ...

    private static async Task<IReadOnlyCollection<T>> GetList<T>(Query query, CancellationToken ct) where T : IFirebaseEntity
    {
        var snapshot = await query.GetSnapshotAsync(ct);
        return snapshot.Documents.Select(x => x.ConvertTo<T>()).ToList();
    }
}
Enter fullscreen mode Exit fullscreen mode

Usage

Inject it to your whatever it's going to use it.

private readonly FirestoreProvider _firestoreProvider;

public SampleEndpoint(FirestoreProvider firestoreProvider)
{
    _firestoreProvider = firestoreProvider;
}
Enter fullscreen mode Exit fullscreen mode

... and here is how you would actually use it:

// Add or Update

var user = new User("Artur");
await _firestoreProvider.AddOrUpdate(user, cancellationToken);  

// Get by Id

var user = await _firestoreProvider.Get<User>("306f19a85136414b868ff7ba56c3f0a8", cancellationToken);

// Get all (use it wisely*)

var allUsers = await _firestoreProvider.GetAll<User>(cancellationToken);

// Query by first name (for example)

var users = await _firestoreProvider.WhereEqualTo<User>(nameof(Api.Data.User.FirstName), "Artur", ct);

Enter fullscreen mode Exit fullscreen mode

Data model

Now the boooooring part.

Our simplified data model:


[FirestoreData]
public class User : IFirebaseEntity
{
    [FirestoreProperty]
    public string Id { get; set; }

    [FirestoreProperty]
    public string FirstName { get; set; }

    public User()
    {
    }

    public User(string firstName)
    {
        Id = Guid.NewGuid().ToString("N");
        FirstName = firstName;
    }
}
Enter fullscreen mode Exit fullscreen mode

The interfaces that forces to create Id which would be used as document id. We will use string version of Guid for that purpose.

public interface IFirebaseEntity
{
    public string Id { get; set; }
}
Enter fullscreen mode Exit fullscreen mode

Provider registration

Register our provider in Startup.cs within ConfigureServices.

services.AddSingleton(_ => new FirestoreProvider(
    new FirestoreDbBuilder 
    { 
        ProjectId = firebaseSettings.ProjectId, 
        JsonCredentials = firebaseJson // <-- service account json file
    }.Build()
));

Enter fullscreen mode Exit fullscreen mode

Firebase service account json

If you want a quick and dirty way of loading one without json files floating around your solution you can just hard code them into a class:

public class FirebaseSettings
{
    [JsonPropertyName("project_id")]
    public string ProjectId => "that-rug-really-tied-the-room-together-72daa";

    [JsonPropertyName("private_key_id")]
    public string PrivateKeyId => "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx";

    // ... and so on
}

Enter fullscreen mode Exit fullscreen mode

In Startup.cs within ConfigureServices.

var firebaseJson = JsonSerializer.Serialize(new FirebaseSettings());

Enter fullscreen mode Exit fullscreen mode

... and you are done!

Image description

If you want to have plural names for your collections just modify FirestoreProvider so that collection gets s:

_fireStoreDb.Collection($"{typeof(T).Name}s")

Enter fullscreen mode Exit fullscreen mode

Discussion (0)