How to create an API with severals DB connections - asp.net-core

I need to create an API in .NET to connect and verify some data in several database engines (MySQl, PostgreSQL, SQL Server).
I can make one connection, but I don't understand how to make more than one.
Here's my code:
Startup.cs
public void ConfigureServices(IServiceCollection services)
{
#region MSSqlServer
var connection = Configuration.GetConnectionString("SQLSRV");
services
.AddDbContext<SQLSRVDBContext>
(options => options.UseSqlServer(connection));
services
.AddTransient<IService, Service>();
#endregion
services.AddMvc();
}
appsettings.json
"ConnectionStrings": {
"SQLSRV": "Server=localhost;Database= dbName;User Id=dbUser;Password=dbPassword;MultipleActiveResultSets=true",
},
Interface
namespace VerificaUsuarios.Interfaces
{
public interface IService
{
bool GetUsuarioSG(string userName, string password);
}
}
Implementation
namespace VerificaUsuarios.Services
{
using VerificaUsuarios.Interfaces;
using VerificaUsuarios.Models;
using VerificaUsuarios.Persistence;
using System.Linq;
using global::ADWS;
public class Service : IService
{
private readonly SQLSRVDBContext _sQLSRVDBContext;
public Service(SQLSRVDBContext sQLSRVDBContext)
{
_sQLSRVDBContext = sQLSRVDBContext;
}
public bool GetUsuarioSG(string userName, string password)
{
var result = new UsuariosSG();
var activeDirectory = new AD_WSClient();
try
{
bool isUsuario = activeDirectory.LoginAsync(userName, password).Result;
if(isUsuario)
{
try
{
result = _sQLSRVDBContext
.Usuarios
.Where(u => u.UsrLogin.Trim() == userName.Trim())
.First();
}
catch (System.Exception ex)
{
return false;
}
return true;
}
else
{
return false;
}
}
catch(System.Exception excep)
{
return false;
}
}
}
}
And the db context
namespace VerificaUsuarios.Persistence
{
using Microsoft.EntityFrameworkCore;
using VerificaUsuarios.Models;
public partial class SQLSRVDBContext : DbContext
{
public SQLSRVDBContext()
{
}
public virtual DbSet<UsuariosSG> Usuarios{ get; set; }
public SQLSRVDBContext(DbContextOptions<SQLSRVDBContext> options)
: base(options)
{ }
}
}

example of connection to different motors with validation against active directory
1) install the different EF Core Database Providers in VS
Entity Framework Core uses a provider model to access many different databases. EF Core includes providers as NuGet packages which you need to install.
The following lists database providers and NuGet packages for EF Core (NuGet package).
SQL Server Microsoft.EntityFrameworkCore.SqlServer
MySQL MySql.Data.EntityFrameworkCore
PostgreSQL Npgsql.EntityFrameworkCore.PostgreSQL
2)Perform the Scaffold-DbContext to the bd and tables that you want to use in the different engines.
PostgreSQL
Scaffold-DbContext "Host=myserver;Database=mydatabase;Username=myuser;Password=mypassword" Npgsql.EntityFrameworkCore.PostgreSQL -o Models -Table MyTablePSQL
MySql
Scaffold-DbContext "server=myserver;port=3306;user=myuser;password=mypass;database=mydb" MySql.Data.EntityFrameworkCore -OutputDir Models -f -Table MyTableMySQL
SqlServer
Scaffold-DbContext "Server=myserver;Database=mydb;User Id=myuser;Password=mypassword;MultipleActiveResultSets=true;" Microsoft.EntityFrameworkCore.SqlServer -OutputDir Models -Table MyTableSQL
3)add the different connection string in the appsettings.json
"ConnectionStrings": {
"SQLSRV": "Server=myserver;Database= mydb;User Id=myuser;Password=myPassword;MultipleActiveResultSets=true",
"MySql": "server=myserver;user id=myuser;password=mypassword;port=3306;database=mydb;",
"PSQL": "Host=myserver;Database=mydb;Username=myuser;Password=mypassword"
},
4) modify the DbContext generated by the Scaffold-DbContext
SQLSRVDBContext
namespace MyProject.Persistence
{
using Microsoft.EntityFrameworkCore;
using MyProject.Models;
public partial class SQLSRVDBContext : DbContext
{
public SQLSRVDBContext()
{
}
public virtual DbSet<MyTableSQL> Users{ get; set; }
public SQLSRVDBContext(DbContextOptions<SQLSRVDBContext> options)
: base(options)
{ }
}
}
MySQLDBContext
namespace MyProject.Persistence
{
using Microsoft.EntityFrameworkCore;
using MyProject.Models;
public partial class MySQLDBContext : DbContext
{
public MySQLDBContext()
{
}
public virtual DbSet<MyTableMySQL> Users { get; set; }
public MySQLDBContext(DbContextOptions<MySQLDBContext> options)
: base(options)
{ }
}
}
PostgreSQL
namespace MyProject.Models
{
using Microsoft.EntityFrameworkCore;
public partial class PostgreSQLDBContext : DbContext
{
public PostgreSQLDBContext()
{
}
public virtual DbSet<MyTablePSQL> Users { get; set; }
public PostgreSQLDBContext(DbContextOptions<PostgreSQLDBContext> options)
: base(options)
{
}
}
}
5)create Interfaces
SQLSRV
namespace MyProject.Interfaces
{
public interface IService
{
bool GetUserSQLSRV(string userName, string password);
}
}
MySQL
namespace MyProject.Interfaces
{
public interface IServiceMySQL
{
bool GetUserMySQL(string userName, string password);
}
}
PostgreSQL
namespace MyProject.Interfaces
{
public interface IServicePSQL
{
bool GetUserPSQL(string userName, string password);
}
}
6)create the Services
SQLSRV(SQLSRVDBContext)
namespace MyProject.Services
{
using MyProject.Interfaces;
using MyProject.Models;
using MyProject.Persistence;
using System.Linq;
using global::ADWS;
public class Service : IService
{
private readonly SQLSRVDBContext _sQLSRVDBContext;
public Service(SQLSRVDBContext sQLSRVDBContext)
{
_sQLSRVDBContext = sQLSRVDBContext;
}
public bool GetUserSQLSRV(string userName, string password)
{
var result = new MyTableSQL();
var activeDirectory = new AD_WSClient();
try
{
bool isUser = activeDirectory.LoginAsync(userName, password).Result;
if(isUser)
{
try
{
result = _sQLSRVDBContext
.Users
.Where(u => u.UsrLogin.Trim() == userName.Trim())
.First();
}
catch (System.Exception ex)
{
return false;
}
return true;
}
else
{
return false;
}
}
catch(System.Exception excep)
{
return false;
}
}
}
}
MySQL(MySQLDBContext)
namespace MyProject.Services
{
using MyProject.Interfaces;
using MyProject.Models;
using MyProject.Persistence;
using System.Linq;
using global::ADWS;
public class ServiceMySQL : IServiceMySQL
{
private readonly MySQLDBContext _mySQLDBContext;
public ServiceMySQL(MySQLDBContext mySQLDBContext)
{
_mySQLDBContext = mySQLDBContext;
}
public bool GetUserMySQL(string userName, string password)
{
var result = new MyTableMySQL();
var activeDirectory = new AD_WSClient();
try
{
bool isUser = activeDirectory.LoginAsync(userName, password).Result;
if(isUser)
{
try
{
result = _mySQLDBContext
.Users
.Where(u => u.UsrLogin.Trim() == userName.Trim())
.First();
}
catch (System.Exception ex)
{
return false;
}
return true;
}
else
{
return false;
}
}
catch(System.Exception excep)
{
return false;
}
}
}
}
PostgreSQL(PostgreSQLDBContext)
namespace MyProject.Services
{
using MyProject.Interfaces;
using MyProject.Models;
using MyProject.Persistence;
using System.Linq;
using global::ADWS;
public class ServicePSQL : IServicePSQL
{
private readonly PostgreSQLDBContext _postgreSQLDBContext;
public ServicePSQL(PostgreSQLDBContext postgreSQLDBContext)
{
_postgreSQLDBContext = postgreSQLDBContext;
}
public bool GetUserPSQL(string userName, string password)
{
var result = new MyTablePSQL();
var activeDirectory = new AD_WSClient();
try
{
bool isUser = activeDirectory.LoginAsync(userName, password).Result;
if(isUser)
{
try
{
result = _postgreSQLDBContext
.Users
.Where(u => u.UsrLogin.Trim() == userName.Trim())
.First();
}
catch (System.Exception ex)
{
return false;
}
return true;
}
else
{
return false;
}
}
catch(System.Exception excep)
{
return false;
}
}
}
}
7) configure the different services in Startup.cs
public void ConfigureServices(IServiceCollection services)
{
#region SQLSRV
var connection = Configuration.GetConnectionString("SQLSRV");
services
.AddDbContext<SQLSRVDBContext>
(options => options.UseSqlServer(connection));
services
.AddTransient<IService, Service>();
#endregion
#region MySql
var connectionMySql = Configuration.GetConnectionString("MySQL");
services
.AddDbContext<MySQLDBContext>
(options => options.UseMySQL(connectionMySql));
services
.AddTransient<IServiceMySQL, ServiceMySQL>();
#endregion
#region PostgreSQL
var connectionPSQL = Configuration.GetConnectionString("PSQL");
services
.AddDbContext<PostgreSQLDBContext>
(options => options.UseNpgsql(connectionPSQL));
services.AddTransient<IServicePSQL, ServicePSQL>();
#endregion
services.AddMvc();
}
8)creation of the different Controller
SQLSRV
namespace MyProject.Controllers
{
using Microsoft.AspNetCore.Mvc;
using MyProject.Interfaces;
[Route("api/GET/[controller]")]
public class UserSQLSRVController : Controller
{
private readonly IService _userSQLSRVService;
public UserSQLSRVController(IService userSQLSRVService)
{
_userSQLSRVService = userSQLSRVService;
}
[HttpGet]
public IActionResult GetUserSQLSRV(string userName, string password)
{
return Ok(
_userSQLSRVService.GetUserSQLSRV(userName, password));
}
}
}
MySQL
namespace MyProject.Controllers
{
using Microsoft.AspNetCore.Mvc;
using MyProject.Interfaces;
[Route("api/GET/[controller]")]
public class UserMySqlController : Controller
{
private readonly IServiceMySQL _userMySqlService;
public UserMySqlController(IServiceMySQL userMySqlService)
{
_userMySqlService = userMySqlService;
}
[HttpGet]
public IActionResult GetUserMySQL(string userName, string password)
{
return Ok(
_userMySqlService.GetUserMySQL(userName, password));
}
}
}
PSQL
namespace MyProject.Controllers
{
using Microsoft.AspNetCore.Mvc;
using MyProject.Interfaces;
[Route("api/GET/[controller]")]
public class UserPSQLController : Controller
{
private readonly IServicePSQL _userPSQLService;
public UserPSQLController(IServicePSQL userPSQLService)
{
_userPSQLService = userPSQLService;
}
[HttpGet]
public IActionResult GetUserPSQL(string userName, string password)
{
return Ok(
_userPSQLService.GetUserPSQL(userName, password));
}
}
}

Related

Unable to resolve service for type 'BusinessServices.CustomerBusinessService' while attempting to activate 'Repository.CustomerRepository'

Issue Message:
System.AggregateException: 'Some services are not able to be constructed (Error while validating the service descriptor 'ServiceType: Repository.ICustomerRepository Lifetime: Scoped ImplementationType: Repository.CustomerRepository': Unable to resolve service for type 'BusinessServices.CustomerBusinessService' while attempting to activate 'Repository.CustomerRepository'.)'
API Controller:
namespace WebAPIforAzureSQL.Controllers
{
[Route("api/[controller]")]
[ApiController]
[ApiVersion("1.0")]
[ApiVersion("2.0")]
public class AzureController : ControllerBase
{
private readonly ICustomerRepository customerRepository;
private readonly ICustomerBusinessService customerBusinessService;
public AzureController(ICustomerRepository _customerRepository, ICustomerBusinessService _customerBusinessService)
{
customerRepository = _customerRepository;
customerBusinessService = _customerBusinessService;
}
[MapToApiVersion("1.0")]
[HttpGet]
public IEnumerable<Customers> Customers()
{
return customerRepository.GetAllCustomers();
}
[MapToApiVersion("2.0")]
[HttpGet()]
public Customers OneCustomer([FromHeader] int customerID)
{
return customerRepository.GetCustomer(customerID);
}
[HttpGet()]
[ApiVersion("1.0")]
[ApiVersion("2.0")]
public bool NewCustomer([FromHeader] Customers c)
{
return customerRepository.AddCustomer(c);
}
}
}
Program.cs:
using Microsoft.AspNetCore.Mvc.Versioning;
using Microsoft.AspNetCore.Mvc;
using WebAPIforAzureSQL;
using Microsoft.EntityFrameworkCore;
using Repository;
using Microsoft.AspNetCore.Mvc.ApiExplorer;
using Contracts;
using BusinessServices;
var builder = WebApplication.CreateBuilder(args);
// Add services to the container.
builder.Services.AddControllers();
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddControllers().AddXmlSerializerFormatters();
builder.Services.AddSwaggerGen();
builder.Services.ConfigureOptions<ConfigureSwaggerOptions>();
builder.Services.AddApiVersioning(opt =>
{
opt.DefaultApiVersion = new ApiVersion(1, 0);
opt.AssumeDefaultVersionWhenUnspecified = true;
opt.ReportApiVersions = true;
opt.ApiVersionReader = ApiVersionReader.Combine(
new UrlSegmentApiVersionReader(),
new HeaderApiVersionReader("api-version")
);
});
// Add ApiExplorer to discover versions
builder.Services.AddVersionedApiExplorer(setup =>
{
setup.GroupNameFormat = "'v'VVV";
setup.SubstituteApiVersionInUrl = true;
});
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddDbContext<AmounDB001DbContext>(option =>
{
option.UseSqlServer(builder.Configuration.GetConnectionString("DB001"));
});
//adding Services
builder.Services.AddScoped<ICustomerRepository, CustomerRepository>();
builder.Services.AddScoped<ICustomerBusinessService, CustomerBusinessService>();
var app = builder.Build();
var apiVersionDescriptionProvider =
app.Services.GetRequiredService<IApiVersionDescriptionProvider>();
// Configure the HTTP request pipeline.
if (app.Environment.IsDevelopment())
{
app.UseSwagger();
app.UseSwaggerUI(
options =>
{
foreach (var description in apiVersionDescriptionProvider.ApiVersionDescriptions)
{
options.SwaggerEndpoint($"/swagger/{description.GroupName}/swagger.json",
description.GroupName.ToUpperInvariant());
}
}
);
}
app.UseHttpsRedirection();
app.UseAuthorization();
app.MapControllers();
app.Run();
Repository Class
namespace Repository
{
public class CustomerRepository : ICustomerRepository
{
private readonly AmounDB001DbContext amounDB001DbContext;
private readonly CustomerBusinessService customerBusinessService;
public CustomerRepository(
AmounDB001DbContext _amounDB001DbContext,
CustomerBusinessService _customerBusinessService
)
{
this.amounDB001DbContext = _amounDB001DbContext;
customerBusinessService= _customerBusinessService;
}
public bool AddCustomer(Customers c)
{
if (c != null && customerBusinessService.CheckAllFields(c))
{
amounDB001DbContext.Add(c);
amounDB001DbContext.SaveChangesAsync();
return true;
}
return false;
}
public bool RemoveCustomer(Customers c)
{
if (c != null && customerBusinessService.CheckAllFields(c))
{
amounDB001DbContext.Customers.Remove(c);
amounDB001DbContext.SaveChanges();
return true;
}
return false;
}
public bool UpdateCustomer(Customers c)
{
if (c != null && customerBusinessService.CheckAllFields(c))
{
amounDB001DbContext.Customers.Update(c);
amounDB001DbContext.SaveChanges();
return true;
}
return false;
}
public List<Customers> GetAllCustomers()
{
return amounDB001DbContext.Customers.ToList();
}
public Customers GetCustomer(int customerID)
{
if (customerID == 0)
{
return null;
}
return amounDB001DbContext.Customers.FirstOrDefault(c => c.CustomerID == customerID);
}
}
}
I'm not sure what I'm doing wrong. Any ideas?
I think you should change the constructor of CustomerRepository, replacing the parameter type CustomerBusinessService with the interface you registered as a service ICustomerBusinessService.
public class CustomerRepository : ICustomerRepository
{
private readonly AmounDB001DbContext amounDB001DbContext;
private readonly ICustomerBusinessService customerBusinessService;
public CustomerRepository(
AmounDB001DbContext _amounDB001DbContext,
ICustomerBusinessService _customerBusinessService
)
{
this.amounDB001DbContext = _amounDB001DbContext;
customerBusinessService= _customerBusinessService;
}
...

What is the best way to registering multiple services?

I already know, to register customized managers same as 'Identity.UserManager', we have to register them.
but there is any way to prevent register multi-managers?
Thanks to #GOF, the best way is using 'Decorator Design Pattern':
a) Creating main class Managers/DecoratorManager.cs:
using Microsoft.AspNetCore.Identity;
namespace MyProject_WebMVC.Managers
{
public interface IDecoratorManager { }
public class DecoratorManager : IDecoratorManager
{
protected Data.ApplicationDbContext DB { get; }
protected UserManager<Models.User> UserManager { get; }
public DecoratorManager(Data.ApplicationDbContext dbContext,
UserManager<Models.User> userManager)
{
DB = dbContext;
UserManager = userManager;
}
protected DecoratorManager(DecoratorManager manager)
{
DB = manager.DB;
UserManager = manager.UserManager;
}
}
}
b) Register it by Startup.cs:
public void ConfigureServices(IServiceCollection services)
{
...
//! Custom storage manager
services.AddScoped<Managers.IDecoratorManager, Managers.DecoratorManager>();
...
}
c) Inject decorator on Views/_VeiwImports.cs:
#inject Managers.IDecoratorManager Manage
d) Define your manager like this, Ex. ProfileManager.cs:
namespace MyProject_WebMVC.Managers
{
public class ProfileManager : DecoratorManager
{
public ProfileManager(IDecoratorManager manager) : base(manager as DecoratorManager) { }
public bool IsThisWorking() => true;
}
}
e) Define another manager, Ex. StorageManager.cs:
namespace MyProject_WebMVC.Managers
{
public class StorageManager : DecoratorManager
{
public StorageManager(IDecoratorManager manager) : base(manager as DecoratorManager) { }
public string GetWelcomeMessage() => "Hello Message";
}
}
f) And now you can use it what ever you want, Ex. MyView.cshtml
#{
var profileManager = new Managers.ProfileManager(Manager);
var storageManager = new Managers.StorageManager(Manager);
}
<h1>#storageManager.GetWelcomeMessage()</h1>
#if(profileManager.IsThisWorking()) { <h2>This is grate :)</h2> }

How to keep user logged in after browser is closed

Every time I close the browser I need to log in again into this app. It is developed in .NET Core 2.0. I'm trying to let it logged in, like every other regular site.
I checked this post that may be useful, but since the code is quite different from this application I decided to create this post.
This is my security code:
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Security.Claims;
using System.Security.Principal;
using System.Text;
namespace Petito.Common
{
public interface IActivityContext
{
string ActivityID { get; }
IPrincipal User { get; }
}
[JsonObject(MemberSerialization = MemberSerialization.Fields)]
public class ActivityIdentity : IIdentity
{
private string _name = null;
[JsonIgnore()]
private bool _isAuthenticated = false;
[JsonIgnore()]
private string _authenticationType = "";
public ActivityIdentity()
{
}
public ActivityIdentity(string name) : this(name, false, "")
{
}
internal ActivityIdentity(string name, bool isAuthenticated, string authenticationType)
{
this._name = name;
this._isAuthenticated = isAuthenticated;
this._authenticationType = authenticationType;
}
public string Name { get => _name; }
public bool IsAuthenticated { get => _isAuthenticated; }
public string AuthenticationType { get => _authenticationType; }
public static ActivityIdentity Unathenticated => new ActivityIdentity();
}
[JsonObject(MemberSerialization = MemberSerialization.Fields)]
public class ActivityPrincipal : IPrincipal
{
private ActivityIdentity _activityIdentity = null;
private string[] _roles = null;
public ActivityPrincipal() : this(ActivityIdentity.Unathenticated, null)
{
}
public ActivityPrincipal(ActivityIdentity activityIdentity, params string[] roles)
{
_activityIdentity = activityIdentity;
_roles = roles;
}
public ActivityIdentity Identity => _activityIdentity;
IIdentity IPrincipal.Identity => _activityIdentity;
public bool IsInRole(string role)
{
if (_roles != null && _roles.Length > 0)
{
return _roles.Contains(role);
}
return false;
}
}
[JsonObject(MemberSerialization = MemberSerialization.Fields)]
public class ActivityContext : IDisposable, IActivityContext
{
private string _activityID = Guid.NewGuid().ToString();
private DateTime _startDate = DateTime.UtcNow;
private DateTime? _endDate = null;
private ActivityPrincipal _activityPrincipal = null;
public ActivityContext() : this(null)
{
}
public ActivityContext(IPrincipal principal)
{
_activityPrincipal = Convert(principal);
}
private ActivityPrincipal Convert(IPrincipal principal)
{
if (principal == null)
{
return new ActivityPrincipal();
}
var activityPrincipal = principal as ActivityPrincipal;
if (activityPrincipal != null)
{
return activityPrincipal;
}
var claimsPrincipal = principal as ClaimsPrincipal;
if (claimsPrincipal != null)
{
var roles = claimsPrincipal.Claims.Select(x => x.Value);
var p = new ActivityPrincipal(
new ActivityIdentity(claimsPrincipal.Identity.Name, claimsPrincipal.Identity.IsAuthenticated, claimsPrincipal.Identity.AuthenticationType)
, roles.ToArray()
);
return p;
}
throw new NotSupportedException($"Converting {principal.GetType()} not supported");
}
public void Dispose()
{
if (!_endDate.HasValue)
{
_endDate = DateTime.UtcNow;
}
}
public string ActivityID { get => _activityID; }
public DateTime StartDate { get => _startDate; }
public DateTime? EndDate { get => _endDate; }
public IPrincipal User
{
get
{
return _activityPrincipal;
}
}
}
}
Of course, I'll still try to figure out what's wrong with the code. Please if you need another part of the code other from that I posted let me know.
Thanks!

How could i manage multiple database in linq2db

I want to manage multiple database connection in my application.I am using ASP.NET Core & linq2db. For my first database connection i have create below configuration and its working fine.
public class DatabaseXSettings : ILinqToDBSettings
{
public IEnumerable<IDataProviderSettings> DataProviders
{
get { yield break; }
}
public string DefaultConfiguration => "Oracle.Managed";
public string DefaultDataProvider => "Oracle.Managed";
public string ConnectionString { get; set; }
public DatabaseXSettings(string connectionString)
{
ConnectionString = connectionString;
}
public IEnumerable<IConnectionStringSettings> ConnectionStrings
{
get
{
yield return
new ConnectionStringSettings
{
Name = "DatabaseX",
ProviderName = "Oracle.Managed",
ConnectionString = ConnectionString
};
}
}
}
public static class DatabaseXStartup
{
private static bool _started;
public static void Init(string connectionString)
{
if (!_started)
{
DataConnection.DefaultSettings = new DatabaseXSettings(connectionString);
_started = true;
}
}
}
public class DatabaseX : DataConnection
{
public DatabaseX() : base("DatabaseX") { }
}
For My Second Database
For my second database connection i create a similar configuration like this. But this not working.
public class DatabaseYSettings : ILinqToDBSettings
{
.......
}
public static class DatabaseYStartup
{
.......
}
public class DatabaseY : DataConnection
{
public DatabaseY() : base("DatabaseY") { }
}
My appsettings.json
{
"Logging": {
"LogLevel": {
"Default": "Warning"
}
},
"AllowedHosts": "*",
"ConnectionStrings": {
"DatabaseX": "Data Source=..........",
"DatabaseY": "Data Source=.........."
}
}
LinqToDB.LinqToDBException: 'Configuration 'DatabaseY' is not defined.'
Is there any other way to do that.
Edit: added example
You don't need two ILinqToDBSettings instances. You should move second database settings to first settings class, so ConnectionStrings will return both connection strings.
public class DatabaseSettings : ILinqToDBSettings
{
public IEnumerable<IDataProviderSettings> DataProviders
{
get { yield break; }
}
public string DefaultConfiguration => "Oracle.Managed";
public string DefaultDataProvider => "Oracle.Managed";
private readonly IConnectionStringSettings[] _connectionStrings;
public DatabaseSettings(IConnectionStringSettings[] connectionStrings)
{
_connectionStrings = connectionStrings;
}
public IEnumerable<IConnectionStringSettings> ConnectionStrings => _connectionStrings;
}
public static class DatabaseSetup
{
// just call it on application startup
public static void Init()
{
// create connectionStrings collection with both connection strings from appsettings.json
DataConnection.DefaultSettings = new DatabaseSettings(connectionStrings);
}
}

using RavenDB with ServiceStack

I read this post by Phillip Haydon about how to use NHibernate/RavenDB with ServiceStack.
I don't see the point about getting the IDocumentStore and open new session every time i need something from the db like this:
public class FooService : ServiceBase<Foo>
{
public IDocumentStore RavenStore{ get; set; }
protected override object Run(ProductFind request)
{
using (var session = RavenStore.OpenSession())
{
// Do Something...
return new FooResponse{/*Object init*/};
}
}
}
Why cant i just use one session per request and when the request is ended, commit the changes or roll them back according to the response status?
If my approach is fine, than how can i implement it?
here is my attempt:
I created this class:
public class RavenSession : IRavenSession
{
#region Data Members
private readonly IDocumentStore _store;
private IDocumentSession _innerSession;
#endregion
#region Properties
public IDocumentSession InnerSession
{
get { return _innerSession ?? (_innerSession = _store.OpenSession()); }
}
#endregion
#region Ctor
public RavenSession(IDocumentStore store)
{
_store = store;
}
#endregion
#region Public Methods
public void Commit()
{
if (_innerSession != null)
{
try
{
InnerSession.SaveChanges();
}
finally
{
InnerSession.Dispose();
}
}
}
public void Rollback()
{
if (_innerSession != null)
{
InnerSession.Dispose();
}
}
#endregion
#region IDocumentSession Delegation
public ISyncAdvancedSessionOperation Advanced
{
get { return InnerSession.Advanced; }
}
public void Delete<T>(T entity)
{
InnerSession.Delete(entity);
}
public ILoaderWithInclude<object> Include(string path)
{
return InnerSession.Include(path);
}
public ILoaderWithInclude<T> Include<T, TInclude>(Expression<Func<T, object>> path)
{
return InnerSession.Include<T, TInclude>(path);
}
public ILoaderWithInclude<T> Include<T>(Expression<Func<T, object>> path)
{
return InnerSession.Include(path);
}
public T Load<T>(string id)
{
return InnerSession.Load<T>(id);
}
public T[] Load<T>(params string[] ids)
{
return InnerSession.Load<T>(ids);
}
public T Load<T>(ValueType id)
{
return InnerSession.Load<T>(id);
}
public T[] Load<T>(IEnumerable<string> ids)
{
return InnerSession.Load<T>(ids);
}
public IRavenQueryable<T> Query<T, TIndexCreator>() where TIndexCreator : AbstractIndexCreationTask, new()
{
return InnerSession.Query<T, TIndexCreator>();
}
public IRavenQueryable<T> Query<T>()
{
return InnerSession.Query<T>();
}
public IRavenQueryable<T> Query<T>(string indexName)
{
return InnerSession.Query<T>(indexName);
}
public void Store(dynamic entity, string id)
{
InnerSession.Store(entity, id);
}
public void Store(object entity, Guid etag, string id)
{
InnerSession.Store(entity, etag, id);
}
public void Store(object entity, Guid etag)
{
InnerSession.Store(entity, etag);
}
public void Store(dynamic entity)
{
InnerSession.Store(entity);
}
#endregion
}
And now my service looks like this:
public class FooService : ServiceBase<Foo>
{
public IRavenSession RavenSession { get; set; }
protected override object Run(ProductFind request)
{
// Do Something with RavenSession...
return new FooResponse {/*Object init*/};
}
}
but i still need to find a way to know when the request is ended for commit/rollback the changes.
the best way i found is by using ResponseFilters:
public class AppHost : AppHostBase
{
public AppHost()
: base("", typeof (Foo).Assembly, typeof (FooService).Assembly)
{
}
public override void Configure(Container container)
{
// Some Configuration...
this.ResponseFilters.Add((httpReq, httpResp, respnseDto) =>
{
var currentSession = (RavenSession) this.Container.Resolve<IRavenSession>();
if (!httpResp.IsErrorResponse())
{
currentSession.Commit();
}
else
{
currentSession.Rollback();
}
});
// Some Configuration...
}
}
I am sure that there is a better way to do this but how?
I just included this on the Configure method for the AppHost
var store = new DocumentStore()
{
Url = "http://127.0.0.1:8080",
DefaultDatabase = "Test"
}.Initialize();
container.Register(store);
container.Register(c => c.Resolve<IDocumentStore>().OpenSession()).ReusedWithin(ReuseScope.Request);
You can put it aside on module and initialize it.
Then in your services just add a constructor that accepts IDocumentSession
public HelloService : Service {
private readonly IDocumentSession session;
public HelloService(IDocumentSession session) {
this.session = session;
}
}
And you're good to go.
Filtering the response in ServiceStack
The ways to introspect the Response in ServiceStack is with either:
The Response Filter or Response Filter Attributes or other custom hooks
Overriding AppHost.ServiceExceptionHandler or custom OnAfterExecute() hook
Some other notes that might be helpful:
ServiceStack's built-in IOC (Funq) now supports RequestScope
You can add IDisposable to a base class which gets called immediately after the service has finished executing, e.g. if you were to use an RDBMS:
public class FooServiceBase : IService, IDisposable
{
public IDbConnectionFactory DbFactory { get; set; }
private IDbConnection db;
public IDbConnection Db
{
get { return db ?? (db = DbFactory.OpenDbConnection()); }
}
public object Any(ProductFind request)
{
return new FooResponse {
Result = Db.Id<Product>(request.Id)
};
}
public void Dispose()
{
if (db != null) db.Dispose();
}
}
I tried the answer given by Felipe Leusin but it has not worked for me. The main thing that I want to achieve is having a single DocumentSession.SaveChanges call per request. After looking at the RacoonBlog DocumentSession lifecycle management and at ServiceStack request lifecycle events I put together a configuration that works for me:
public override void Configure(Funq.Container container)
{
RequestFilters.Add((httpReq, httpRes, requestDto) =>
{
IDocumentSession documentSession = Container.Resolve<IDocumentStore>().OpenSession();
Container.Register<IDocumentSession>(documentSession);
});
ResponseFilters.Add((httpReq, httpRes, requestDto) =>
{
using (var documentSession = Container.Resolve<IDocumentSession>())
{
if (documentSession == null)
return;
if (httpRes.StatusCode >= 400 && httpRes.StatusCode < 600)
return;
documentSession.SaveChanges();
}
});
var documentStore = new DocumentStore
{
ConnectionStringName = "RavenDBServer",
DefaultDatabase = "MyDatabase",
}.Initialize();
container.Register(documentStore);
I am using funq with RequestScope for my RavenSession, and now i update it to:
public class RavenSession : IRavenSession, IDisposable
{
#region Data Members
private readonly IDocumentStore _store;
private readonly IRequestContext _context;
private IDocumentSession _innerSession;
#endregion
#region Properties
public IDocumentSession InnerSession
{
get { return _innerSession ?? (_innerSession = _store.OpenSession()); }
}
#endregion
#region Ctor
public RavenSession(IDocumentStore store, IRequestContext context)
{
_store = store;
_context = context;
}
#endregion
#region IDocumentSession Delegation
public ISyncAdvancedSessionOperation Advanced
{
get { return InnerSession.Advanced; }
}
public void Delete<T>(T entity)
{
InnerSession.Delete(entity);
}
public ILoaderWithInclude<object> Include(string path)
{
return InnerSession.Include(path);
}
public ILoaderWithInclude<T> Include<T, TInclude>(Expression<Func<T, object>> path)
{
return InnerSession.Include<T, TInclude>(path);
}
public ILoaderWithInclude<T> Include<T>(Expression<Func<T, object>> path)
{
return InnerSession.Include(path);
}
public T Load<T>(string id)
{
return InnerSession.Load<T>(id);
}
public T[] Load<T>(params string[] ids)
{
return InnerSession.Load<T>(ids);
}
public T Load<T>(ValueType id)
{
return InnerSession.Load<T>(id);
}
public T[] Load<T>(IEnumerable<string> ids)
{
return InnerSession.Load<T>(ids);
}
public IRavenQueryable<T> Query<T, TIndexCreator>() where TIndexCreator : AbstractIndexCreationTask, new()
{
return InnerSession.Query<T, TIndexCreator>();
}
public IRavenQueryable<T> Query<T>()
{
return InnerSession.Query<T>();
}
public IRavenQueryable<T> Query<T>(string indexName)
{
return InnerSession.Query<T>(indexName);
}
public void Store(dynamic entity, string id)
{
InnerSession.Store(entity, id);
}
public void Store(object entity, Guid etag, string id)
{
InnerSession.Store(entity, etag, id);
}
public void Store(object entity, Guid etag)
{
InnerSession.Store(entity, etag);
}
public void Store(dynamic entity)
{
InnerSession.Store(entity);
}
#endregion
#region Implementation of IDisposable
public void Dispose()
{
if (_innerSession != null)
{
var httpResponse = _context.Get<IHttpResponse>();
try
{
if (!httpResponse.IsErrorResponse())
{
_innerSession.SaveChanges();
}
}
finally
{
_innerSession.Dispose();
}
}
}
#endregion
}
but this would not work because:
1) although i am using RequestScope, no one is register the IRequestContext of the request so funq cant resolve my RavenSession.
2) funq does not run the Dispose method after the request is done, which is odd.