Where do I place Firebase Check and Fix Dependencies code in Unity? - firebase

I wanted to use Firebase in my Unity game so one of the steps was to add the following code, but the place into which the code should be added wasn't specified. Any help?
Firebase.FirebaseApp.CheckAndFixDependenciesAsync().ContinueWith(task => {
var dependencyStatus = task.Result;
if (dependencyStatus == Firebase.DependencyStatus.Available) {
// Create and hold a reference to your FirebaseApp,
// where app is a Firebase.FirebaseApp property of your application class.
app = Firebase.FirebaseApp.DefaultInstance;
// Set a flag here to indicate whether Firebase is ready to use by your app.
} else {
UnityEngine.Debug.LogError(System.String.Format(
"Could not resolve all Firebase dependencies: {0}", dependencyStatus));
// Firebase Unity SDK is not safe to use here.
}
});
(This is the link to the instructions on Firebase)
https://firebase.google.com/docs/unity/setup?authuser=0#confirm-google-play-version

The best answer I can give is "before you do anything else" in Unity.
In a normal application, you'd have something like a main api entry point (application:didFinishLaunchingWithOptions in iOS, Activity.onCreate in Android, or literally void main(int,char**) in a typical C/C++ desktop application). But in Unity, you don't have any sort of "run this before everything else" logic. Whichever scene is first is up to you and subject to change, and scripts execute roughly in parallel (in an order that's best considered random but technically can be ordered).
Here are the patterns I've either used or have thought about using and a few associated pros and cons:
[Recommended for beginners and small projects] In my videos, I typically recommend having a "loading" or "setup" scene. In that scene I place a FirebaseInit script that will initialize Firebase and raise an event when it's done. I can then either collect a bunch of initialization functionality (say downloading asset bundles or doing some initial setup processing) or just jump immediately into my main scene. Most of the time this will resolve to a no-op (on Android play services is up to date), so you can even shove it in the main menu if you're careful:
using System;
using Firebase;
using Firebase.Extensions;
using UnityEngine;
using UnityEngine.Events;
public class FirebaseInit : MonoBehaviour
{
public UnityEvent OnInitialized = new UnityEvent();
public InitializationFailedEvent OnInitializationFailed = new InitializationFailedEvent();
void Start()
{
FirebaseApp.CheckAndFixDependenciesAsync().ContinueWithOnMainThread(task =>
{
if (task.Exception != null)
{
OnInitializationFailed.Invoke(task.Exception);
}
else
{
OnInitialized.Invoke();
}
});
}
[Serializable]
public class InitializationFailedEvent : UnityEvent<Exception>
{
}
}
I'm not a big proponent of dependency injection (as in I typically don't use DI frameworks, but I do tend to use [SerializedField]s as a pseudo-DI system), so I don't have a good example to share. You can use ZenJect to create and inject the Firebase singletons to anything that needs them. The biggest issue you run into is that you have to await the dependencies to be initialized, which is possible but I just haven't gone through the steps of doing it in a sample project. The benefit is that you only need to express "I depend on Firebase", and ZenJect will take care of the rest (just avoid the DefaultInstance functions in this case).
In more complex projects, I'll tend to wrap Firebase in a Coroutine (or an async task, but I prefer coroutines in Unity). So I'll have a Coroutine that will wait for check and fix dependencies to complete and return an instance of Realtime Database when it finishes (and intelligent logic to skip the waiting if not necessary). Again you have to avoid DefaultInstance outside of your management script, and every use of Firebase becomes a coroutine, but you can be sure that you always await on construction. Here is one example focusing on Realtime Database (I'm stripping unnecessary code to make it fit in a SO answer), and I'll reiterate that this is a lot of overhead for a small project:
public class FirebaseBehaviour : MonoBehaviour
{
private IEnumerator _setupFirebase;
private DatabaseReference _databaseReference;
void Awake()
{
// everything depends on firebase being setup. Do this first.
_setupFirebase = SetupFirebase();
StartCoroutine(_setupFirebase);
}
private IEnumerator SetupFirebase()
{
// we need to fix dependencies on Android
if (Application.platform == RuntimePlatform.Android && !Application.isEditor)
{
Debug.Log("Checking dependencies on Android");
var checkDependencies = new TaskYieldInstruction<DependencyStatus>(FirebaseApp.CheckDependenciesAsync());
yield return checkDependencies;
Debug.Log($"Check Dependencies: {checkDependencies.Result}");
}
_databaseReference = FirebaseDatabase.DefaultInstance.RootReference;
}
/// <summary>
/// Safely gets a database reference at the given path
/// </summary>
/// <param name="path">The path at which to get a reference</param>
/// <returns>A yield instruction that can be yielded in a coroutine</returns>
public GetDatabaseReferenceYieldInstruction GetDatabaseReference(string path)
{
return new GetDatabaseReferenceYieldInstruction(path, _setupFirebase);
}
/// <summary>
/// Asynchronously gets a database reference at the given path after a predicate executes
/// </summary>
public class GetDatabaseReferenceYieldInstruction : IEnumerator
{
private IEnumerator _predicate;
private readonly string _path;
public DatabaseReference Root { get; private set; }
public GetDatabaseReferenceYieldInstruction(string path, IEnumerator predicate)
{
_path = path;
_predicate = predicate;
}
public bool MoveNext()
{
if (_predicate != null)
{
if (_predicate.MoveNext())
{
return true;
}
_predicate = null;
// TODO: this is a cross cutting concern to inject
Root = FirebaseDatabase.DefaultInstance.RootReference.Child(_path);
}
return false;
}
public void Reset()
{
}
public object Current => Root;
}
}
Which you can use like this:
[SerializeField] private FirebaseSingleton _firebaseSingleton;
public void Awake()
{
_firebase = _firebaseSingleton.Instance;
_getDatabase = _firebase.GetDatabaseReference(DatabaseName);
}
private IEnumerator RegisterForEvents()
{
yield return _getDatabase;
_getPigDatabase.Root.ValueChanged += HandlePigValuesChanged;
}
With Unity's new DOTS system, I've been flirting with the idea of moving Unity's initialization into the OnCreate function for a system. Because you have a clean entrypoint, you'd be able to block Firebase functionality until it comes online, and you could control Firebase by injecting specific entities into the world with custom Firebase-specific components on it. I don't have a good example yet, although it's on my project backlog.

Related

Unity to DryIoC conversion ParameterOverride

We are transitioning from Xamarin.Forms to .Net MAUI but our project uses Prism.Unity.Forms. We have a lot of code that basically uses the IContainer.Resolve() passing in a collection of ParameterOverrides with some primitives but some are interfaces/objects. The T we are resolving is usually a registered View which may or may not be the correct way of doing this but it's what I'm working with and we are doing it in backend code (sometimes a service). What is the correct way of doing this Unity thing in DryIoC? Note these parameters are being set at runtime and may only be part of the parameters a constructor takes in (some may be from already registered dependencies).
Example of the scenario:
//Called from service into custom resolver method
var parameterOverrides = new[]
{
new ParameterOverride("productID", 8675309),
new ParameterOverride("objectWithData", IObjectWithData)
};
//Custom resolver method example
var resolverOverrides = new List<ResolverOverride>();
foreach(var parameterOverride in parameterOverrides)
{
resolverOverrides.Add(parameterOverride);
}
return _container.Resolve<T>(resolverOverrides.ToArray());
You've found out why you don't use the container outside of the resolution root. I recommend not trying to replicate this error with another container but rather fixing it - use handcoded factories:
internal class SomeFactory : IProductViewFactory
{
public SomeFactory( IService dependency )
{
_dependency = dependency ?? throw new ArgumentNullException( nameof(dependency) );
}
#region IProductViewFactory
public IProductView Create( int productID, IObjectWithData objectWithData ) => new SomeProduct( productID, objectWithData, _dependency );
#endregion
#region private
private readonly IService _dependency;
#endregion
}
See this, too:
For dependencies that are independent of the instance you're creating, inject them into the factory and store them until needed.
For dependencies that are independent of the context of creation but need to be recreated for each created instance, inject factories into the factory and store them.
For dependencies that are dependent on the context of creation, pass them into the Create method of the factory.
Also, be aware of potential subtle differences in container behaviours: Unity's ResolverOverride works for the whole call to resolve, i.e. they override parameters of dependencies, too, whatever happens to match by name. This could very well be handled very differently by DryIOC.
First, I would agree with the #haukinger answer to rethink how do you pass the runtime information into the services. The most transparent and simple way in my opinion is by passing it via parameters into the consuming methods.
Second, here is a complete example in DryIoc to solve it head-on + the live code to play with.
using System;
using DryIoc;
public class Program
{
record ParameterOverride(string Name, object Value);
record Product(int productID);
public static void Main()
{
// get container somehow,
// if you don't have an access to it directly then you may resolve it from your service provider
IContainer c = new Container();
c.Register<Product>();
var parameterOverrides = new[]
{
new ParameterOverride("productID", 8675309),
new ParameterOverride("objectWithData", "blah"),
};
var parameterRules = Parameters.Of;
foreach (var po in parameterOverrides)
{
parameterRules = parameterRules.Details((_, x) => x.Name.Equals(po.Name) ? ServiceDetails.Of(po.Value) : null);
}
c = c.With(rules => rules.With(parameters: parameterRules));
var s = c.Resolve<Product>();
Console.WriteLine(s.productID);
}
}

Using Unity Dependency Injection in Multi-User Web Application: Second User to Log In Causes First User To See Second User's Data

I'm trying to implement a web application using ASP.NET MVC and the Microsoft Unity DI framework. The application needs to support multiple user sessions at the same time, each of them with their own connection to a separate database (but all users using the same DbContext; the database schemas are identical, it's just the data that is different).
Upon a user's log-in, I register the necessary type mappings to the application's Unity container, using a session-based lifetime manager that I found in another question here.
My container is initialized like this:
// Global.asax.cs
public static UnityContainer CurrentUnityContainer { get; set; }
protected void Application_Start()
{
// ...other code...
CurrentUnityContainer = UnityConfig.Initialize();
// misc services - nothing data access related, apart from the fact that they all depend on IRepository<ClientContext>
UnityConfig.RegisterComponents(CurrentUnityContainer);
}
// UnityConfig.cs
public static UnityContainer Initialize()
{
UnityContainer container = new UnityContainer();
DependencyResolver.SetResolver(new UnityDependencyResolver(container));
GlobalConfiguration.Configuration.DependencyResolver = new Unity.WebApi.UnityDependencyResolver(container);
return container;
}
This is the code that's called upon logging in:
// UserController.cs
UnityConfig.RegisterUserDataAccess(MvcApplication.CurrentUnityContainer, UserData.Get(model.AzureUID).CurrentDatabase);
// UnityConfig.cs
public static void RegisterUserDataAccess(IUnityContainer container, string databaseName)
{
container.AddExtension(new DataAccessDependencies(databaseName));
}
// DataAccessDependencies.cs
public class DataAccessDependencies : UnityContainerExtension
{
private readonly string _databaseName;
public DataAccessDependencies(string databaseName)
{
_databaseName = databaseName;
}
protected override void Initialize()
{
IConfigurationBuilder configurationBuilder = Container.Resolve<IConfigurationBuilder>();
Container.RegisterType<ClientContext>(new SessionLifetimeManager(), new InjectionConstructor(configurationBuilder.GetConnectionString(_databaseName)));
Container.RegisterType<IRepository<ClientContext>, RepositoryService<ClientContext>>(new SessionLifetimeManager());
}
}
// SessionLifetimeManager.cs
public class SessionLifetimeManager : LifetimeManager
{
private readonly string _key = Guid.NewGuid().ToString();
public override void RemoveValue(ILifetimeContainer container = null)
{
HttpContext.Current.Session.Remove(_key);
}
public override void SetValue(object newValue, ILifetimeContainer container = null)
{
HttpContext.Current.Session[_key] = newValue;
}
public override object GetValue(ILifetimeContainer container = null)
{
return HttpContext.Current.Session[_key];
}
protected override LifetimeManager OnCreateLifetimeManager()
{
return new SessionLifetimeManager();
}
}
This works fine as long as only one user is logged in at a time. The data is fetched properly, the dashboards work as expected, and everything's just peachy keen.
Then, as soon as a second user logs in, disaster strikes.
The last user to have prompted a call to RegisterUserDataAccess seems to always have "priority"; their data is displayed on the dashboard, and nothing else. Whether this is initiated by a log-in, or through a database access selection in my web application that calls the same method to re-route the user's connection to another database they have permission to access, the last one to draw always imposes their data on all other users of the web application. If I understand correctly, this is a problem the SessionLifetimeManager was supposed to solve - unfortunately, I really can't seem to get it to work.
I sincerely doubt that a simple and common use-case like this - multiple users logged into an MVC application who each are supposed to access their own, separate data - is beyond the abilities of Unity, so obviously, I must be doing something very wrong here. Having spent most of my day searching through depths of the internet I wasn't even sure truly existed, I must, unfortunately, now realize that I am at a total and utter loss here.
Has anyone dealt with this issue before? Has anyone dealt with this use-case before, and if yes, can anyone tell me how to change my approach to make this a little less headache-inducing? I am utterly desperate at this point and am considering rewriting my entire data access methodology just to make it work - not the healthiest mindset for clean and maintainable code.
Many thanks.
the issue seems to originate from your registration call, when registering the same type multiple times with unity, the last registration call wins, in this case, that will be data access object for whoever user logs-in last. Unity will take that as the default registration, and will create instances that have the connection to that user's database.
The SessionLifetimeManager is there to make sure you get only one instance of the objects you resolve under one session.
One option to solve this is to use named registration syntax to register the data-access types under a key that maps to the logged-in user (could be the database name), and on the resolve side, retrieve this user key, and use it resolve the corresponding data access implementation for the user
Thank you, Mohammed. Your answer has put me on the right track - I ended up finally solving this using a RepositoryFactory which is instantiated in an InjectionFactory during registration and returns a repository that always wraps around a ClientContext pointing to the currently logged on user's currently selected database.
// DataAccessDependencies.cs
protected override void Initialize()
{
IConfigurationBuilder configurationBuilder = Container.Resolve<IConfigurationBuilder>();
Container.RegisterType<IRepository<ClientContext>>(new InjectionFactory(c => {
ClientRepositoryFactory repositoryFactory = new ClientRepositoryFactory(configurationBuilder);
return repositoryFactory.GetRepository();
}));
}
// ClientRepositoryFactory.cs
public class ClientRepositoryFactory : IRepositoryFactory<RepositoryService<ClientContext>>
{
private readonly IConfigurationBuilder _configurationBuilder;
public ClientRepositoryFactory(IConfigurationBuilder configurationBuilder)
{
_configurationBuilder = configurationBuilder;
}
public RepositoryService<ClientContext> GetRepository()
{
var connectionString = _configurationBuilder.GetConnectionString(UserData.Current.CurrentPermission);
ClientContext ctx = new ClientContext(connectionString);
RepositoryService<ClientContext> repository = new RepositoryService<ClientContext>(ctx);
return repository;
}
}
// UserData.cs (multiton-singleton-hybrid)
public static UserData Current
{
get
{
var currentAADUID = (string)(HttpContext.Current.Session["currentAADUID"]);
return Get(currentAADUID);
}
}
public static UserData Get(string AADUID)
{
UserData instance;
lock(_instances)
{
if(!_instances.TryGetValue(AADUID, out instance))
{
throw new UserDataNotInitializedException();
}
}
return instance;
}
public static UserData Current
{
get
{
var currentAADUID = (string)(HttpContext.Current.Session["currentAADUID"]);
return Get(currentAADUID);
}
}
public static UserData Get(string AADUID)
{
UserData instance;
lock(_instances)
{
if(!_instances.TryGetValue(AADUID, out instance))
{
throw new UserDataNotInitializedException();
}
}
return instance;
}

Is it possible to delay-load PRISM / Xamarin Forms components that aren't immediately needed?

I have the following AppDelegate which takes quite some time to load:
Syncfusion.ListView.XForms.iOS.SfListViewRenderer.Init();
new Syncfusion.SfNumericUpDown.XForms.iOS.SfNumericUpDownRenderer();
Syncfusion.SfCarousel.XForms.iOS.SfCarouselRenderer.Init();
Syncfusion.XForms.iOS.Buttons.SfSegmentedControlRenderer.Init();
Syncfusion.XForms.iOS.Buttons.SfCheckBoxRenderer.Init();
new Syncfusion.XForms.iOS.ComboBox.SfComboBoxRenderer();
//Syncfusion.XForms.iOS.TabView.SfTabViewRenderer.Init();
new Syncfusion.SfRotator.XForms.iOS.SfRotatorRenderer();
new Syncfusion.SfRating.XForms.iOS.SfRatingRenderer();
new Syncfusion.SfBusyIndicator.XForms.iOS.SfBusyIndicatorRenderer();
What options should I consider when I know some of these components aren't needed for the main screen, but for subscreens?
I am using PRISM, and it appears that every tab is pre-loaded immediately before allowing display or interaction with the end user. What can I do to delay the pre-rendering that the Prism TabView does prior to showing the interface?
Should I use Lazy<T>? What is the right approach?
Should I move these components to another initialization section?
There are a number of ways you could ultimately achieve this, and it all depends on what your real goals are.
If your goal is to ensure that you get to a Xamarin.Forms Page as fast as possible so that you have some sort of activity indicator, that in essence says to the user, "it's ok I haven't frozen, we're just doing some stuff to get ready for you", then you might try creating a "SpashScreen" page where you do additional loading. The setup might look something like the following:
public partial class AppDelegate : FormsApplicationDelegate
{
public override bool FinishedLaunching(UIApplication app, NSDictionary options)
{
global::Xamarin.Forms.Forms.Init();
LoadApplication(new App(new iOSInitializer()));
return base.FinishedLaunching(app, options);
}
}
}
public class iOSInitializer : IPlatformInitializer, IPlatformFinalizer
{
public void RegisterTypes(IContainerRegistry containerRegistry)
{
containerRegistry.RegisterInstance<IPlatformFinalizer>(this);
}
public void Finalize()
{
new Syncfusion.SfNumericUpDown.XForms.iOS.SfNumericUpDownRenderer();
Syncfusion.SfCarousel.XForms.iOS.SfCarouselRenderer.Init();
Syncfusion.XForms.iOS.Buttons.SfSegmentedControlRenderer.Init();
Syncfusion.XForms.iOS.Buttons.SfCheckBoxRenderer.Init();
}
}
public class App : PrismApplication
{
protected override async void OnInitialized()
{
await NavigationService.NavigateAsync("SplashScreen");
}
}
public class SplashScreenViewModel : INavigationAware
{
private IPlatformFinalizer _platformFinalizer { get; }
private INavigationService _navigationService { get; }
public SplashScreenViewModel(INavigationService navigationService, IPlatformFinalizer platformFinalizer)
{
_navigationService = navigationService;
_platformFinalizer = platformFinalizer;
}
public async void OnNavigatedTo(INavigationParameters navigationParameters)
{
_platformFinalizer.Finalize();
await _navigationService.NavigateAsync("/MainPage");
}
}
If you're working with Modules you could take a similar approach though any Modules that would initialize at Startup would still be making that call to Init the renderers before you've set a Page to navigate to. That said, working with Modules does give you a number of benefits here as you only ever would have to initialize things that the app actually requires at that point.
All of that said I'd be surprised if you see much in the way of gain as these Init calls are typically empty methods only designed to prevent the Linker from linking them out... if you aren't linking or have a linker file you could simply instruct the Linker to leave your Syncfusion and other libraries alone.

Simple UnityContainerExtension

I'm working on an application that is using the bbv EventBrokerExtension library. What I'm trying to accomplish is that I want to have unity register the instance that are instantiated through the container with the EventBroker. I'm planning on doing this through a UnityContainerExtension and implementing the IBuilderStrategy. The problem is that the methods for the interface seem to be called for each parameter in the constructor. The problem is when Singleton instances get resolved when building an object they will be registered multiple times.
For instance suppose you had
class Foo(ISingletonInterface singleton){}
class Foo2(ISingletonInterface singleton){}
and you resolve them via unity using
var container = new UnityContainer();
container.AddNewExtension<EventBrokerWireupStrategy>();
container.RegisterInstance<IEventBroker>(new EventBroker());
container.RegisterInstance(new Singleton());
var foo = container.Resolve<Foo>();
var foo2 = container.Resolve<Foo2>();
Then the UnityContainerExtension will call postbuildup on the same singleton object. Here is my naive implementation of UnityContainerExtension.
using Microsoft.Practices.ObjectBuilder2;
using Microsoft.Practices.Unity;
using Microsoft.Practices.Unity.ObjectBuilder;
using bbv.Common.EventBroker;
using System.Collections.Generic;
namespace PFC.EventingModel.EventBrokerExtension
{
public class EventBrokerWireupExtension : UnityContainerExtension, IBuilderStrategy
{
private IEventBroker _eventBroker;
private List<object> _wiredObjects = new List<object>();
public EventBrokerWireupExtension(IEventBroker eventBroker)
{
_eventBroker = eventBroker;
}
protected override void Initialize()
{
Context.Strategies.Add(this, UnityBuildStage.PostInitialization);
}
public void PreBuildUp(IBuilderContext context)
{
}
public void PostBuildUp(IBuilderContext context)
{
if (!_wiredObjects.Contains(context.Existing))
{
_eventBroker.Register(context.Existing);
_wiredObjects.Add(context.Existing);
}
}
public void PreTearDown(IBuilderContext context)
{
}
public void PostTearDown(IBuilderContext context)
{
}
}
}
After further investigation it appears that the problem has to do with the EventBrokerExtension. If I subscribe them in a certain order then some of them don't get registered with the event broker.
UPDATE:
Wanted to update this question really quick with the answer in case anyone else witnesses similar behavior when using the bbv EventBroker library. The behavior I was seeing was that a subscriber would get events for a while but then would stop receiving events. By design the EventBroker only maintains weak references to the publishers and subscribers that have been registered. Since the eventbroker was the only class referencing the objects they were getting garbage collected at an indeterminate time and wouldn't receive events anymore. The solution was simply to create a hard reference somewhere in the application besides the EventBroker.

Managing NHibernate sessions for a multi-tenant ASP.NET application

I have an existing multi-tenant ASP.NET application where all users authenticate against a single SQL Server database. This database also contains several other settings type data that is used within the application. Each client after authentication, utilizes their own SQL Server database for data storage, for isolation purposes. Essentially all of the client database are identical and reside on the same server, but reside on one or more servers as well.
The application is currently written in asp.net 2.5 framework and utilizes the Microsoft Practices Enterprise Library for DAL, and we are looking to migrate to 4.0 and implement NHibernate to replace the MPEL.
I have implemented a solution already using NHibernate and the 4.0 framework, so I am familiar with the concepts. I found the resources for my current session manager here as a matter of fact. But that application only had a single database, so not much too it. The implementation is essentially what you see here:
http://www.lostechies.com/blogs/nelson_montalvo/archive/2007/03/30/simple-nhibernate-example-part-4-session-management.aspx
The other solutions that I have seen suggest multiple config entries and/or files to manage this, but that is not desirable, since we may add new clients frequently and all of the connection information is already maintained in the authentication database.
Does anyone have any suggestions on how I might be able to pass in the client's connection string to a session manager?
The following is my current session manager class, which is based on the article mentioned above.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Runtime.Remoting.Messaging;
using System.Web;
using NHibernate;
using NHibernate.Cfg;
using NHibernate.Cache;
using singlepoint.timeclock.domain;
namespace singlepoint.timeclock.repositories
{
/// <summary>
/// Handles creation and management of sessions and transactions. It is a singleton because
/// building the initial session factory is very expensive. Inspiration for this class came
/// from Chapter 8 of Hibernate in Action by Bauer and King. Although it is a sealed singleton
/// you can use TypeMock (http://www.typemock.com) for more flexible testing.
/// </summary>
public sealed class nHibernateSessionManager
{
private ISessionFactory idadSessionFactory;
private ISessionFactory clientSessionFactory;
private string _client;
#region Thread-safe, lazy Singleton
// lazy initialisation, therefore initialised to null
private static nHibernateSessionManager instance = null;
/// <summary>
/// This is a thread-safe, lazy singleton. See http://www.yoda.arachsys.com/csharp/singleton.html
/// for more details about its implementation.
/// </summary>
public static nHibernateSessionManager Instance
{
get { return GetInstance(); }
}
public static nHibernateSessionManager GetInstance()
{
// lazy init.
if (instance == null)
instance = new nHibernateSessionManager();
return instance;
} // GetInstance
/// <summary>
/// Initializes the NHibernate session factory upon instantiation.
/// </summary>
private nHibernateSessionManager()
{
InitSessionFactory();
}
/// <summary>
/// Initializes the NHibernate session factory upon instantiation.
/// </summary>
private nHibernateSessionManager(string client)
{
InitSessionFactory();
InitClientSessionFactory(client);
}
/// <summary>
/// Assists with ensuring thread-safe, lazy singleton
/// </summary>
private class Nested
{
static Nested()
{
}
internal static readonly nHibernateSessionManager nHibernatenHibernateSessionManager = new nHibernateSessionManager();
}
#endregion
private void InitSessionFactory()
{
var configuration = new Configuration();
configuration.Configure(System.Configuration.ConfigurationManager.AppSettings["IDAD_HBM"]);
configuration.AddAssembly(typeof(enterprise).Assembly);
idadSessionFactory = configuration.BuildSessionFactory();
}
private void InitClientSessionFactory(string client)
{
var configuration = new Configuration();
configuration.Configure(System.Configuration.ConfigurationManager.AppSettings["Client_IDAD_HBM"]);
configuration.SetProperty("connection.connection_string", client);
configuration.AddAssembly(typeof(enterprise).Assembly);
clientSessionFactory = configuration.BuildSessionFactory();
}
/// <summary>
/// Allows you to register an interceptor on a new session. This may not be called if there is already
/// an open session attached to the HttpContext. If you have an interceptor to be used, modify
/// the HttpModule to call this before calling BeginTransaction().
/// </summary>
public void RegisterInterceptor(IInterceptor interceptor)
{
ISession session = ThreadSession;
if (session != null && session.IsOpen)
{
throw new CacheException("You cannot register an interceptor once a session has already been opened");
}
GetSession(interceptor);
}
public ISession GetSession()
{
return GetSession(null);
}
/// <summary>
/// Gets a session with or without an interceptor. This method is not called directly; instead,
/// it gets invoked from other public methods.
/// </summary>
private ISession GetSession(IInterceptor interceptor)
{
ISession session = ThreadSession;
if (session == null)
{
if (interceptor != null)
{
session = idadSessionFactory.OpenSession(interceptor);
}
else
{
session = idadSessionFactory.OpenSession();
}
ThreadSession = session;
}
return session;
}
public void CloseSession()
{
ISession session = ThreadSession;
ThreadSession = null;
if (session != null && session.IsOpen)
{
session.Close();
}
}
public void BeginTransaction()
{
ITransaction transaction = ThreadTransaction;
if (transaction == null)
{
transaction = GetSession().BeginTransaction();
ThreadTransaction = transaction;
}
}
public void CommitTransaction()
{
ITransaction transaction = ThreadTransaction;
try
{
if (transaction != null && !transaction.WasCommitted && !transaction.WasRolledBack)
{
transaction.Commit();
ThreadTransaction = null;
}
}
catch (HibernateException ex)
{
RollbackTransaction();
throw ex;
}
}
public void RollbackTransaction()
{
ITransaction transaction = ThreadTransaction;
try
{
ThreadTransaction = null;
if (transaction != null && !transaction.WasCommitted && !transaction.WasRolledBack)
{
transaction.Rollback();
}
}
catch (HibernateException ex)
{
throw ex;
}
finally
{
CloseSession();
}
}
/// <summary>
/// If within a web context, this uses <see cref="HttpContext" /> instead of the WinForms
/// specific <see cref="CallContext" />. Discussion concerning this found at
/// http://forum.springframework.net/showthread.php?t=572.
/// </summary>
private ITransaction ThreadTransaction
{
get
{
if (IsInWebContext())
{
return (ITransaction)HttpContext.Current.Items[TRANSACTION_KEY];
}
else
{
return (ITransaction)CallContext.GetData(TRANSACTION_KEY);
}
}
set
{
if (IsInWebContext())
{
HttpContext.Current.Items[TRANSACTION_KEY] = value;
}
else
{
CallContext.SetData(TRANSACTION_KEY, value);
}
}
}
/// <summary>
/// If within a web context, this uses <see cref="HttpContext" /> instead of the WinForms
/// specific <see cref="CallContext" />. Discussion concerning this found at
/// http://forum.springframework.net/showthread.php?t=572.
/// </summary>
private ISession ThreadSession
{
get
{
if (IsInWebContext())
{
return (ISession)HttpContext.Current.Items[SESSION_KEY];
}
else
{
return (ISession)CallContext.GetData(SESSION_KEY);
}
}
set
{
if (IsInWebContext())
{
HttpContext.Current.Items[SESSION_KEY] = value;
}
else
{
CallContext.SetData(SESSION_KEY, value);
}
}
}
private static bool IsInWebContext()
{
return HttpContext.Current != null;
}
private const string TRANSACTION_KEY = "CONTEXT_TRANSACTION";
private const string SESSION_KEY = "CONTEXT_SESSION";
[Obsolete("only until we can fix the session issue globally")]
internal ISession OpenSession()
{
return idadSessionFactory.OpenSession();
}
}
}
This is being called from a repository class like so:
public string getByName(string name)
{
return getByName(nHibernateSessionManager.Instance.GetSession(), name);
}
What I would really like to be able to do is the following:
public string getByName(string name, string clientConnectionString)
{
return getByName(nHibernateSessionManager.Instance.GetSession(clientConnectionString), name);
}
But I am having trouble modifying my existing session manager to accomodate this.
You appear to be asking to swap a connection for a given session. Or rather that is certainly what the code you have written is asking - "return a session identified by the name parameter, and it should also now use the connection string provided by this method."
That is not possible. NHibernate builds a session (and actually really a session factory) per connection and once built the factory and session are immutable. You cannot change connections for an existing session.
I got the impression that your application involves mostly in initial connection string that is the moving target, but after that your "real" session is on a single database. If that is the case, NHibernate can easily do this. If that is not the case, well, some things NHibernate is just not that well suited for. Maybe understanding a little more about the basis NHibernate operates on is helpful either way?
One of my genuine criticisms of NHibernate is that you have a somewhat arcane use of terminology and the well known unhelpful nature of it's exception messages. These coupled with the fact that what it is doing is in reality mechanically complicated tends to really obscure that there is a relatively simple and technically sound underlying model.
In this case, if you think about it this business of an immutable session makes a lot of sense. NHibernate connects to a database, but it also maintains objects in the session so they can be persisted back to that database at a later time. NHibernate does not support changing connections per session because there may already be other objects in the session and if you change connections their persistence is no longer assured.
Now, what you can do is create a factory/session per database for multiple databases and access them in one application, but objects still belong to their own session. You can even move objects to a new session with a different connection. In this case you have what would logically be a "replication" scenario. NHibernate supports this but you have to do most of the work. This also makes sense - they really cannot give you that as stable out of the box functionality, you have to manage a process like that on your own.
You can also build code to do exactly what you are asking. But think about what that is. Make a session, not per database, but only for this specific instance of this specific repository. I am thinking that is most likely not really what you want. But that is exactly what the semantics of your request are saying to do. Your existing class, On the other hand, was built on different semantics which are more typically what people want - "Build a session for this particular connection definition, i.e this database."
A real need to inject a connection string at the repository level implies that now not only is the database a moving target, but at the actual table level the target also moves. If that is the case, NHibernate is possibly not a good option. If that is not the case, you may be trying to mix programming paradigms. NHiberate imposes a few what I would call "assumptions" rather than any sort of real "limitations" and in return you don't have to write a bunch of code that would allow you a finer grain of control because often you really don't need that additional control.
Sorry if this is no longer a direct answer to your question, hopefully it is helpful somehow.
Original Answer:
Sure, since the connection info is in the authentication database this is easy.
1) Configure NHibernate in the "usual" fashion and point the config at the authentication database. Get the db connection for the user, and then close that session and session factory. You are done with that one now.
2) Create a new session etc this time in code instead of a config file.
class MyNewSession
{
private ISession _session;
private ISessionFactory _factory;
public void InitializeSession()
{
NHibernate.Cfg.Configuration config = new NHibernate.Cfg.Configuration();
config.Properties.Clear();
IDictionary props = new Hashtable();
// Configure properties as needed, this is pretty minimal standard config here.
// Can read in properties from your own xml file or whatever.
// Just shown hardcoded here.
props["proxyfactory.factory_class"] = "NHibernate.ByteCode.Castle.ProxyFactoryFactory, NHibernate.ByteCode.Castle";
props["connection.provider"] = "NHibernate.Connection.DriverConnectionProvider";
props["dialect"] = "NHibernate.Dialect.MsSql2000Dialect";
props["connection.driver_class"] = "NHibernate.Driver.SqlClientDriver";
props["connection.connection_string"] = "<YOUR CONNECTION STRING HERE>";
props["connection.isolation"] = "ReadCommitted";
foreach (DictionaryEntry de in props)
{
config.Properties.Add(de.Key.ToString(), de.Value.ToString());
}
// Everything from here on out is the standard NHibernate calls
// you already use.
// Load mappings etc, etc
// . . .
_factory = config.BuildSessionFactory();
_session = _factory.OpenSession();
}
}
I know this is old but if you have not found a solution I hope this will help,
I created a solution that uses multisessionfactory using unhaddins (I made alterations to suit my needs).
Basically the multisession factory creates session factories for each database and stores in Application object.
Depending on the client the call to getfactory("name of factory from factory config file") returns correct database to query on.
You will have to alter your management module to support this and all of your repositories to support the change in management. This may be impractical at first but you have to alter them anyway. Your calls from your repository can be something like this:
public string getByName(string name)
{
return getByName(nHibernateSessionManager.SessionFactoryManager.GetFactory(Session["session variable that holds client session factory name that was set on login"]).GetCurrentSession(), name);
}
or (creating a method in the sessionmanager to return session of a given factory) your code could be like this
public string getByName(string name)
{
return getByName(nHibernateSessionManager.GetSession(Session["session variable that holds client session factory name that was set on login"]), name);
}

Resources