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;
}
...
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> }
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!
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);
}
}
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.