I have the Membase server installed with a couple buckets setup and I was looking for a good tutorial or example of how to use this as the 2nd level cache with NHibernate.
I am interested in what a sample configuration would look like and if there is anything I need to do in code or if I can handle it all from my NHibernate mappings.
Thanks for any assistance.
In your mapping files, you will need to include the property:
<class name="ClassName" table="Table">
<cache usage="read-write" />
<!-- SNIP -->
</class>
Options are read-write (read committed isolation), nonstrict-read-write (objects that are rarely written, better performance but increased chance of stale data), or read-only (data that never changes).
Then, in your web (or app) config you need a section to configure memcached:
<configuration>
<configSections>
<!-- SNIP -->
<section name="memcache" type="NHibernate.Caches.MemCache.MemCacheSectionHandler,NHibernate.Caches.MemCache" />
</configSections>
<memcache>
<memcached host="127.0.0.1" port="11211" weight="2" />
</memcache>
<!-- SNIP -->
</configuration>
Finally, in your session factory configuration be sure to use:
<hibernate-configuration>
<session-factory>
<!-- SNIP -->
<property name="expiration">300</property> <!--memcache uses seconds -->
<property name="cache.provider_class">NHibernate.Caches.MemCache.MemCacheProvider,NHibernate.Caches.MemCache</property>
<property name="cache.use_second_level_cache">true</property>
<property name="cache.use_query_cache">false</property> <!-- true if you want to cache query results -->
</session-factory>
</hibernate-configuration>
Of course you will need to download and reference a dll from the appropriate version of NHibernate.Caches to get the right cache provider. The memcached one takes a dependency on ICSharpCode.SharpZipLib and Memcached.ClientLibrary as well (s/b included in the download)
If you're using fluent NHibernate, there is a .Cache method in the setup chain for a session factory that you can use, though some of the properties need to be set manually through a call to .ExposeConfiguration.
Related
I've inherited an app that uses NHibernate and FluentNibernate to connect to an Oracle database. Unfortunately, I have no experience with NHibernate. The current connection string is like so:
Data Source=(DESCRIPTION=(ADDRESS_LIST=(ADDRESS=(PROTOCOL=TCP)(HOST=hostname)(PORT=0000)))(CONNECT_DATA=(SERVER=DEDICATED)(SERVICE_NAME=servicename)));User Id={username};Password={password};
but now needs to switch over to LDAP (see the last code snippet).
My question, is it possible to make a change only to the web.config with an LDAP connection or will I need to add the Oracle.ManagedDataAccess and make some code changes?
It is my understanding that OracleClientConfiguration has been deprecated based on this page hence the part of the question about a new nuget package. Another note, there isn't a hibernate.cfg.xml file within the project (which I think is ok?) but wondering if this project isn't using the best practices for NHibernate. Any other hints or tips would be appreciated.
C# code:
private ISessionFactory GetSessionFactory()
{
var connectionString = LoginHelper.GetConnectionString(ConnectionStringSetting, LoginInfoSetting);
var iSessionFactory
= Fluently
.Configure()
.Database(OracleClientConfiguration.Oracle10.ConnectionString(connectionString))
.Mappings(e => e.FluentMappings.AddFromAssembly(Assembly.GetExecutingAssembly()))
.BuildSessionFactory();
return iSessionFactory;
}
Web.config (there no other references to NHibernate in the Web.config. There are some references to dependentAssembly in other dll.config files):
<configuration>
<configSections>
<section name="hibernate-configuration" type="NHibernate.Cfg.ConfigurationSectionHandler, NHibernate" />
<configSections>
<configuration>
What I (think I) need in the Web.config is something like so:
<oracle.dataaccess.client> <!-- or <oracle.manageddataaccess.client>? -->
<version number="*">
<LDAPsettings>
<LDAPsetting name="DIRECTORY_SERVERS" value="" />
<LDAPsetting name="DIRECTORY_SERVER_TYPE" value="" />
<LDAPsetting name="DEFAULT_ADMIN_CONTEXT" value="" />
</LDAPsettings>
<settings>
<setting name="NAMES.DIRECTORY_PATH" value="" />
<setting name="LDAP_ADMIN" value="" />
</settings>
</version>
</oracle.dataaccess.client>
EDIT 1:
Contents of ldap.ora file:
DIRECTORY_SERVERS=(ldap:XXX)
DEFAULT_ADMIN_CONTEXT="dc=Oracle,dc=com"
DIRECTORY_SERVER_TYPE=ad
Here is what I did in order to get this to work...
In the Oracle folder structure, navigate to the Network folder.
Add the following files (should be able to copy and paste another folder down called Sample):
sqlnet.ora
ldap.ora
tnsnames.ora
sqlnet.ora (the key here was adding LDAP to the beginning of the list):
SQLNET.AUTHENTICATION_SERVICES= (NTS)
NAMES.DIRECTORY_PATH= (LDAP, TNSNAMES, EZCONNECT)
ldap.ora:
DIRECTORY_SERVERS=(ldap:XXX:XXX)
DEFAULT_ADMIN_CONTEXT="dc=oracle,dc=com"
DIRECTORY_SERVER_TYPE=XX
tnsnames.ora:
No change was required; left as default
Connection string:
<add name="connection" connectionString="Data Source=YourDataSource"/>
<!-- might need to add a username/password -->
According documentation it should be this:
<LDAPsettings>
<LDAPsetting name="DIRECTORY_TYPE" value="AD" />
<LDAPsetting name="DEFAULT_ADMIN_CONTEXT" value="dc=Oracle,dc=com"/>
</LDAPsettings>
Or use
<settings>
<setting name="TNS_ADMIN" value="C:\oracle\work"/>
</settings>
and specify LDAP settings in C:\oracle\work\ldap.ora file (in conjunction with C:\oracle\work\sqlnet.ora file).
I don't think it is still valid but have a look at ODP.NET Managed library does resolve alias, but 32-bit library does
You can modify the config file with a simple text editor or use the config tool OraProvCfg.exe. Would be like this:
OraProvCfg.exe /action:config /product:odpm /frameworkversion:v4.0.30319 /providerpath:C:\oracle\product\12.1\Client_x64\odp.net\managed\common\Oracle.ManagedDataAccess.dll /set:settings\TNS_ADMIN:C:\oracle\network\admin
OraProvCfg.exe /action:config /product:odpm /frameworkversion:v4.0.30319 /providerpath:C:\oracle\product\12.1\Client_x64\odp.net\managed\common\Oracle.ManagedDataAccess.dll /set:LDAPsettings\DIRECTORY_TYPE:ad
Note, you have a OraProvCfg.exe for 32-bit (x86) and for 64-bit. Run the one which matches to your application, (or simply run both).
I have consolidated the connection string information for a number of C# .NET solutions that are in my possession. Previously, each project was storing its connection string in its own format, requiring me to modify several files for each installation of the software.
Only one remaining solution is giving me trouble. This particular solution uses Castle Windsor 2.0, ActiveRecord 2.0 and NHibernate 2.1. The code reads its configuration from an XML file. I wish to remove the connection string from the config file and set it programmatically in the code.
Here is the relevant section of code that initiates Windsor:
windsorContainer = new WindsorContainer(new XmlInterpreter(xmlFileName));
windsorContainer.Resolve<IWindsorConfigurator>().Configure(windsorContainer);
logger = windsorContainer.Resolve<ILogger>();
Here are the contents of the XML file:
<?xml version="1.0"?>
<configuration>
<properties>
<connectionString>Server=*****;Database=*****;User Id=*****;Password=*****</connectionString>
</properties>
<facilities>
<facility id="logging" type="Castle.Facilities.Logging.LoggingFacility, Castle.Facilities.Logging" loggingApi="log4net" configFile="Configs/log4net.config" />
<facility id="atm" type="Castle.Facilities.AutomaticTransactionManagement.TransactionFacility, Castle.Facilities.AutomaticTransactionManagement" />
<facility id="arfacility" type="Castle.Facilities.ActiveRecordIntegration.ActiveRecordFacility, Castle.Facilities.ActiveRecordIntegration" isDebug="false" isWeb="false">
<!-- Configure the namespaces for the models using Active Record Integration -->
<assemblies>
<item>ChronoSteril.Application</item>
</assemblies>
<config>
<add key="connection.driver_class" value="NHibernate.Driver.SqlClientDriver" />
<add key="dialect" value="NHibernate.Dialect.MsSql2005Dialect" />
<add key="connection.provider" value="NHibernate.Connection.DriverConnectionProvider" />
<add key="connection.connection_string" value="#{connectionString}" />
<add key="hibernate.cache.provider_class" value="NHibernate.Caches.SysCache.SysCacheProvider, NHibernate.Caches.SysCache" />
<add key="proxyfactory.factory_class" value="NHibernate.ByteCode.Castle.ProxyFactoryFactory, NHibernate.ByteCode.Castle" />
<add key="hibernate.expiration" value="60" />
</config>
</facility>
</facilities>
<components>
<component id="windsorConfigurator" service="ChronoSteril.Application.IWindsorConfigurator, ChronoSteril.Application" type="ChronoSteril.WinApp.ClarionIntegrationWindsorConfigurator, ChronoSteril.WinApp" />
</components>
I am not familiar with Windsor. During my Google tour, I did see some code that adds facilities programmatically, but those examples were not valid for my version of Windsor (I assume).
Question: Can anyone guide me in removing the connection string information from the XML file and allow me to set it in the code?
Thank you!
I managed to accomplish my intention. It is not ideal, but will work until the code base is rewritten. (I cannot wait to drop the existing code like a bad dream.)
Patrick's comment, under my initial question, let me to refine my search criteria, which yielded the thread located here.
My XML file remains the same, except that I use bogus values for the connection string information. I will never need to modify these, and they do not reveal any valid connection information. This was my intention. I still have not discovered how to successfully remove the ActiveRecord configuration from the XML file and configure using code.
I now call a method that contains the following code:
ISessionFactoryHolder sessionFactoryHolder = ActiveRecordMediator.GetSessionFactoryHolder();
NHibernate.Cfg.Configuration configuration = sessionFactoryHolder.GetConfiguration(typeof(ActiveRecordBase));
connectionString = ReadConnectionString();
configuration.SetProperty("connection.connection_string", connectionString);
This works for me. I hope that it can also help someone else who is in the same position as I was.
The project structure is:
Now I am trying to create a bean NHibernateSessionFactory as:
<object id="NHibernateSessionFactory" type="Spring.Data.NHibernate.LocalSessionFactoryObject, Spring.Data.NHibernate32">
<property name="DbProvider" ref="DbProvider"/>
<property name="MappingResources">
<list>
<value>EMSApplication.Domain/EMSApplication.hbm.xml</value>
</list>
</property>
<property name="HibernateProperties">
<dictionary>
<entry key="hibernate.connection.provider" value="NHibernate.Connection.DriverConnectionProvider"/>
<entry key="dialect" value="NHibernate.Dialect.MsSql2008Dialect"/>
<entry key="connection.driver_class" value="NHibernate.Driver.SqlClientDriver"/>
<entry key="proxyfactory.factory_class" value="NHibernate.Bytecode.DefaultProxyFactoryFactory, NHibernate"/>
<entry key="show_sql" value="true"/>
<entry key="hbm2ddl.auto" value="update"/>
<entry key="cache.use_query_cache" value="true"/>
</dictionary>
</property>
<property name="ExposeTransactionAwareSessionFactory" value="true" />
</object>
But I am getting the error message:
Error creating context 'spring.root': file [C:\Program Files (x86)\Common Files\Microsoft Shared\DevServer\10.0\EMSApplication.Domain\EMSApplication.hbm.xml] cannot be resolved to local file path - resource does not use 'file:' protocol.
Now the project is on D drive, I am wondering why this is pointing C drive?
Also how can I add the specified hbm file in the mapping resource? This file is set as 'Embedded Resource'.
Is there any way to specify a directory here to scan for the multiple hbm files?
Any information will be very helpful to me.
You are not using a protocol identifier (e.g. file:// or assembly://) in your resource name, so Spring uses the default file protocol. It tries to resolve the hbm file from the location where the web app is running (the devserver path), not where it's files are stored, which you seem to expect.
To reference a file in your website use a ~ to identify the root of your web site, e.g. <value>file://~/EMSApplication.Domain/EMSApplication.hbm.xml</value>. But make sure the file is copied, which might not be the case, since you have set it to embedded resource (why?).
Consider the following improvements, they'll make your live more easy:
move the code in your folders dao, domain and NHibernate to a separate assembly (a class library project that is referenced from your web application) named Your.AssemblyName.
The session factory also supports specifying mapping assemblies to scan, so that you don't have to specify each file - see the docs on setting up a session factory using spring.net. Specify Your.AssemblyName in the mapping assembly list:
<object id="MySessionFactory" type="Spring.Data.NHibernate.LocalSessionFactoryObject, Spring.Data.NHibernate">
<property name="DbProvider" ref="DbProvider"/>
<property name="MappingAssemblies">
<list>
<value>Your.AssemblyName</value>
</list>
</property>
<property name="HibernateProperties">
<!-- snip -->
</object>
And check out the NHibernate example in the spring docs - it's very applicable to the questions you are raising here.
I'm using Spring.NET's IoC container and everything has been working just fine....until now. Somehow, in one of our previous releases, we introduced a circular dependency. Since we use setter based injection as opposed to constructor based injection, Spring.NET just kept humming along fine, but the behavior of our app changed.
Now I have a solution with a hundred or so components, and somewhere in that pile of components exists a circular dependency, which I now need to find.
Are there any tools that can take my Spring.NET config files and give me a graphical picture of my components and their dependencies?
AFAIK there isn't such a tool available, although there is one for spring for Java.
This thread on the spring.net forum
discusses the issue and proposes a solution.
I made a quick-and-dirty proof of concept based on Thomas Darimont's approach using QuickGraph.
For the following configuration file:
<?xml version="1.0" encoding="utf-8" ?>
<objects xmlns="http://www.springframework.net">
<object id="a1" type="q7446068.ClassA, q7446068" >
<property name="MyOtherA" ref="a2" />
</object>
<object id="a2" type="q7446068.ClassA, q7446068" >
<property name="MyOtherA" ref="a1" />
</object>
<object id="a3" type="q7446068.ClassA, q7446068" />
</objects>
I was able to create the following dot file:
digraph G {
0 [label="a1"];
1 [label="a2"];
2 [label="a3"];
0 -> 1 [];
1 -> 0 [];
}
Which shows the circular dependency.
The code is available as a gist.
I've got an application using Autofac at the moment. I've structured my autofac to register from the config file after the modules are loaded in. (For an xml-based override of the default behaviour).
Right now I've got a class with a bunch of basic environmental settings (a slew of public auto-properties really). This class tries to guess the default configuration based on some settings, but the idea is that each environment the app runs under would override these settings in the xml config.
Here's my problem:
Earlier in the development of this system, things were peachy. Now it seems to be completely ignoring the input of the XML Configs. I am unsure what changed that caused this to get broken, so I'm hoping someone can point out something obvious I am doing wrong.
I have verified the config file is getting read/parsed by autofac, just not applied.
web.config:
<autofac>
<files>
<file name="Config/Environment.config" section="autofac-common" />
<file name="Config/Environment.config" section="autofac-public" />
</files>
</autofac>
Environment.config:
<configuration>
<configSections>
<section name="autofac-common" type="Autofac.Configuration.SectionHandler, Autofac.Configuration"/>
<section name="autofac-public" type="Autofac.Configuration.SectionHandler, Autofac.Configuration"/>
</configSections>
<!-- Common -->
<autofac-common>
<components>
<component type="MyApp.Web.Configuration.ServerConfiguration, MyApp.Web"
service="MyApp.Web.Configuration.IServerConfiguration, MyApp.Web">
<properties>
<property name="Host" value="beta.mysite.com" />
<property name="MediaHost" value="beta.media.mysite.com" />
</properties>
</component>
</components>
</autofac-common>
<!-- Web Site -->
<autofac-public>
<modules>
<module type="MyApp.Configuration.CachingModule, MyApp">
<properties>
<property name="Disable" value="true" />
</properties>
</module>
</modules>
</autofac-public>
</configuration>
Container Building Code
var builder = new ContainerBuilder();
// Register Identifier so it's available to modules
builder.RegisterType<ServerIdentifier>()
.As<IServerIdentifier>()
.SingleInstance();
var container = builder.Build();
builder = new ContainerBuilder();
builder.RegisterModule(new CachingModule() { Disable = true });
builder.RegisterModule(new LoggerModule());
builder.RegisterModule(new InventoryModule());
builder.RegisterModule(new CatalogModule());
builder.RegisterModule(new WebModule(container));
// Override with settings from XML
builder.RegisterModule(new ConfigurationSettingsReader("autofac"));
builder.Update(container);
Autofac Version is 2.3.2.632 For .NET 3.5
The problem is in your <files> section. Since this is handled by FileElementCollection, a configuration class inheriting the NamedConfigurationElementCollection class and using the name property as its key value, multiple entries must have unique name values.
Since you are referring to the same name twice, the first entry referring to the autofac-common section is effectively overwritten by the second entry. Thus, the service will never be registered, only the CachingModule.
I cannot say whether this is new behavior on Autofac's part or not, as far as I can tell from the source, the FileElementCollection class was introduced in 2.0 and haven't changed logic since then. Could it be that you have referenced different .config files earlier?