MongoDB IRepository db Connections - mongodb

This is what I have so far with regards to my IRepository for MongoDB and was wondering whether or not I'm on the right lines?
public abstract class Repository<TEntity> : IRepository<TEntity> {
private const string _connection = "mongodb://localhost:27017/?safe=true";
private MongoDatabase _db;
protected abstract string _collection{get;}
public Repository() {
this._db = MongoServer.Create(_connection).GetDatabase("Photos");
}
public IQueryable<TEntity> FindAll() {
return this._db.GetCollection<TEntity>(_collection).FindAll().AsQueryable();
}
}
This way I can create my PhotoRepository class that inherits from here and supplies the required _collection name.
I just want to make sure that I'm opening the connection to the db in the correct place and in the correct way.

Yes, this is fine. MongoServer.Create will return the same instance of MongoServer when passed the same connection string, so it is safe to call MongoServer.Create as many times as you want.

Related

How to use dynamic connection string in mongodbcontext in C#

I would like to connect to the database specified in the connection string. I have multiple connection sting for different client
public MongoContext(IConfiguration configuration)
{
_configuration = configuration;
// Every command will be stored and it'll be processed at SaveChanges
_commands = new List<Func<Task>>();
}
private void ConfigureMongo()
{
if (MongoClient != null)
{
return;
}
// Configure mongo (You can inject the config, just to simplify)
MongoClient = new MongoClient(_configuration["MongoSettings:Connection"]);
Database = MongoClient.GetDatabase(_configuration["MongoSettings:DatabaseName"]);
}
public IMongoCollection<T> GetCollection<T>(string name)
{
ConfigureMongo();
return Database.GetCollection<T>(name);
}
}
My Repository Class is
public abstract class BaseRepository<TEntity> : IRepository<TEntity> where TEntity : class
{
protected readonly IMongoContext Context;
protected IMongoCollection<TEntity> DbSet;
protected BaseRepository(IMongoContext context, int parentID)
{
Context = context;
DbSet = Context.GetCollection<TEntity>(typeof(TEntity).Name);
}
}
For each request, i want to set connection string dynamically based on UserID
Not sure I've got what you're asking, but if you need connecting to more than one cluster, you should create a separate mongo client for each separate cluster

How to add MongoDB database context into startup .netcore

I'm using MongoDB in my project, I have created a DB context like this:
public class PortfolioDbContext
{
public readonly IMongoCollection<PortfolioData> _portfolioCollection;
public PortfolioDbContext()
{
var client = new MongoClient("mongodb://localhost:27017");
_portfolioCollection = client.GetDatabase("portfolioServiceDb").GetCollection<PortfolioData>("Portfolios");
}
}
I was to inject it into my repository class:
public PortfolioDbContext _db;
public DataService(PortfolioDbContext _db)
{
_db = this._db;
}
but db returns null, I thought I need to register it in my startup:
services.AddSingleton<PortfolioDbContext>();
but I'm still getting null, any idea why?
The error is in the constructor of DataService. Your are assigning this._db (the variable of the class) to _db (the argument of the constructor), instead of the other way around. As the default of public PortfolioDbContext _db is null and you are never assigning a different value to it, it remains null.
The coding guidelines from Microsoft https://learn.microsoft.com/en-us/dotnet/csharp/fundamentals/coding-style/coding-conventions recommends using camel case with a _ prefix for private variables in a class and method parameters with camel case (without underline). This helps preventing issues like this.
Your DataService could look like this:
public class DataService
{
private PortfolioDbContext _db;
public DataService(PortfolioDbContext db)
{
_db = db;
}
// ... some more methods
}

Using Microsoft.AspNetCore.Identity.MongoDB for Multi Tenancy. How do we inject dynamic Tenant into MongoDbContext

Does anyone know how we can inject context into User Manager > MongoDB serStore at runtime in .net core 2.0.
We cannot do this at startup due to the context being dynamic but the UserStore is not accessible and UserManager has too many variables to new up, and it is wrong. Are there any solutions?
public class UserStore<TUser> :
IUserPasswordStore<TUser>,
IUserRoleStore<TUser>,
IUserLoginStore<TUser>,
IUserSecurityStampStore<TUser>,
IUserEmailStore<TUser>,
IUserClaimStore<TUser>,
IUserPhoneNumberStore<TUser>,
IUserTwoFactorStore<TUser>,
IUserLockoutStore<TUser>,
IQueryableUserStore<TUser>,
IUserAuthenticationTokenStore<TUser>
where TUser : IdentityUser
{
private readonly IMongoCollection<TUser> _Users;
//THIS IS WHERE WE WANT TO INJECT THE users AT RUNTIME
public UserStore(IMongoCollection<TUser> users)
{
_Users = users;
}
public virtual void Dispose()
{
// no need to dispose of anything, mongodb handles connection pooling automatically
}
public virtual async Task<IdentityResult> CreateAsync(TUser user, CancellationToken token)
{
await _Users.InsertOneAsync(user, cancellationToken: token);
return IdentityResult.Success;
}
unfortunately users is null at startup, and should be as the tenant has not been created at that point.
We have also been using the saaskit.Multitenancy and just can't find a solution.
Any help would be much appreciated.
Thanks
i think u need a generic repository to act as a wrapper for IMongoCollection then inject the repository inside controllers
public class Repository<T>
{
public IMongoCollection<T> Collection { get; private set; }
public Repository(IDbFactory dbFactory)
{
MongoClient client = new MongoClient("ur connection string");
this.Collection = client.GetDatabase("db").GetCollection<T>(typeof(T).Name);
}
public T Find(Expression<Func<T, bool>> filter)
{
return this.Collection.AsQueryable<T>().FirstOrDefault<T>(filter);
}
public async Task<T> FindAsync(Expression<Func<T, bool>> filter)
{
return await this.Collection.AsQueryable<T>().FirstOrDefaultAsync<T>(filter);
}
// here add more methods
}
then register the dependency as below inside Startup.cs
public void ConfigureServices(IServiceCollection services)
{
services.AddTransient(typeof(IRepository<>), typeof(Repository<>));
services.AddMvc();
}
finally inside controllers u inject the generic repository, also dont forget to Implement the IDisopsible in genereic repository
public class ProductController : Controller
{
private readonly IRepository<Product> _productRepository = null;
public ProductController(IRepository<Product> productRepository)
{
this._productRepository = productRepository;
}
}

Entity Framework Unity of Work and Repository pattern

I'm trying to implement UoW and Repository pattern, but I get error
An entity object cannot be referenced by multiple instances of IEntityChangeTracker.
I know that I get that error because I have two repositories which create two different DBContext, but I don't know why that happens.
Here is my code for UoW
public class UnitOfWorkRepositoryRepository : IUnitOfWorkRepository
{
private readonly IDatabaseFactory _databaseFactory;
private DatabaseContext _databaseContext;
public UnitOfWorkRepositoryRepository(IDatabaseFactory databaseFactory)
{
_databaseFactory = databaseFactory;
}
public DatabaseContext Database
{
get { return _databaseContext ?? (_databaseContext = _databaseFactory.GetDatabaseContext()); }
}
public void Save()
{
_databaseContext.Save();
}
}
And here sample Repository:
private static readonly DatabaseFactory DatabaseFactory = new DatabaseFactory();
private static readonly UnitOfWorkRepositoryRepository UnitOfWorkRepositoryRepository = new UnitOfWorkRepositoryRepository(DatabaseFactory);
public User GetUserById(int id)
{
return UnitOfWorkRepositoryRepository.Database.Users.SingleOrDefault(u => u.UserId.Equals(id));
}
What's wrong ? how should I implement UoW
P.S.
I'm not getting any errors in this repository, but other one was too long, this one serves just as sample.
Did you try this
http://www.asp.net/mvc/tutorials/getting-started-with-ef-using-mvc/implementing-the-repository-and-unit-of-work-patterns-in-an-asp-net-mvc-application
I think it is more descriptive, I have ever seen.
Have a look at this SO answer where I describe a way to decoulple Uow from Repository.

MongoDB C# Driver database.GetCollection and magic strings

Just getting into the NoSQL stuff so forgive me if this is a simple question. I am trying to somewhat implement a repository type pattern using a generic repository for the more common operations.
One thing that I have run into that is killing this idea is that in order to get the collection you plan to work with you have to pass a string value for the name of the collection.
var collection = database.GetCollection<Entity>("entities");
This means that I have to hard code my collection names or code up a dictionary somewhere to act as a lookup so that i can map the object type to a collection name.
How is everyone else handling this?
What you can do is "semi-hardcode." You can put the name of the collection in a class name and refere to it:
public class Entity {
public static readonly string Name = "entities";
}
var collection = database.GetCollection<Entity>(Entity.Name);
I wrote a class to manage DB transactions
First you need a base class for all entities:
public abstract class Entity
{
public ObjectId Id { set; get; }
}
then an static class to manage all:
public static class MongoDB
{
private static string connectionString = "mongodb://localhost";
public static string DatabaseName { get { return "test"; } }
private static MongoServer _server;
private static MongoDatabase _database;
public static MongoServer Server
{
get
{
if (_server == null)
{
var client = new MongoClient(connectionString);
_server = client.GetServer();
}
return _server;
}
}
public static MongoDatabase DB
{
get
{
if(_database == null)
_database = Server.GetDatabase(MongoDB.DatabaseName);
return _database;
}
}
public static MongoCollection<T> GetCollection<T>() where T : Entity
{
return DB.GetCollection<T>(typeof(T).FullName);
}
public static List<T> GetEntityList<T>() where T : Entity
{
var collection = MongoDB.DB.GetCollection<T>(typeof(T).FullName);
return collection.FindAll().ToList<T>();
}
public static void InsertEntity<T>(T entity) where T : Entity
{
GetCollection<T>().Save(entity);
}
}
then use it like this:
public class SomeEntity : Entity { public string Name {set;get;} }
MongoDB.InsertEntity<SomeEntity>(new SomeEntity(){ Name = "ashkan" });
List<SomeEntity> someEntities = MongoDB.GetEntityList<SomeEntity>();
I finally found an approach very usefull for me as all my mongo collections follow a camel case underscore naming convention, so I made a simple string extension to translate the POCO naming convention to my mongo convention.
private readonly IMongoDatabase _db;
public IMongoCollection<TCollection> GetCollection<TCollection>() =>
_db.GetCollection<TCollection>(typeof(TCollection).ToString().MongifyToCollection());
This method is inside a class made for handling mongo using dependency injection and it also wraps the default GetCollection to make it a bit more OO
public class MongoContext : IMongoContext
{
private readonly IMongoDatabase _db;
public MongoContext()
{
var connectionString = MongoUrl.Create(ConfigurationManager.ConnectionStrings["mongo"].ConnectionString);
var client = new MongoClient(connectionString);
_db = client.GetDatabase(connectionString.DatabaseName);
RegisterConventions();
}
public IMongoCollection<TCollection> GetCollection<TCollection>() =>
_db.GetCollection<TCollection>(typeof(TCollection).Name.MongifyToCollection());
...
And the extension:
// It may require some improvements, but enough simple for my needs at the moment
public static string MongifyToCollection(this string source)
{
var result = source.Mongify().Pluralize(); //simple extension to replace upper letters to lower, etc
return result;
}