Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Using generics for resolving a collection name from a string

I have some collections in a mongo DB: autocomplete.brands and autocomplete.makes

Each of these collections have items based on the same pattern that have a Name property:

public class AutocompleteEntity
{
    [BsonId]
    [BsonRepresentation(BsonType.ObjectId)]
    public string Id { get; set; } = ObjectId.GenerateNewId().ToString();

    [BsonElement("Name")]
    public string Name { get; set; }

    [BsonElement("Culture")]
    public string Culture { get; set; }

    [BsonElement("Key")]
    public string Key { get; set; }
}

I would like to use generics for searching in each of these collections, here is what I have.

The high-level service using repositories:

public class AutocompleteHandler : IAutocompleteHandler
{
    private readonly IAutocompleteRepository<Brands> _brandsRepository;
    private readonly IAutocompleteRepository<Makes> _makesRepository;

    public AutocompleteHandler(
        IAutocompleteRepository<Brands> brandsRepository,
        IAutocompleteRepository<Makes> makesRepository
        )
    {
        _brandsRepository = brandsRepository;
        _makesRepository = makesRepository;
    }

    public async Task<ICollection<string>> SearchForBrand(string name, string culture)
    {
        return await _brandsRepository.Search(name, culture);
    }

    public async Task<ICollection<string>> SearchForMake(string name, string culture)
    {
        return await _makesRepository.Search(name, culture);
    }
}

For this to work I defined a class for each repository type:

public interface IAutocompleteCollection
{
}

public class Brands : IAutocompleteCollection
{
}

public class Makes : IAutocompleteCollection
{
}

and the generic repository:

public class AutocompleteRepository<T> : IAutocompleteRepository<T> where T : IAutocompleteCollection
{
    private readonly IMongoCollection<AutocompleteEntity> _collection;

    public AutocompleteRepository(IMongoTenantResolver mongoResolver)
    {
        var name = typeof(T).Name;
        var collectionName = $"autocomplete.{char.ToLowerInvariant(name[0]) + name.Substring(1)}";

        _collection = mongoResolver
            .GetMongoDbInstance()
            .GetCollection<AutocompleteEntity>(collectionName);
    }

    public async Task<List<string>> Search(string name, string culture)
    {
        var filter = Builders<AutocompleteEntity>.Filter.Regex("Name", new BsonRegularExpression(name, "i"));
        filter &= Builders<AutocompleteEntity>.Filter.Eq("Culture", culture);
        var data = await _collection.FindAsync(filter);
        var items = await data.ToListAsync();
        return items.Select(i => i.Name).ToList();
    }
}

My question

I am using the empty classes Brands and Makes in order to be able to resolve the collection by name in the generic repository:

var name = typeof(T).Name;
var collectionName = $"autocomplete.{char.ToLowerInvariant(name[0]) + name.Substring(1)}";

Is there a more elegant way to use generics without relying on empty classes/interface or is that the best way to use generics in my case?

Update1 :

After @RobertHarvey 's answer, I am adding this information. Generics usage is useful to me because I am using Dependency Injection and thus I can have this one-liner in Startup.cs:

services.AddTransient(typeof(IAutocompleteRepository<>), typeof(AutocompleteRepository<>));

I can then add many other autocomplete types without the need to register them individually in the Startup. Also because of DI usage, I am not sure how I can inject an enum value into the constructor.

like image 790
Bruno Avatar asked Feb 16 '26 08:02

Bruno


1 Answers

public enum CollectionName
{
    Brands,
    Makes
}

// Constructor
public AutoCompleteRepository(IMongoTenantResolver resolver, CollectionName name)
{
    // Name of collection is name.ToString().  Preserves static type checking,
    // avoids empty classes and interfaces.
}
like image 191
Robert Harvey Avatar answered Feb 18 '26 22:02

Robert Harvey



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!