By default, the solution generated from Sharp Architecture's templify package configures NHibernate using an NHibernate.config file in the {SolutionName}.Web project. I would like to replace it with a fluent configuration of my own and still have the rest of Sharp Architecture work correctly.
Any help will be much appreciated. :)
Solution: Here's how I got it to work:
IPersistenceConfigurer configurer = OracleClientConfiguration.Oracle10
.AdoNetBatchSize(500)
.ShowSql()
.ConnectionString(c => c.FromConnectionStringWithKey("NHibernate.Localhost"))
.DefaultSchema("MySchema")
.ProxyFactoryFactory("NHibernate.ByteCode.Castle.ProxyFactoryFactory, NHibernate.ByteCode.Castle")
.UseReflectionOptimizer();
NHibernateSession.Init(
webSessionStorage,
new string[] { Server.MapPath("~/bin/MyProject.Data.dll") },
new AutoPersistenceModelGenerator().Generate(),
null,
null,
null,
configurer);
iirc the NhibernateSession class that is used to configure nhibernate has a bunch of overloads one of them giving you the ability to configure it via code.
Very old post. I'll leave it here in case someone else is interested. On SharpArch 1.9.6.0 you can add two methods to NHibernateSession.cs. This will let you pass-in a FluentConfiguration object.
public static FluentConfiguration Init(ISessionStorage storage, FluentConfiguration fluentConfiguration)
{
InitStorage(storage);
try
{
return AddConfiguration(DefaultFactoryKey, fluentConfiguration);
}
catch
{
// If this NHibernate config throws an exception, null the Storage reference so
// the config can be corrected without having to restart the web application.
Storage = null;
throw;
}
}
private static FluentConfiguration AddConfiguration(string defaultFactoryKey, FluentConfiguration fluentConfiguration)
{
var sessionFactory = fluentConfiguration.BuildSessionFactory();
Check.Require(!sessionFactories.ContainsKey(defaultFactoryKey),
"A session factory has already been configured with the key of " + defaultFactoryKey);
sessionFactories.Add(defaultFactoryKey, sessionFactory);
return fluentConfiguration;
}
Related
I have spent over two weeks searching google, bing, stack overflow, and msdn docs trying to figure out how to do a proper dependency injection for a mobile app that I am developing. To be clear, I do DI every day in web apps. I do not need a crash course on what, who, and why DI is important. I know it is, and am always embracing it.
What I need to understand is how this works in a mobile app world, and in particular a UWP Template 10 Mobile app.
From my past, in a .net/Asp app I can "RegisterType(new XYZ).Singleton() blah" {please forgive syntax; just an example} in App_Start.ConfigureServices. This works almost identical in .netcore, granted some syntactic changes.
My problem is now I am trying to provide my api is going to an UWP app that needs to digest my IXYZ service. By no means do I think that they should "new" up an instance every time. There has to be a way to inject this into a container on the UWP side; and I feel I am missing something very simple in the process.
Here is the code I have:
App.xaml.cs
public override async Task OnStartAsync(StartKind startKind, IActivatedEventArgs args)
{
// TODO: add your long-running task here
//if (args.Kind == ActivationKind.LockScreen)
//{
//}
RegisterServices();
await NavigationService.NavigateAsync(typeof(Views.SearchCompanyPage));
}
public static IServiceProvider Container { get; private set; }
private static void RegisterServices()
{
var services = new ServiceCollection();
services.AddSingleton<IXYZ, XYZ>();
Container = services.BuildServiceProvider();
}
MainPage.xaml.cs:
public MainPage()
{
InitializeComponent();
NavigationCacheMode = NavigationCacheMode.Enabled;
}
MainPageViewModel:
public class MainPageViewModel : ViewModelBase
{
private readonly IXYZ _xyz;
public MainPageViewModel(IXYZ xyz)
{
//Stuff
_xyz= xyz;
}
}
I now get the error:
XAML MainPage...ViewModel type cannot be constructed. In order to be constructed in XAML, a type cannot be abstract, interface nested generic or a struct, and must have a public default constructor.
I am willing to use any brand of IoC Container, but what I need is an example of how to properly use DI for services in a UWP app. 99.9% of questions about DI is about Views (i.e. Prism?) not just a simple DI for a service (i.e. DataRepo; aka API/DataService).
Again, I feel I am missing something obvious and need a nudge in the right direction. Can somebody show me an example project, basic code, or a base flogging on how I should not be a programmer...please don't do that (I don't know if my ego could take it).
You can try to Microsoft.Hosting.Extensions just like ASP.NET, there's an implementation on Xamarin.Forms by James Montemagno, as well it can be used in UWP I have tried and it works perfectly. You have to change some parts in order to get it working.
In OnLaunched Method add Startup.Init();
public static class Startup
{
public static IServiceProvider ServiceProvider { get; set; }
public static void Init()
{
StorageFolder LocalFolder = ApplicationData.Current.LocalFolder;
var configFile = ExtractResource("Sales.Client.appsettings.json", LocalFolder.Path);
var host = new HostBuilder()
.ConfigureHostConfiguration(c =>
{
// Tell the host configuration where to file the file (this is required for Xamarin apps)
c.AddCommandLine(new string[] { $"ContentRoot={LocalFolder.Path}" });
//read in the configuration file!
c.AddJsonFile(configFile);
})
.ConfigureServices((c, x) =>
{
// Configure our local services and access the host configuration
ConfigureServices(c, x);
}).
ConfigureLogging(l => l.AddConsole(o =>
{
//setup a console logger and disable colors since they don't have any colors in VS
o.DisableColors = true;
}))
.Build();
//Save our service provider so we can use it later.
ServiceProvider = host.Services;
}
static void ConfigureServices(HostBuilderContext ctx, IServiceCollection services)
{
//ViewModels
services.AddTransient<HomeViewModel>();
services.AddTransient<MainPageViewModel>();
}
static string ExtractResource(string filename, string location)
{
var a = Assembly.GetExecutingAssembly();
using (var resFilestream = a.GetManifestResourceStream(filename))
{
if (resFilestream != null)
{
var full = Path.Combine(location, filename);
using (var stream = File.Create(full))
{
resFilestream.CopyTo(stream);
}
}
}
return Path.Combine(location, filename);
}
}
Injecting a ViewModel is possible as well which is pretty nice.
With help from #mvermef and the SO question Dependency Injection using Template 10 I found a solutions. This turned out to be a rabbit hole where at every turn I ran into an issue.
The first problem was just getting Dependency Injection to work. Once I was able to get that figured out from the sources above I was able to start injecting my services into ViewModels and setting them to the DataContext in the code behind.
Then I ran into an injection issue problem with injecting my IXYZ services into the ViewModels of UserControls.
Pages and their ViewModels worked great but I had issues with the DataContext of the UserControl not being injected with UserControl's ViewModel. They were instead getting injected by the Page's ViewModel that held it.
The final solution turned out to be making sure that the UserControl had the DataContext being set in XAML not the code behind, as we did with the Pages, and then creating a DependencyProperty in the code behind.
To show the basic solution read below.
To make it work I started with:
APP.XAML.CS
public override async Task OnStartAsync(StartKind startKind, IActivatedEventArgs args)
{
// long-running startup tasks go here
RegisterServices();
await Task.CompletedTask;
}
private static void RegisterServices()
{
var services = new ServiceCollection();
services.AddSingleton<IRepository, Repository>();
services.AddSingleton<IBinderService, BinderServices>();
**//ViewModels**
**////User Controls**
services.AddSingleton<AddressesControlViewModel, AddressesControlViewModel>();
services.AddSingleton<CompanyControlViewModel, CompanyControlViewModel>();
**//ViewModels**
**////Pages**
services.AddSingleton<CallListPageViewModel, CallListPageViewModel>();
services.AddSingleton<CallListResultPageViewModel, CallListResultPageViewModel>();
etc....
Container = services.BuildServiceProvider();
}
public override INavigable ResolveForPage(Page page, NavigationService navigationService)
{
**//INJECT THE VIEWMODEL FOR EACH PAGE**
**//ONLY THE PAGE NOT USERCONTROL**
if (page is CallListPage)
{
return Container.GetService<CallListPageViewModel>();
}
if (page is CallListResultPage)
{
return Container.GetService<CallListResultPageViewModel>();
}
etc...
return base.ResolveForPage(page, navigationService);
}
In the code behind for the Page
CALLLISTPAGE.XAML.CS
public CallListPage()
{
InitializeComponent();
}
CallListPageViewModel _viewModel;
public CallListPageViewModel ViewModel
{
get { return _viewModel ?? (_viewModel = (CallListPageViewModel)DataContext); }
}
In your XAML add your UserControl
CALLLISTPAGE.XAML
<binder:CompanyControl Company="{x:Bind ViewModel.SelectedCompany, Mode=TwoWay}"/>
In your UserControl make sure to add the DataContext to the XAML NOT the code behind like we did with the pages.
COMPANYCONTROL.XAML
<UserControl.DataContext>
<viewModels:CompanyControlViewModel x:Name="ViewModel" />
</UserControl.DataContext>
In the UserControl Code Behind add a Dependency Property
COMPANYCONTROL.XAML.CS
public static readonly DependencyProperty CompanyProperty = DependencyProperty.Register(
"Company", typeof(Company), typeof(CompanyControl), new PropertyMetadata(default(Company), SetCompany));
public CompanyControl()
{
InitializeComponent();
}
public Company Company
{
get => (Company) GetValue(CompanyProperty);
set => SetValue(CompanyProperty, value);
}
private static void SetCompany(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var control = d as CompanyControl;
var viewModel = control?.ViewModel;
if (viewModel != null)
viewModel.Company = (Company) e.NewValue;
}
In the end I am not sure if this is an elegant solution but it works.
I twist myself around a workable solution to use several databases in RavenDB for an ASP.Net MVC app using Castle Windsor for the wiring.
This is the current installer
public class RavenInstaller : IWindsorInstaller
{
public void Install(IWindsorContainer container, IConfigurationStore store)
{
container.Register(
Component.For<IDocumentStore>().Instance(CreateDocumentStore()).LifeStyle.Singleton,
Component.For<IDocumentSession>().UsingFactoryMethod(GetDocumentSesssion).LifeStyle.PerWebRequest
);
}
static IDocumentStore CreateDocumentStore()
{
var store = new DocumentStore { ConnectionStringName = "RavenDb_CS9" };
store.Initialize();
IndexCreation.CreateIndexes(typeof(Users).Assembly, store);
return store;
}
static IDocumentSession GetDocumentSesssion(IKernel kernel)
{
var store = kernel.Resolve<IDocumentStore>();
return store.OpenSession();
}
}
The above works perfect but only for one Database.
I can't find the proper thinking how to handle another database. The whole chain starts with a domain service asking for an IDocumentSession. Then the flow is as specified in the above installer. But where/how do I ask for a "SessionToDb1" or a "SessionToDb2"?
The important is of course what connection string to use (where the DB property is specified) but also what indexes to create in respective DB / DocumentStore.
Did anyone accomplish this using Windsor? Am I thinking/attacking it wrong here?
Thanks!
Because you have:
Component.For<IDocumentSession>()
.UsingFactoryMethod(GetDocumentSesssion)
.LifeStyle.PerWebRequest
Your GetDocumentSession method is going to be called any time you inject an IDocumentSession. This is good.
When working with multiple databases, you need to pass the database name as a parameter to OpenSession. So, you need some way to resolve which database you would like to connect to based on the current web request.
You need to modify the GetDocumentSession method to implement whatever custom logic you are going to use. For example, you may want to look at a cookie, asp.net session item, current thread principal, or some other criteria. The decision is custom to your application, all that matters is somehow you open the session with the correct database name.
I've run into this problem before with nhibernate.
I found the best solution is to create a SessionManager class which wraps the Creation of the document store and the Session..
So I.E.
public interface ISessionManager
{
void BuildDocumentStore();
IDocumentSession OpenSession();
}
public interface ISiteSessionManager : ISessionManager
{
}
public class SiteSessionManager : ISiteSessionManager
{
IDocumentStore _documentStore;
public SiteSessionManager()
{
BuildDocumentStore();
}
public void BuildDocumentStore()
{
_documentStore = new DocumentStore
{
Url = "http://localhost:88",
DefaultDatabase = "test"
};
_documentStore.Initialize();
IndexCreation.CreateIndexes(typeof(SiteSessionManager).Assembly, _documentStore);
}
public IDocumentSession OpenSession()
{
return _documentStore.OpenSession();
}
}
// And then!.
Container.Register(Component.For<ISiteSessionManager>().Instance(new SiteSessionManager()).LifestyleSingleton());
// And then!.
public class FindUsers
{
readonly ISiteSessionManager _siteSessionManager;
public FindUsers(ISiteSessionManager siteSessionManager)
{
_siteSessionManager = siteSessionManager;
}
public IList<User> GetUsers()
{
using (var session = _siteSessionManager.OpenSession())
{
// do your query
return null;
}
}
}
Rinse and repeat for multiple databases.!
I am using Castle to create my database context based on a given interface. I have the following code in my Installer class and this works fine at the moment.
private ConfigureDelegate ConfigureContext()
{
return p => p.Named(p.ServiceType.Name)
.LifeStyle.PerWebRequest
.DependsOn(new { connectionString = ConfigurationManager.ConnectionStrings["conStringName"].ConnectionString });
}
However i now have a scenario where this installer will find more than one concrete implementation of my interface, where each one should have a different connection string supplied.
Is this possible - if so, could someone point me in the right direction.
TIA
Yes, it's possible if you can write a piece of code that provides the connection string name for the service. Perhaps something like this:
private ConfigureDelegate ConfigureContext()
{
return p => p.Named(p.ServiceType.Name)
.LifeStyle.PerWebRequest
.DependsOn(new
{
connectionString =
ConfigurationManager
.ConnectionStrings[GetConnectionName(p.ServiceType.Name)]
.ConnectionString
});
}
private string GetConnectionName(string serviceName)
{
// return the connection name
}
I have a Data Access Layer, a Service Layer, and a Presentation Layer. The Presentation Layer is ASP.NET MVC2 RTM (web), and the Service Layer is WCF (services). It's all .NET 3.5 SP1.
The problem is that in the services, the objects being returned are marked with the [DataContract] attribute. The web is using the AppFabric Cache (a.k.a Velocity) SessionStateProvider to store session state. Due to this, anything I store in the session must be serializable.
Here comes the problem: the DataContracts aren't marked with [Serializable] and as far as I can remember, by introducing it onto a class already marked with [DataContract] some issues arise, and so I don't believe this is a solution.
I was initially planning on using the DataContracts right in the web layer, using them as models to views related to rendering the DataContracts (probably nested inside a higher level ViewModel class). But due to the session state provider requiring all objects stored inside it to be serializable, I'm starting to rethink this strategy. It would be nice to have though, since they contain validation logic using the IDataErrorInfo interface, and the same validation logic could be re-used in MVC as part of model binding.
What do you believe is the best way to allow me to reduce the work needed?
I've currently thought of the following different ways:
A. Create a 'ServiceIntegration' part in the web project.
This would be a middle man between my controllers and my WCF service layer. The ServiceIntegration part would speak to the service layer using DataContracts, and to the Web layer using ViewModels, but would have to transform between the DataContracts and ViewModels using a two-way Transformer.
Also, since the IDataErrorInfo Validation wouldn't be re-usable, it would be necessary to create a Validator per DataContract too, that uses the Transformer to convert from ViewModel to DataContract, perform validation using IDataErrorInfo and return its results. This would then be used inside action methods of Controllers (e.g. if (!MyValidator.IsValid(viewModel)) return View();)
Different classes required: xDataContract, xViewModel, xTransformer, xValidator
B. Create a 'SessionIntegration' part in the web project
This would be a middle-man between the controllers (or anything accessing the session) and the session itself. Anything requiring access to the session would go through this class. DataContracts would be used in the entire application, unless they are being stored into the session. The SessionIntegration part would take the responsibility of transforming the DataContract to some ISerializable form, and back. No additional Validator is needed because of the use of of IDataErrorInfo interface on the DataContract.
Different classes required: xDataContract, xTransformer, xSerializableForm
Note: there would still be ViewModels around in both scenarios, however with (B) I'd be able to compose ViewModels from DataContracts.
(B) has the benefit of not needing an extra validator.
Before I go off and implement (A)/(B) fully, I'd like some feedback. At the moment, I'm starting to lean towards (B), however, (A) might be more flexible. Either way, it seems like way too much work for what it's worth. Has anyone else come across this problem, do you agree/disagree with me, and/or do you have any other way of solving the problem?
Thanks,
James
Without going the full blown route of A or B, could you just make a generic ISerializable wrapper object and put those in your SessionState?
[Serializable]
public class Wrapper : ISerializable
{
public object Value { get; set; }
void ISerializable.GetObjectData(SerializationInfo info, StreamingContext context)
{
if (Value != null)
{
info.AddValue("IsNull", false);
if (Value.GetType().GetCustomAttributes(typeof(DataContractAttribute), false).Length == 1)
{
using (var ms = new MemoryStream())
{
var serializer = new DataContractSerializer(Value.GetType());
serializer.WriteObject(ms, Value);
info.AddValue("Bytes", ms.ToArray());
info.AddValue("IsDataContract", true);
}
}
else if (Value.GetType().IsSerializable)
{
info.AddValue("Value", Value);
info.AddValue("IsDataContract", false);
}
info.AddValue("Type", Value.GetType());
}
else
{
info.AddValue("IsNull", true);
}
}
public Wrapper(SerializationInfo info, StreamingContext context)
{
if (!info.GetBoolean("IsNull"))
{
var type = info.GetValue("Type", typeof(Type)) as Type;
if (info.GetBoolean("IsDataContract"))
{
using (var ms = new MemoryStream(info.GetValue("Bytes", typeof(byte[])) as byte[]))
{
var serializer = new DataContractSerializer(type);
Value = serializer.ReadObject(ms);
}
}
else
{
Value = info.GetValue("Value", type);
}
}
}
}
As an extension to the provided answer, I added these two methods to ease storing/retrieving the data.
public static void Set<T>(HttpSessionStateBase session, string key, T value)
{
session[key] = new Wrapper(value);
}
public static T Get<T>(HttpSessionStateBase session, string key)
{
object value = session[key];
if (value != null && typeof(T) == value.GetType())
{
return (T) value;
}
Wrapper wrapper = value as Wrapper;
return (T) ((wrapper == null) ? null : wrapper.Value);
}
This makes it a little easier to set/get values from the session:
MyDataContract c = ...;
Wrapper.Set(Session, "mykey", c);
c = Wrapper.Get<MyDataContract>(Session, "mykey");
To make it even easier, add extension methods:
public static class SessionWrapperEx
{
public static void SetWrapped<T>(this HttpSessionStateBase session, string key, T value)
{
Wrapper.Set<T>(session, key, value);
}
public static T GetWrapped<T>(this HttpSessionStateBase session, string key)
{
return Wrapper.Get<T>(session, key);
}
}
And use as below:
MyDataContract c = ...;
Session.SetWrapped("mykey", c);
c = Session.GetWrapped<MyDataContract>("mykey");
I have an ASP.NET MVC app which depends on a lot of settings (name-value pairs), I am planning to store this information in a database table called SiteSettings. Is there an easy way in which I can get these settings using NHibernate. And what are the best practices when saving settings for a web application. And by settings I mean the settings which control the flow of processes in the web application and which are governed by business rules. These are not the typical connection string kind of settings. I was unable to get much information on the web on this topic. Maybe I am not searching on the right keywords, Any help will be greatly appreciated.
I can't answer in the context of nhibernate (which I'm not using) or best practices (I came up with this on my own recently). However, it works well for me, and will probably work for you.
I have a table (Biz_Config) in the database to store business preferences. (I've created a web.config section for what I call IT preferences.)
I have a class that is in charge of managing the biz preferences. The constructor grabs the entire table (one row per setting) and copies these into a dictionary, and it has methods to access (such as bizconfig.get("key")) and update this dictionary, also updating the table at the same time. It also has a few shortcut properties for specific dictionary values, especially where the value has to be cast (I have a few important numbers). It works quite well.
In order to be more efficient and not instantiate it every time I need a setting, and also to access it easily from my controllers and views, I created a static class, Globals, that is in charge of getting things out of the session or application variables. For the biz config object, it checks the application variable and, if null, creates a new one. Otherwise it just returns it. Globals is part of my helpers namespace, which is included in my web.config to be available to my views. So I can easily call:
<% Globals.Biz_Config.Get("key") %>
I hope this helps. If you'd like code, I can dig that up for you.
James
If you have a set of key/value pairs, you probably want to use a <map>. See the official NHibernate documentation or Ayende's post about 'NHibernate Mapping - <map/>'.
I have come up with a solution which is quite similar to the one suggested by James. I have an SiteSettingsService class which manages the settings for the whole site, it has a simple dependency on an interface called ISiteServiceRepository. This might not be the most elegant solution, But it is working perfectly for me. I have also configured the SiteSettingsService class as a Singleton using StructureMap. So, it saves me unnecessary instantiantion every time I need any settings.
//ISiteServiceRepository, an implementation of this uses NHibernate to do just two things
//i)Get all the settings, ii)Persist all the settings
using System.Collections.Generic;
using Cosmicvent.Mcwa.Core.Domain.Model;
namespace Cosmicvent.Mcwa.Core.Domain {
public interface ISiteServiceRepository {
IList<Setting> GetSettings();
void PersistSettings(IDictionary<string, string> settings);
}
}
//The main SiteSettingsService class depends on the ISiteServiceRepository
using System;
using System.Collections.Generic;
using Cosmicvent.Mcwa.Core.Domain;
using Cosmicvent.Mcwa.Core.Domain.Model;
namespace Cosmicvent.Mcwa.Core.Services {
public class SiteSettingsService : ISiteSettingsService {
private readonly ISiteServiceRepository _siteServiceRepository;
private IDictionary<string, string> _settings;
public SiteSettingsService(ISiteServiceRepository siteServiceRepository) {
_siteServiceRepository = siteServiceRepository;
//Fill up the settings
HydrateSettings();
}
public int ActiveDegreeId {
get {
return int.Parse(GetValue("Active_Degree_Id"));
}
}
public string SiteTitle {
get { return GetValue("Site_Title"); }
}
public decimal CounsellingFee {
get { return decimal.Parse(GetValue("Counselling_Fee")); }
}
public decimal TuitionFee {
get { return decimal.Parse(GetValue("Tuition_Fee")); }
}
public decimal RegistrationFee {
get { return decimal.Parse(GetValue("Registration_Fee")); }
}
public void UpdateSetting(string setting, string value) {
if (!string.IsNullOrEmpty(setting) && !string.IsNullOrEmpty(value)) {
SetValue(setting, value);
PersistSettings();
}
}
//Helper methods
private void HydrateSettings() {
_settings = new Dictionary<string, string>();
IList<Setting> siteRepoSettings = _siteServiceRepository.GetSettings();
if (siteRepoSettings == null) {
throw new ArgumentException("Site Settings Repository returned a null dictionary");
}
foreach (Setting setting in siteRepoSettings) {
_settings.Add(setting.Name.ToUpper(), setting.Value);
}
}
private string GetValue(string key) {
key = key.ToUpper();
if (_settings == null) {
throw new NullReferenceException("The Site Settings object is Null");
}
if (!_settings.ContainsKey(key)) {
throw new KeyNotFoundException(string.Format("The site setting {0} was not found", key));
}
return _settings[key];
}
private void SetValue(string key, string value) {
key = key.ToUpper();
if (_settings == null) {
throw new NullReferenceException("The Site Settings object is Null");
}
if (!_settings.ContainsKey(key)) {
throw new KeyNotFoundException(string.Format("The site setting {0} was not found", key));
}
_settings[key] = value;
}
private void PersistSettings() {
_siteServiceRepository.PersistSettings(_settings);
}
}
}
Hope this helps future developers facing similar problems. Any suggestions for improving this are more than welcome.