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> }
I have:
namespace Test
{
public interface ITest
{
public string Test1(string s);
}
public class Test : ITest
{
[Microsoft.AspNetCore.Mvc.TempData]
public string Message
{
get; set;
}
public string Test1(string s)
{
Message = "Test " + s;
return "Test has run";
}
}
}
And in Startup.cs:
services.AddScoped<Test.ITest, Test.Test>();
Then in a Razor view:
#inject Test.ITest Test
<p>Result is #Test.Test1("Hello World!")</p>
<p>TempData["Message"] is #TempData["Message"]</p>
The output is:
Result is Test has run
TempData["Message"] is
Where have I done incorrectly? How can I pass a TempData["Message"] from some code (that is not in a Controller) to a Razor page?
How can I pass a TempData["Message"] from some code (that is not in a Controller) to a Razor page?
If you'd like to retain and pass value via TempData inside your custom service, you can try the following code snippet.
public interface ITest
{
public string Test1(string s);
public string Test2();
}
public class Test : ITest
{
private readonly IHttpContextAccessor _httpContextAccessor;
public Test(IHttpContextAccessor httpContextAccessor)
{
_httpContextAccessor = httpContextAccessor;
}
public string Message
{
get {
var tempDataDictionaryFactory = _httpContextAccessor.HttpContext.RequestServices.GetRequiredService<ITempDataDictionaryFactory>();
var tempDataDictionary = tempDataDictionaryFactory.GetTempData(_httpContextAccessor.HttpContext);
if (tempDataDictionary.TryGetValue("Message", out object value))
{
return (string)value;
};
return "";
}
set
{
var tempDataDictionaryFactory = _httpContextAccessor.HttpContext.RequestServices.GetRequiredService<ITempDataDictionaryFactory>();
var tempDataDictionary = tempDataDictionaryFactory.GetTempData(_httpContextAccessor.HttpContext);
tempDataDictionary.Remove("Message");
tempDataDictionary.TryAdd("Message", value);
}
}
public string Test1(string s)
{
Message = "Test " + s;
return "Test has run";
}
public string Test2()
{
return Message;
}
}
In Razor Page
<p>Result is #Test.Test1("Hello World!")</p>
<p>TempData["Message"] is #TempData.Peek("Message")</p>
<p>#Test.Test2()</p>
Test Result
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've two projects (class library projects) which implement one interface:
The first one:
public class MailPlugin : Extensibility.IProductorPlugin
{
...
}
The second one:
public class FileSystemPlugin : Extensibility.IProductorPlugin
{
...
}
Extensibility.IProductorPlugin, is a interface of a third project:
namespace Extensibility
{
public delegate void NotifyDigitalInputs(List<Domain.DigitalInput> digital_inputs);
public interface IProductorPlugin
{
String Name { get; }
String Description { get; }
String Version { get; }
List<Domain.Channel> AvailableChannels { get; }
IEnumerable<Guid> TypeGuids { get; }
event NotifyDigitalInputs OnDigitalInputs;
}
}
In my composition root, I've created this class:
namespace UI
{
public sealed class NinjectServiceLocator
{
private static readonly Lazy<NinjectServiceLocator> lazy = new Lazy<NinjectServiceLocator>(() => new NinjectServiceLocator());
public static NinjectServiceLocator Instance { get { return lazy.Value; } }
public Ninject.IKernel Kernel { get; private set; }
private NinjectServiceLocator()
{
using (var k = this.Kernel = new Ninject.StandardKernel())
{
k.Bind(b => b.FromAssembliesMatching("*")
.SelectAllClasses()
.InheritedFrom(typeof(Extensibility.IProductorPlugin))
.BindAllInterfaces()
);
}
}
}
}
So, when I want to look for all plugins, I just perform this:
protected void initialize()
{
foreach (Extensibility.IProductorPlugin productor_plugin in NinjectServiceLocator.Instance.Kernel.GetAll(typeof(Extensibility.IProductorPlugin)))
{
using (var channel_tile = new DevExpress.XtraBars.Docking2010.Views.WindowsUI.Tile() { Group = "Plugin Channels" })
{
foreach (Domain.Channel channel in productor_plugin.AvailableChannels)
{
channel_tile.Elements.Add(new DevExpress.XtraEditors.TileItemElement() { Text = channel.Name });
channel_tile.Elements.Add(new DevExpress.XtraEditors.TileItemElement() { Text = channel.Description });
this.tileContainer1.Items.Add(channel_tile);
}
}
}
}
However, GetAll returns anything.
What am I doing wrong?
I'll appreciate a lot your help.
Thanks for all.
try removing the using() from around the Kernel instantiation. a using will dispose the object at the end of the scope, which we don't want for a kernel.
using (var k = this.Kernel = new Ninject.StandardKernel())
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.