简体   繁体   中英

Injecting app settings via DI based on generic type in .NET Core 3.1

I'm trying to be cheeky and create a base service for my MongoDB client. Inside my DI container I've injected database connection settings from the application.json (as per Microsoft's MongoDB tutorial ).

What I can't figure out is the best way to access one of these setting properties based on a generic type passed into the constructor for the base service.

For example, the MongoDB collection name for Type Player is named is Players . One idea I had was to do typeof(T) instead of putting the key/value in the settings, but that cements the collection name to the type name.

Here is the database settings object being injected:

    public class GameDatabaseSettings : IGameDatabaseSettings
    {
        public string PlayersCollectionName { get; set; }
        public string ConnectionString { get; set; }
        public string DatabaseName { get; set; }
        public string LobbiesCollectionName { get; set; }
    }

The application json:

"GameDatabaseSettings": {
    "PlayersCollectionName": "Players",
    "LobbiesCollectionName": "Lobbies",
    "ConnectionString": "mongodb+srv://bugbeeb:*******@csharptest-yzm6y.mongodb.net/test?retryWrites=true&w=majority",
    "DatabaseName": "DiceGameDB"
  },

And here is how the base service operates:

    public interface IGameItem
    {
        public string Id { get; set; }
    }

    public class BaseService<T> where T:IGameItem
    {
        private readonly IMongoCollection<T> _collection;
        public BaseService(IGameDatabaseSettings settings)
        {
            var client = new MongoClient(settings.ConnectionString);
            var db = client.GetDatabase(settings.DatabaseName);
            _collection = db.GetCollection<T>(settings.CollectionName); //<-- How to make this T specific??
        }
        public virtual async Task<T> GetAsync(string id)
        {
            var result = await _collection.FindAsync(item => item.Id == id);
            return result.FirstOrDefault();
        }
        public virtual async Task DeleteAsync(string id)
        {
            await _collection.FindOneAndDeleteAsync(item => item.Id == id);
        }

    }

You were on the right track with typeof(T) . The only step you were missing is that you can bind a list of key/value pairs in your appSettings.json to a Dictionary<string, string> in your GameDatabaseSettings .

That will let you look up configuration values based on a string name, thus allowing you to configure the mappings between any generic Type and an associated MongoDB collection name via your appSettings.json .

So given the following appSettings.json :

"GameDatabaseSettings": {
    "CollectionNames": {
      "Players": "Players",
      "Lobbies": "Lobbies"
    },
    "ConnectionString": "mongodb+srv://bugbeeb:*******@csharptest-yzm6y.mongodb.net/test?retryWrites=true&w=majority",
    "DatabaseName": "DiceGameDB"
  }
}

You can bind to a settings object like:

public class GameDatabaseSettings : IGameDatabaseSettings
{
    public Dictionary<string, string> CollectionNames { get; set; }
    public string ConnectionString { get; set; }
    public string DatabaseName { get; set; }
}

And now, within your service, you should be able to do something like:

_collection = db.GetCollection<T>(settings.CollectionNames[typeof(T).Name]);

Of course, you shouldn't assume that the correct mapping will be configured, so you'll likely want to write more defensive code, similar to:

_collection = db.GetCollection<T>(
  settings.CollectionNames.TryGetValue(
    typeof(T).Name,
    out var collectionName
  )? 
    collectionName :
    throw new ConfigurationError(
      $"The ‘{typeof(T).Name}’ mapping is not configured as part of {nameof(GameDatabaseSettings)}."
    )
);

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM