I'm having a problems configuring instance per http request in service stack. I am running a console app and trying to use Ninject as my container.
I would like an instance to last for 1 full request. I would like to know how I can configure this.
I would like an example showing how I could start the lifetime of the dependency when the request starts and how to dispose that same instance when the request ends.
Could anyone give me an example of how to do this in autofac and ninject.
public class OutboundHost : AppHostHttpListenerBase
{
public OutboundHost() : base("StarterTemplate HttpListener", typeof(SmsService).Assembly) { }
protected override void ProcessRequest(System.Net.HttpListenerContext context)
{
Console.WriteLine("Begin Processing request");
base.ProcessRequest(context);
}
public override void Start(string urlBase)
{
base.Start(urlBase);
}
public override void OnEndRequest()
{
Console.WriteLine("End processing request");
base.OnEndRequest();
}
public override void Configure(Funq.Container container) {
IKernel kernel = new StandardKernel();
RegisterEntityFrameworkNinject(kernel);
RegisterDependenciesNinject(kernel);
LogManager.LogFactory = new ConsoleLogFactory();
Console.WriteLine("Starting Container");
container.Register<ICacheClient>(new MemoryCacheClient());
container.RegisterValidators(typeof(SendSmsValidator).Assembly);
Plugins.Add(new RazorFormat());
Plugins.Add(new ValidationFeature());
Plugins.Add(new RequestLogsFeature() { RequiredRoles = new string[] { } });
Plugins.Add(new AuthFeature(() => new AuthUserSession(), new IAuthProvider[] {new CustomCredentialsAuthProvider(), }));
SetConfig(new EndpointHostConfig
{
CustomHttpHandlers = {
{ System.Net.HttpStatusCode.NotFound, new RazorHandler("/notfound") }
}
});
InitializeLookupTables(kernel);
container.Adapter = new NinjectIocAdapter(kernel);
}
private void RegisterDependenciesNinject(IKernel kernel)
{
kernel.Bind<ILog>().ToMethod(c => LogManager.LogFactory.GetLogger(typeof (ConsoleLogger))).InSingletonScope();
kernel.Bind<ISendSmsIntenda>().To<SendSmsIntenda>();
}
private void RegisterEntityFrameworkNinject(IKernel kernel)
{
kernel.Bind<IEntityFrameworkUnitOfWork>().To<EntityFrameworkUnitOfWork>();
kernel.Bind<IUnitOfWork>().To<EntityFrameworkUnitOfWork>();
kernel.Bind<IRepository>().To<Repository>();
kernel.Bind<ILookupTableFactory>().To<EntityFrameworkLookupTableFactory>();
kernel.Bind<IDataQuery>().To<DatabaseQuery>();
kernel.Bind<IContextFactory>().To<DefaultContextFactory<OutboundContext>>().InSingletonScope()
.WithConstructorArgument("connectionStringName", GetConnectionString());
}
private string GetConnectionString()
{
return ConfigurationManager.ConnectionStrings["Outbound"].ConnectionString;
}
private void InitializeLookupTables(IKernel kernel)
{
using (var kernelContext = kernel.BeginBlock())
{
var contextFactory = kernelContext.Get<IContextFactory>();
var frameworkContext = kernelContext.Get<IContextFactory>().GetContext() as IFrameworkContext;
var unitOfWork = kernelContext.Get<IUnitOfWork>();
contextFactory.GetContext().RegisterLookupTable<SmsStatusType, SmsStatusEnum>();
frameworkContext.SaveLookupTableChanges();
unitOfWork.Commit();
}
}
}
public class NinjectIocAdapter : IContainerAdapter
{
private readonly IKernel kernel;
private readonly Container container;
public NinjectIocAdapter(IKernel kernel, Funq.Container container)
{
this.kernel = kernel;
this.container = container;
}
public T Resolve<T>()
{
return this.kernel.Get<T>();
}
public T TryResolve<T>()
{
return this.kernel.TryGet<T>();
}
}
Related
I added the RazorPlugin along with a Test.cshtml in the root View folder (tried wwwroot as well along with TestGet.cshtml). This is an donet Core project. Whenever browsing to: /api/test the following error is generated:
Snapshot of TestGet generated by ServiceStack on 6/3/2017 4:20:40 PM
view json datasource from original url:
http://localhost:51550/api/test? in other formats: json xml csv jsv
Response Status Error CodeNullReferenceExceptionMessageObject
reference not set to an instance of an object.Stack Trace[TestGet:
6/3/2017 4:20:40 PM]: [REQUEST: {}] System.NullReferenceException:
Object reference not set to an instance of an object. at
ServiceStack.Mvc.RazorFormat.FindView(IEnumerable1 viewNames) in
/opt/lib/teamcity-agent/work/d09206570215629/src/ServiceStack.Mvc/RazorFormat.cs:line
174 at ServiceStack.Mvc.RazorFormat.ProcessRequest(IRequest req,
IResponse res, Object dto) in
/opt/lib/teamcity-agent/work/d09206570215629/src/ServiceStack.Mvc/RazorFormat.cs:line
138 at System.Linq.Enumerable.Any[TSource](IEnumerable1 source,
Func`2 predicate) at
ServiceStack.Formats.HtmlFormat.SerializeToStream(IRequest req, Object
response, IResponse res) in
/opt/lib/teamcity-agent/work/d09206570215629/src/ServiceStack/Formats/HtmlFormat.cs:line
63Errors
The other formats work (json/etc).
public class Test
{
public string ExternalId { get; set; }
}
[Authenticate, Route("/test")]
public class TestGet : IGet, IReturn<Test>
{
}
public class TestService : Service
{
public IAutoQueryDb _autoQueryDb { get; set; }
public IDbConnectionFactory _dbFactory { get; set; }
public TestService(IDbConnectionFactory dbFactory)
{
_dbFactory = dbFactory;
}
public Test Get(TestGet request)
{
var test = new Test();
test.ExternalId = "abc";
return test;
}
}
Test.cshtml
#using App.Shared.DomainModel
#model Test
<div>Test</div>
AppHost.cs
using App.DomainServices;
using App.FontEnd.Infrastructure.Configuration;
using App.FontEnd.Infrastructure.Core;
using App.Shared.DomainModel;
using Funq;
using LightInject;
using ServiceStack;
using ServiceStack.Admin;
using ServiceStack.Api.Swagger;
using ServiceStack.Auth;
using ServiceStack.Caching;
using ServiceStack.Configuration;
using ServiceStack.Data;
using ServiceStack.Mvc;
using ServiceStack.OrmLite;
using ServiceStack.Validation;
using System;
using System.Collections.Generic;
using System.Data;
using System.Linq;
using System.Threading.Tasks;
namespace App.FrontEnd
{
public class AppHost : AppHostBase
{
/// <summary>
/// Base constructor requires a Name and Assembly where web service implementation is located
/// </summary>
public AppHost()
: base("TestApi", typeof(CompanyService).GetAssembly())
{
}
/// <summary>
/// Application specific configuration
/// This method should initialize any IoC resources utilized by your web service classes.
/// </summary>
public override void Configure(Container container)
{
this.GlobalRequestFilters.Add((httpReq, httpResp, requestDto) =>
{
var currentSession = httpReq.GetSession();
if (currentSession != null)
{
RequestContext.Instance.Items.Add("CurrentUserName", currentSession.UserName);
RequestContext.Instance.Items.Add("CurrentUserId", currentSession.UserAuthId);
}
});
ServiceContainer LightContainer = new ServiceContainer();
LightContainer.RegisterInstance<IDbConnectionFactory>
(
new OrmLiteConnectionFactory(
"removed",
SqlServer2014Dialect.Provider
)
);
LightContainer.Register<IDbConnection>(c =>
c.GetInstance<IDbConnectionFactory>().OpenDbConnection(),
new PerScopeLifetime()
);
LightContainer.Register<OrmLiteAppSettings>(c =>
new OrmLiteAppSettings(c.GetInstance<IDbConnectionFactory>()));
LightContainer.Register<ServiceStack.Web.IServiceGatewayFactory>(x => new ApiServiceGatewayFactory());
container.Adapter = new LightInjectAdapter(LightContainer);
var settings = LightContainer.GetInstance<OrmLiteAppSettings>();
settings.InitSchema();
AppSettings = new MultiAppSettings(
settings
);
container.Register<ICacheClient>(new OrmLiteCacheClient
{
DbFactory = LightContainer.GetInstance<IDbConnectionFactory>()
});
var cacheclient = container.Resolve<ICacheClient>();
cacheclient.InitSchema();
AuthConfig(container, AppSettings);
Plugins.Add(new RegistrationFeature());
Plugins.Add(new SwaggerFeature());
Plugins.Add(new RequestLogsFeature());
Plugins.Add(new PostmanFeature());
Plugins.Add(new CorsFeature(allowCredentials: true));
Plugins.Add(new ValidationFeature());
Plugins.Add(new RazorFormat());
OrmLiteConfig.InsertFilter = (dbCmd, row) =>
{
var auditRow = row as CoreModel;
if (auditRow != null)
{
var currentDate = DateTime.UtcNow;
var insertUserId = RequestContext.Instance.Items["CurrentUserId"] as string;
auditRow.Id = Guid.NewGuid();
auditRow.CreatedDate = currentDate;
auditRow.CreatedBy = insertUserId;
auditRow.UpdatedDate = currentDate;
auditRow.UpdatedBy = insertUserId;
}
};
OrmLiteConfig.UpdateFilter = (dbCmd, row) =>
{
var auditRow = row as CoreModel;
if (auditRow != null)
{
var updateUserId = RequestContext.Instance.Items["CurrentUserId"] as string;
auditRow.UpdatedDate = DateTime.UtcNow;
auditRow.UpdatedBy = updateUserId;
}
};
var aq = new AutoQueryFeature { MaxLimit = 100, EnableAutoQueryViewer = true };
aq.ImplicitConventions.Add("%neq", aq.ImplicitConventions["%NotEqualTo"]);
aq.ImplicitConventions.Add("%eq", "{Field} = {Value}");
Plugins.Add(aq);
Plugins.Add(new AdminFeature());
SetConfig(new HostConfig
{
HandlerFactoryPath = "api",
DebugMode = true
});
container.CheckAdapterFirst = true;
//Set up service stack validators
container.ValidatorsSetup();
}
public void AuthConfig(Container container, IAppSettings settings)
{
Plugins.Add(new AuthFeature(() => new AuthUserSession(),
new IAuthProvider[] {
new CredentialsAuthProvider(AppSettings),
new JwtAuthProvider(AppSettings)
{
AuthKeyBase64 = "abcdefgh"
},
new BasicAuthProvider()
}));
var authRepo = CreateOrmLiteAuthRepo(container, settings);
}
private static IUserAuthRepository CreateOrmLiteAuthRepo(Container container, IAppSettings appSettings)
{
//Store User Data into the referenced SqlServer database
container.Register<IAuthRepository>(c =>
new OrmLiteAuthRepository(c.Resolve<IDbConnectionFactory>()));
//Use OrmLite DB Connection to persist the UserAuth and AuthProvider info
var authRepo = (OrmLiteAuthRepository)container.Resolve<IAuthRepository>();
//If using and RDBMS to persist UserAuth, we must create required tables
if (appSettings.Get("RecreateAuthTables", false))
authRepo.DropAndReCreateTables(); //Drop and re-create all Auth and registration tables
else
authRepo.InitSchema(); //Create only the missing tables
return authRepo;
}
}
}
I've created a minimal verifiable MVC test project to simulate your configuration which is working as expected at: https://github.com/NetCoreApps/scratch/tree/master/src/RazorApi
It includes the same Test Services, (sans [Authenticate] attribute):
public class Test
{
public string ExternalId { get; set; }
}
[Route("/test")]
public class TestGet : IGet, IReturn<Test>
{
}
public class TestService : Service
{
public Test Get(TestGet request)
{
var test = new Test { ExternalId = "abc" };
return test;
}
}
And the minimal AppHost configuration which just sets the HandlerFactoryPath to api and registers ServiceStack's RazorFormat plugin:
public class AppHost : AppHostBase
{
public AppHost()
: base("ServiceStack + .NET Core", typeof(MyServices).GetTypeInfo().Assembly) {}
public override void Configure(Funq.Container container)
{
SetConfig(new HostConfig
{
HandlerFactoryPath = "api",
DebugMode = true,
});
Plugins.Add(new RazorFormat());
}
}
With the Test.cshtml maintained in /Views/Test.cshtml:
#model Test
#{
Layout = "_Layout";
}
<h1>Test.cshtml</h1>
<p>#Model.ExternalId</p>
Which works as expected with the Razor View executed when calling:
http://localhost:5000/api/test
Which also works when renamed to match the Request DTO at TestGet.cshtml.
As the issue seems specific to your project I'd compare your layout with bare RazorApi Github Project to see if you can find any differences, failing that I'd recommend commenting out configuration to get it to a working state then uncomment sections at a time to find out which configuration is causing the issue.
I have the following code to register Mapping (version 4.2)
public class ModelMapperProfile : Profile
{
protected override void Configure()
{
CreateMap<Case, CaseModel>();
CreateMap<CaseDetail, CaseDetailModel>();
}
}
public static class AutoMapperService
{
public static MapperConfiguration Initialize()
{
MapperConfiguration config = new MapperConfiguration(cfg =>
{
cfg.AddProfile<ModelMapperProfile>();
});
return config;
}
}
And I register the dependency using unity as follows...
public static void RegisterTypes(IUnityContainer container)
{
container.LoadConfiguration();
var mapper = AutoMapperService.Initialize()
.CreateMapper();
container.RegisterInstance<IMapper>(mapper);
}
My here service constructor..
public TaxLiabilityCaseService(IMapper mapper,
IUnitOfWork unitofWork,
IRepository<Case> caseR,
IRepository<CaseDetail> caseDetailR)
{
_mapper = mapper;
_unitofWork = unitofWork;
_caseR = caseR;
_caseDetailR = caseDetailR;
}
And I get the following error message..
The current type, AutoMapper.IMapper, is an interface and cannot be
constructed. Are you missing a type mapping?
Answers found here did not work for me
What am I missing here
Try following these steps (MVC5):
Get Unity Nuget package:
Unity.Mvc5
Create this class:
public class MapperConfig
{
public static IMapper Mapper { get; set; }
public static void RegisterProfiles()
{
var config = new MapperConfiguration(cfg =>
{
// add profiles here
});
config.AssertConfigurationIsValid();
Mapper = config.CreateMapper();
}
}
In the UnityConfig file (created by the package), add this:
public static void RegisterComponents()
{
var container = new UnityContainer();
container.RegisterInstance<IMapper>(MapperConfig.Mapper);
}
In the Global.asax, add these:
protected void Application_Start()
{
MapperConfig.RegisterProfiles();
UnityConfig.RegisterComponents();
}
You should be good after this.
Given the following code for my connection factory:
public interface IDbFrontEndConnectionFactory : IDbConnectionFactory
{
}
public class FrontEndDbFactory : IDbFrontEndConnectionFactory
{
private readonly IAppSettings _settings;
private readonly IDbConnectionFactory _dbFactory;
public Func<IDbConnection, IDbConnection> ConnectionFilter { get; set; }
public FrontEndDbFactory(IDbConnectionFactory dbFactory, IAppSettings settings)
{
_dbFactory = dbFactory;
_settings = settings;
ConnectionFilter = (Func<IDbConnection, IDbConnection>)(x => x);
}
public IDbConnection OpenDbConnection()
{
var tenantId = Tenant.GetTenant();
return OpenTenant(tenantId);
}
public IDbConnection OpenTenant(string tenantId = null)
{
return tenantId != null
? new OrmLiteConnectionFactory(_settings.GetString("TenantId{0}:{1}".Fmt(tenantId, "Frontend"))).OpenDbConnection()
: _dbFactory.OpenDbConnection();
}
public IDbConnection CreateDbConnection()
{
return _dbFactory.CreateDbConnection();
}
}
IoC registration
IDbFrontEndConnectionFactory feFactory = new FrontEndDbFactory(masterDbFactory, fileSettings)
{
ConnectionFilter = x => new ProfiledDbConnection(x, Profiler.Current)
};
container.Register(feFactory);
Global.asax
protected void Application_Start(object sender, EventArgs e)
{
LogManager.LogFactory = new NLogFactory();
new AppHost().Init();
}
protected void Application_BeginRequest(object src, EventArgs e)
{
if (Request.IsLocal)
Profiler.Start();
}
protected void Application_EndRequest(object src, EventArgs e)
{
Profiler.Stop();
}
The Repository:
public partial class CampaignRepository : ICampaignRepository
{
readonly ILog _log = LogManager.GetLogger(typeof(CampaignRepository));
private readonly IDbFrontEndConnectionFactory _connectionFactory;
public CampaignRepository(IDbFrontEndConnectionFactory connectionFactory)
{
_connectionFactory = connectionFactory;
}
}
The IoC registration of my repository
container.Register<ICampaignRepository>(c => new CampaignRepository(c.Resolve<IDbFrontEndConnectionFactory>()));
The Service method when I'm inspecting the profiler
public CampaignViewModel Get(GetCampaign request)
{
if (request == null) throw new ArgumentNullException("request");
var profiler = Profiler.Current;
using (profiler.Step("Campaign Service Get"))
{
try
{
var vm = CampaignRepository.GetCampaignInfo(request.Id).ToViewModel();
if (vm.Campaign != null) return vm;
Response.StatusCode = (int)HttpStatusCode.NotFound;
return null;
}
catch (Exception exception)
{
_log.Error(request.ToJson(), exception);
throw;
}
}
}
I was expecting the profiler to show the sql timings, but that's not the case, the connection is not being profiled.
Is there something else that needs to be enabled or corrected for this to work?
Thank you, Stephen
To enable SQL Profiling in MiniProfiler you need to register the OrmLiteConnectionFactory to use MiniProfilers ProfiledDbConnection, e.g:
Container.Register<IDbConnectionFactory>(c =>
new OrmLiteConnectionFactory(connectionString, SqlServerDialect.Provider) {
ConnectionFilter = x => new ProfiledDbConnection(x, Profiler.Current)
});
I have this base class structure:
Base:
public abstract class BackgroundTask
{
protected readonly Logger Logger = LogManager.GetCurrentClassLogger();
protected virtual void Initialize()
{
// initialize database access
}
public void Run()
{
Initialize();
try
{
Execute();
// insert to database or whatever
}
catch (Exception ex)
{
Logger.ErrorException(string.Format("Error proccesing task: {0}\r\n", ToString()), ex);
Exceptions.Add(ex);
}
finally
{
TaskExecuter.Discard();
}
}
protected abstract void Execute();
public abstract override string ToString();
public IList<Exception> Exceptions = new List<Exception>();
}
Task executor:
public static class TaskExecuter
{
private static readonly ThreadLocal<IList<BackgroundTask>> TasksToExecute
= new ThreadLocal<IList<BackgroundTask>>(() => new List<BackgroundTask>());
public static void ExecuteLater(BackgroundTask task)
{
TasksToExecute.Value.Add(task);
}
public static void StartExecuting()
{
foreach (var backgroundTask in TasksToExecute.Value)
{
Task.Factory.StartNew(backgroundTask.Run);
}
}
public static void Discard()
{
TasksToExecute.Value.Clear();
TasksToExecute.Dispose();
}
}
FileTask:
public class FileTask : BackgroundTask
{
protected static string BaseFolder = #"C:\ASCII\";
private static readonly ReaderWriterLockSlim Lock = new ReaderWriterLockSlim();
private readonly string _folder;
private IHistoryRepository _historyRepository;
public string Folder
{
get { return _folder; }
}
public FileTask(string folder)
{
_folder = string.Format("{0}{1}", BaseFolder, folder);
}
protected override void Initialize()
{
_historyRepository = new HistoryRepository();
}
protected override void Execute()
{
// todo: Get institute that are active,
var institute = MockInstitute(); // todo: uncomment _historyRepository.FindInstituteByFolderName(Folder);
// todo: Update institute, lastupdate - [date] | [files amount] | [phonenumbers amount]
if (institute == null)
{
Logger.Warn("Not found data", Folder);
return;
}
// todo: read file get encoding | type and parse it
Task.Factory.StartNew(ReadFile);
}
private void ReadFile()
{
var list = GetFilesByFolder();
StreamReader sr = null;
try
{
Lock.EnterReadLock();
foreach (var fi in list)
{
var fileName = fi.FullName;
Logger.Info("Line: {0}:=> Content: {1}", fileName, Thread.CurrentThread.ManagedThreadId);
sr = new StreamReader(fileName, DetectEncoding(fileName));
string currentLine;
while ((currentLine = sr.ReadLine()).ReturnSuccess())
{
if (string.IsNullOrEmpty(currentLine)) continue;
Logger.Info("Line: {0}:=> Content: {1}", fileName, currentLine);
}
}
Lock.ExitReadLock();
}
finally
{
if (sr != null) sr.Dispose();
Logger.Info("Finished working" + Folder);
}
}
protected IEnumerable<FileInfo> GetFilesByFolder()
{
return Directory.GetFiles(Folder).Select(fileName => new FileInfo(fileName));
}
protected Encoding DetectEncoding(string file)
{
using (FileStream fs = new FileStream(file, FileMode.Open, FileAccess.ReadWrite, FileShare.ReadWrite))
{
var cdet = new Ude.CharsetDetector();
cdet.Feed(fs);
cdet.DataEnd();
return cdet.With(x => x.Charset)
.Return(x => Encoding.GetEncoding(cdet.Charset),
Encoding.GetEncoding("windows-1255"));
}
}
private Institute MockInstitute()
{
return new Institute
{
FromFolderLocation = string.Format("{0}{1}", BaseFolder, Folder)
};
}
public override string ToString()
{
return string.Format("Folder: {0}", Folder);
}
}
When don't read the file every thing ok, the Log is populated and every thing runs smooth,
but when i attach the Task.Factory.StartNew(ReadFile); method i have an exception.
Exception:
Cannot access a disposed object.
Object name: 'The ThreadLocal object has been disposed.'.
How do i solve that issue? might i need to change the LocalThread logic, or what - i have been trying to handle that issue, for almost a day.
BTW: It's an MVC4 project, and C# 5.0 and i'm trying to TDD it all.
You shouldn't be calling TasksToExecute.Dispose();
there.
Here is my problem:
I want to pass in one of the values to the constructor every time I request an instance form the kernel. I written some code below to illustrate the problem. The test is not failing so I guess that this works, but it does look pretty ugly. Is there a better, cleaner way to accomplish this with Ninject? Or should I rethink my design? All suggestions are appreciated.
[TestFixture]
public class Sandbox
{
[Test]
public void Run_Forrest_Run()
{
using (var kernel = new StandardKernel(new Module()))
{
var connection = new Connection(Guid.NewGuid().ToString());
var downloader = kernel.Get<IDownloader>(new IParameter[] { new Parameter("connection", connection, false) });
Assert.That(downloader.Connection.Info, Is.EqualTo(connection.Info));
}
}
public class Downloader : IDownloader
{
public Downloader(Connection connection, ILogger logger)
{
Connection = connection;
Logger = logger;
}
public Connection Connection { get; private set; }
public void Download()
{
Logger.Log("Downloading...");
}
public ILogger Logger { get; private set; }
}
public interface IDownloader
{
Connection Connection { get; }
void Download();
}
public class ConsoleLogger : ILogger
{
public void Log(string message)
{
Console.Out.WriteLine(message);
}
}
public interface ILogger
{
void Log(string message);
}
public class Connection
{
public Connection(string info)
{
Info = info;
}
public string Info { get; private set; }
}
public class Module : NinjectModule
{
public override void Load()
{
Bind<ILogger>().To<ConsoleLogger>();
Bind<IDownloader>().To<Downloader>()
.WithConstructorArgument("connection", context =>
{
var p = context.Parameters.First(x => x.Name == "connection");
return p.GetValue(context, null);
});
}
}
}
If you always want to specify the Connection when resolving a IDownloader then I think the ConstructorArgument (which is a IParameter) is what you are looking for:
[Test]
public void Run_Forrest_Run()
{
using (var kernel = new StandardKernel(new Module()))
{
var connection = new Connection(Guid.NewGuid().ToString());
var downloader = kernel.Get<IDownloader>(new [] {
new ConstructorArgument("connection", connection) });
Assert.That(downloader.Connection.Info, Is.EqualTo(connection.Info));
}
}
public class Module : NinjectModule
{
public override void Load()
{
Bind<ILogger>().To<ConsoleLogger>();
Bind<IDownloader>().To<Downloader>();
}
}