Cache configurations of FluentNHibernate? - nhibernate

I'm using FluentNhibernate version 2.0.3.0 and I'm trying to configure the cache of it to use with queries, I'm using HQL. I want to one opinion if I did all configurations alright, and I want to know how could I see if cache was activate ?
Connection
FluentConfiguration _config = Fluently.Configure()
.Database(
MySQLConfiguration.Standard.ConnectionString(
x => x.Server(HOST)
.Username(USER)
.Password(PASSWORD)
.Database(DB)))
.Cache(c => c.ProviderClass(typeof(NHibernate.Cache.HashtableCacheProvider).AssemblyQualifiedName)
.UseQueryCache())
.Mappings(m => m.FluentMappings.AddFromAssemblyOf<UsuarioMap>())
.ExposeConfiguration(cfg => new SchemaUpdate(cfg)
.Execute(false, true));
Mapping
public class UsuarioMap : ClassMap<Usuario> {
public UsuarioMap() {
Table("USUARIOS");
Cache.ReadWrite();
Id(u => u.id).GeneratedBy.Native();
Map(u => u.nome).Not.Nullable().Length(50);
Map(u => u.email).Unique().Not.Nullable().Length(255);
Map(u => u.senha).Not.Nullable();
Map(u => u.status).CustomType<int>();
Map(u => u.dtCadastro).CustomType<DateTime>();
Map(u => u.dtLastLogin).CustomType<DateTime>();
Map(u => u.tipo).CustomType<int>();
Map(u => u.imagem);
Map(u => u.loginBy).CustomType<int>();
Map(u => u.idFacebook);
}
}
HQL Query
public class UsuarioDAO : GenericDAO<Usuario>{
/** retorna todos os objetos */
public IList<Usuario> findAll(){
ISession _session = getSession();
IList<Usuario> list = _session.CreateQuery("FROM Usuario u ORDER BY u.nome")
.SetCacheable(true).SetCacheMode(CacheMode.Normal)
.List<Usuario>();
return list;
}

A UseSecondLevelCache call is very likely missing (xml configuration parameter cache.use_second_level_cache).
HashtableCacheProvider is not intended for production use, only for test. Choose another one.
Transactions do not seem used. At first data change done outside of a transaction, the second level cache will be disabled. Better transacted all session usages.
A cache.default_expiration should be configured. This is an integer, in seconds.
You may use session statistics to check if the cache is used. Enable them on the session factory.
sessionFactory.Statistics.IsStatisticsEnabled = true;
// Do your stuff then consult the statistics on the session factory.
Avoid letting them enabled in production, this incurs some overhead.
Additional notes:
If you have some mapped collection you wish to cache, you will have to enable caching for them in their mapping.
Caching works better with lazy-loading. Eager loading may cause cached entities to be reloaded from database instead of being loaded from cache.

Related

How to register properly a custom IdentityServer ConfigurationDbContext in Asp.Net Core 2?

I am trying to create my own ConfigurationDbContext from IdentityServer.
public class IdSrvConfigurationDbContext : ConfigurationDbContext<ConfigurationDbContext>
{
public IdSrvConfigurationDbContext(DbContextOptions<IdSrvConfigurationDbContext> options, ConfigurationStoreOptions storeOptions) : base(options.ChangeOptionsType<ConfigurationDbContext>(), storeOptions)
{
}
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
//mylogic here
base.OnModelCreating(modelBuilder);
}
}
Now in the Startup.cs I tried the following
services.AddIdentityServer()
.AddDeveloperSigningCredential()
.AddConfigurationStore(options =>
{
// (DbContextOptionsBuilder) paramBuilder
options.ConfigureDbContext = paramBuilder =>
paramBuilder.UseSqlServer(connectionString,
sql => sql.MigrationsAssembly(migrationAssembly));
});
Now when I am trying to run migrations over my project, it goes through all the startup logic injection and ending with this error:
You will need to set up your IdSrvConfigurationDbContext type to expect a DbContextOptions<ConfigurationDbContext> instead. That’s the type the underlying ConfigurationDbContext expects and that’s also what the IdentityServer is going to pass down.
Usually, you should always use a typed DbContextOptions<T> matching to the context. But when inheriting from existing contexts, this can be a bit difficult. But in those cases, you don’t need to worry much: The typed options are only used to differentiate between the various configured options. So as long as each context in your application still uses a separate type, there shouldn’t be any problems.
The main reason for me to define custom ConfigurationDbContext/PersistedGrantDbContext was to set HasMaxLength over some properties. That's because in production we use NDBCLUSTER engine and if length is not defined or it's too big default type within MySQL will be longtext. This causes a problem, because NDBCLUSTER doesn't support text family data-types.
Within extension method I am plugging custom db context and at the same time setting the common ConfigurationDbContext/PersistedGrantDbContext. I guess IdentityServer uses them for the internal stuff.
services
.AddIdentityServer(options => {})
.AddConfigurationStore<ApplicationConfigurationDbContext>()
.AddConfigurationStoreCache()
.AddOperationalStore<ApplicationPersistedGrantDbContext>();
services.AddDbContext<PersistedGrantDbContext>(options => ContextBuilder(options, connectionString));
services.AddDbContext<ConfigurationDbContext>(options => ContextBuilder(options, connectionString));
services.AddDbContext<ApplicationPersistedGrantDbContext>(options => ContextBuilder(options, connectionString));
services.AddDbContext<ApplicationConfigurationDbContext>(options => ContextBuilder(options, connectionString));
static public void ContextBuilder(DbContextOptionsBuilder options, string connectionString)
{
options.UseMySql(connectionString: connectionString,
ServerVersion.AutoDetect(connectionString),
dbOptions => dbOptions.MigrationsAssembly(DataExtensions.MigrationAssemblyName));
}
That way migrations are created and applied properly and I didn't face any problems during the runtime.

ServiceStack With Funq and FuentNHibernate Sesssion per Request

I'm trying to use FluentNHibernate in ServiceStack with the Funq IoC container on a session-per-request basis and I'm running into a problem where upon the second request to my service, I get an ObjectDisposedException. Shouldn't Funq create a new Session for each request?
My understanding is that by using ReusedWithin(ReuseScope.Request) in Funq, each request would get a new ISession, but that's only happening for the first request. In my AppHost I have the following:
public static NH.ISession CurrentSession
{
get
{
SessionFactory = GetFactory();
NH.ISession session = SessionFactory.OpenSession();
return session;
}
}
private static NH.ISessionFactory GetFactory()
{
return Fluently.Configure().Database(MsSqlConfiguration.MsSql2008
.ConnectionString(ConfigurationManager.ConnectionStrings["DefaultConnection"].ConnectionString)).Mappings(m =>
{ m.FluentMappings.AddFromAssemblyOf<Case>(); })
.BuildSessionFactory();
}
And the registration with the container:
container.Register<NH.ISession>(c => CurrentSession).ReusedWithin(Funq.ReuseScope.Request);
container.Register<ILog>(c => LogManager.GetLogger(GetType()));
So I figured out what my problem was. When using a request scope of per-request in Funq for a NHibernate ISession, make sure the other services that depend on it are also scoped per-request or their backing dependency (ISesssion in this case) will be disposed of on the next request. I changed my container registration to the below:
container.Register<NH.ISession>(c => CurrentSession).ReusedWithin(Funq.ReuseScope.Request);
container.Register<ILog>(c => LogManager.GetLogger(GetType()));
container.Register<IRequestService>(c => new Services.RequestService(c.Resolve<NH.ISession>(), c.Resolve<ILog>())).ReusedWithin(Funq.ReuseScope.Request);
The key is that the Request service must also be scoped per-request.

Nhibernate woes. Calling lock to reattach object throws exception "No persister for: MovieProxy"

Below is the code. Ive simplified it down to nothing. Basically i open a session, load the movie, test that lock works, then evict the item, then try lock it again and it throws the exception. Has anyone seen this before. I would appreciate it if anyone can help. There are no custom interceptors or anything. Im using fluent nhibernate mappings and nhibernate 3.3.1.4000.
var session = SessionFactory.OpenSession();
var m = session.Get(movie.Id);
session.Lock(m, LockMode.None);
session.Evict(m);
session.Lock(m, LockMode.None);
i have created a simple class mapping to narrow down the problem. The problem appears to be a bug in lazy loading tagged to properties. Locking works fine when the lazy loaded phone number is commented out however crashes when its included in the mapping.
//DynamicUpdate();
//LazyLoad();
Id(x => x.Id);
Map(x => x.Name).Unique();
Map(x => x.CleanName);
Map(x => x.PhoneNumber).LazyLoad();
HasMany(o => o.CountryList).Table("Country").Element("String").Cascade.AllDeleteOrphan().LazyLoad();
//HasMany(x => x.AddressList).Cascade.AllDeleteOrphan().Inverse().LazyLoad();
Version(x => x.EditDate);

Fluent NHibernate caching with automapping

I'm trying to understand how to configure Fluent NHibernate to enable 2nd-level caching for queries, entities, etc... And at the same time use automapping. There is very little information online on how to do that. Sure it can be done when mapping the classes one by one... But how about automapping?
Here is my configuration code so far:
AutoPersistenceModel model = AutoMap.AssemblyOf<Seminar>()
.Where(t => t.Namespace == "[MY NAMESPACE]")
.Conventions.Add(DefaultCascade.All());
Configuration config = Fluently.Configure()
.Database
(
MsSqlConfiguration.MsSql2005
.ConnectionString(#"[MY CONNECTION STRING]")
)
.Mappings(m => m.AutoMappings.Add(model))
.BuildConfiguration();
_sessionFactory = config.BuildSessionFactory();
Thanks!
Assuming you've already downloaded a 2nd-level cache provider from the NHibernate Contribution project, you should be able to use the following to initialize the cache within your automappings setup.
Configuration config = Fluently.Configure()
.Database
(
MsSqlConfiguration.MsSql2005
.ConnectionString(#"[MY CONNECTION STRING]")
.Cache(c => c.UseQueryCache().ProviderClass<YourCacheProviderOfChoice>())
)
.Mappings(m => m.AutoMappings.Add(model))
.BuildConfiguration();
Selecting the queries you want to cache is simply a matter of calling SetCacheable(true) on your Criteria instance.
var query = session.CreateQuery("from Blog b where b.Author = :author")
.SetString("author", "Gabriel")
.SetCacheable(true);
var list = query.List<Blog>();
This is an epic blog post on NHibernate's first and second level caches, good reference material.
I've been struggling with this for a while and was surprised how little information is out there. This question is the best I could find and even here the accepted answer doesn't say how to enable entity caching. Here's what I've found out.
To enable second level cache:
Fluently.Configure()
.Database(/* your DB config */)
.Cache(c => c.UseSecondLevelCache().ProviderClass<CacheProviderClass>())
You can use both this and query cache:
Fluently.Configure()
.Database(/* your DB config */)
.Cache(c => c.UseSecondLevelCache()
.UseQueryCache()
.ProviderClass<CacheProviderClass>())
To enable per-entity caching:
.Mappings(m => m.AutoMappings.Add(AutoMap.AssemblyOf<Entity>()
.Conventions.Add(ConventionBuilder.Class.Always(c => c.Cache.ReadWrite()))
)
)
Of course, you can use ReadOnly() or NonStrictReadWrite() if you wish.

NHibernate Configuration reference at runtime

Is there a way to get a reference to NHibernate Configuration at the runtime? I need it for SchemaExport().
Update: I am using StructureMap with FluentNHibernate to set it up, but I just want to know if I can get it from SessionFactory or some other object, after SessionFactory has been initialized, without having to rewrite setup in ioc to hold on to reference to Configuration.
Ok, here is how I did it.
ForRequestedType<FluentConfiguration>()
.CacheBy(InstanceScope.Singleton)
.TheDefault.Is.ConstructedBy(
()=>Fluently.Configure()
.Database(MsSqlConfiguration.MsSql2005
.AdoNetBatchSize(10)
.Driver("NHibernate.Driver.SqlClientDriver")
.ProxyFactoryFactory("NHibernate.ByteCode.Castle.ProxyFactoryFactory, NHibernate.ByteCode.Castle")
.UseOuterJoin()
.ConnectionString(#"Server=.\SQLEXPRESS;User Id=epitka;Password=password;Database=dnn49;")
.ShowSql()
.CurrentSessionContext("thread_static")) // CHANGE THIS FOR WEB
.Mappings(m => m.FluentMappings.AddFromAssemblyOf<MetaProject>())
.ExposeConfiguration(
cfg =>{
cfg.SetProperty(
Environment.TransactionStrategy,
typeof (AdoNetTransactionFactory).FullName);
cfg.SetProperty(Environment.GenerateStatistics, "true"); //REMOVE FOR LIVE
})
)
.WithName("SharMod_FluentConfiguration");
ForRequestedType<Configuration>()
.TheDefault.Is.ConstructedBy(
() =>
{
var fc =ObjectFactory.GetInstance<FluentConfiguration>();
return fc.BuildConfiguration();
})
.WithName("SharpMod_Configuration");
//SharpMod_SessionFactory
ForRequestedType<ISessionFactory>()
.CacheBy(InstanceScope.Singleton)
.AddInstances(x => x.ConstructedBy(() =>
ObjectFactory.GetNamedInstance<FluentConfiguration>("SharMod_FluentConfiguration")
.BuildSessionFactory())
.WithName("SharpMod_SessionFactory"));
Now to get it I just do:
var cfg = ObjectFactory.GetNamedInstance<Configuration>("SharpMod_Configuration");