how can i decrease memory usage by entity framework - asp.net

i made one CMS on .NET4 And Entity Framework. this CMS in now begin used on server with shared memory.
application pool has limited memory so app pool reset when my application reach the memory limit . after trace my app i realize EF use most of memory . is there any way to decrease memory usage of EF or any tweaking?
my sample use of EF :
using BICT.Data;
//
public static List<Data.Log> GetList()
{
using(CMSEntities cms = new CMSEntities())
{
return cms.Log.ToList<Data.Log>();
}
}
am i use a wrong way to use EF?

Assuming CMSEntities is your dbContext, then you're using it the right way as it implements the IDisposable interface.
One thing I would suggest is returning an IQueryable instead of a List as then you gain the flexibility of pipelining Linq queries, and then actually accessing the database. (Lazy Loading).
If you want all of the items, you can call .ToList(), so that option is still available to you.
EDIT
For example, say I have a User table in my database, I create a wrapper class for accessing the data.
namespace Data.Repositories
{
public class UserRepository : IDisposable
{
ColegioDBV2Entities db = new ColegioDBV2Entities();
public IQueryable<User> FindAllUsers()
{
return db.Users;
}
public void Dispose()
{
db.Dispose();
}
}
}
And when I want to access data I do so through this repository class:
using (UserRepository repo = new UserRepository())
{
var result = repo.FindAllUsers().Where(u => u.Username == txtUsername.Text && u.Password == txtPassword.Text);
}
Once you leave that using block, your context is automatically closed and released.

Related

Variety of NHibernate errors relating to transactions during spidering

We have an ASP.Net 4 / MVC 3 hybrid web application which uses NInject 3 and (Fluent) NHibernate 3.2. DB is SQL Server 2008 R2. Server is 6-core 28 GB Windows 2008 64-bit server.
Our customer has recently started testing the site using a spidering tool. As soon as the site experiences the load produced by the spider, our log starts to fill up with exceptions.
We see a variety of errors from NHibernate, including some of the following:
NHibernate.TransactionException: Commit failed with SQL exception ---> System.Data.SqlClient.SqlException: The transaction operation cannot be performed because there are pending requests working on this transaction.
System.Data.SqlClient.SqlException (0x80131904): The server failed to resume the transaction. Desc:410000050f. The transaction active in this session has been committed or aborted by another session.
System.NullReferenceException: Object reference not set to an instance of an object. at System.Data.SqlClient.SqlInternalTransaction.GetServerTransactionLevel()....
NHibernate.Exceptions.GenericADOException: could not execute native bulk manipulation query:exec [Stats.InsertListingStatsList] #ListingStats =:ListingStats[SQL: exec [Stats.InsertListingStatsList] #ListingStats =#p0] ---> System.Data.SqlClient.SqlException: New request is not allowed to start because it should come with valid transaction descriptor.
to give just four examples. All have a similar flavour - they all seem to relate to the management of transactions by ADO.Net as the substrate of NHibernate.
Now, some details of our NH implementation:
SessionFactory is static;
SessionFactory uses AdoNetTransactionFactory;
ISession is in request scope, and stored in the HttpContext.Items collection;
Repositories are also in request scope;
We are now using config.CurrentSessionContext();
Each call to our generic repository uses a transaction
Here are two methods from our repository.
public T GetById<T>(int id)
{
using (var t = Session.BeginTransaction())
{
var entity = Session.Get<T>(id);
t.Commit();
return entity;
}
}
public void Add<T>(T entity)
{
using (var t = Session.BeginTransaction())
{
Session.Save(entity);
t.Commit();
}
}
My question is simple: what is going wrong? What is causing these apparent conflicts between transactions, or between the various data-related operations that our domain instigates as it de/hydrates our domain?
UPDATE: here is our full configuration:
public FluentConfiguration BuildConfiguration(string connectionString)
{
var sqlConfig = MsSqlConfiguration.MsSql2008.ConnectionString(connectionString).AdoNetBatchSize(30);
var config = Fluently.Configure().Database(sqlConfig);
var entityMapping = AutoMap.AssemblyOf<User>(new AutomappingConfiguration())
.UseOverridesFromAssemblyOf<UserMappingOverride>()
.AddMappingsFromAssemblyOf<TableNamingConvention>()
.Conventions.AddFromAssemblyOf<TableNamingConvention>();
var cqrsMapping = AutoMap.AssemblyOf<AdvertView>(new QueryAutomappingConfiguration())
.UseOverridesFromAssemblyOf<AdvertViewMappingOverride>();
config.Mappings(c => c.AutoMappings.Add(entityMapping));
config.Mappings(c => c.AutoMappings.Add(cqrsMapping));
config.Mappings(c => c.HbmMappings.AddFromAssemblyOf<AdvertView>());
config.ExposeConfiguration(c => c.SetProperty(Environment.TransactionStrategy, typeof(AdoNetTransactionFactory).FullName));
config.CurrentSessionContext<WebSessionContext>();
return config;
}
More code for you guys and gals. Here is the relevant section of our IoC Container configuration.
var domainEntityBootstrapper = new DomainEntitySessionBootStrapper("Domain", "NHibernate.ISession.Domain", _enableLucine, HttpContextItemsProvider);
Bind<ISessionFactory>().ToMethod(domainEntityBootstrapper.CreateSessionFactory).InSingletonScope().Named(domainEntityBootstrapper.Name);
Bind<ISession>().ToMethod(domainEntityBootstrapper.GetSession).InRequestScope();
var queryBootstrapper = new QueryEntitySessionBootStrapper("Query", "NHibernate.ISession.Query", HttpContextItemsProvider);
Bind<ISessionFactory>().ToMethod(queryBootstrapper.CreateSessionFactory).InSingletonScope().Named(queryBootstrapper.Name);
Bind<ISession>().ToMethod(queryBootstrapper.GetSession).WhenInjectedInto(typeof (QueryExecutor)).InRequestScope();
and here is the code from the GetSession() method of the base class for these SessionBootstrappers (please note that the CreateSessionFactory method calls the BuildConfiguration method above and then calls BuildSessionFactory()).
public virtual ISession GetSession(IContext context)
{
var items = GetHttpContextItems();
var session = default(ISession);
var sessionExists = items.Contains(SessionKey);
if (!sessionExists)
{
session = context.Kernel.Get<ISessionFactory>(Name).OpenSession();
items.Add(SessionKey, session);
}
else
{
session = (ISession)items[SessionKey];
}
return session;
}
// a Func which serves access to the HttpContext.Current.Items collection
private Func<IDictionary> GetHttpContextItems { get; set; }
Please note that we use two sessions, one for ordinary domain de/hydration and one for CQRS, hence the pair of bindings in the Container.
The error messages indicate that you are not managing transactions correctly. I think the root cause is that you are handling transactions in the repository methods which in my opinion is a very poor design. Your repositories should have an ISession injected into their constructors, and your controllers should have any repositories they are dependent upon injected into their constructors. It's easy to wire this all up with Ninject. With this approach you can use transaction-per-request or (much better imo) manage the transaction in the action methods.
Here's how I'm setting up NHibernate with Ninject in NinjectWebCommon. The root cause of your problem may be that you are binding the ISession in request scope and storing it in HttpContext, which is unnecessary. I am also confused why you have two sets of bindings for Domain and Query.
private static void RegisterServices(IKernel kernel)
{
kernel.Bind<ISessionFactory>().ToProvider(new SessionFactoryProvider()).InSingletonScope();
kernel.Bind<ISession>().ToProvider(new SessionProvider()).InRequestScope();
}
private class SessionFactoryProvider : Provider<ISessionFactory>
{
protected override ISessionFactory CreateInstance(IContext context)
{
// create and configure the session factory
// I have a utility class to do this so the code isn't shown
return nhibernateHelper.BuildSessionFactory();
}
}
private class SessionProvider : Provider<ISession>
{
protected override ISession CreateInstance(IContext context)
{
var sessionFactory = context.Kernel.Get<ISessionFactory>();
var session = sessionFactory.OpenSession();
session.FlushMode = FlushMode.Commit;
return session;
}
}
A sample controller action using a transaction. Managing transactions outside of the repositories is important for several reasons:
Allows multiple repositories to participate in a transaction
Allows the controller to set the transaction boundaries (unit of work)
Allows lazy loads to occur in the transaction
Transactions are needed for read operations if second level caching is used. Even if it caching isn't used I think it's a best practice
public ActionResult EditDocuments(int id, string name)
{
using (var txn = _session.BeginTransaction())
{
var summary = _characterizationRepository
.GetCharacterization(id)
.AsCharacterizationSummaryView()
.ToFutureValue();
var documents = _characterizationRepository
.GetCharacterization(id)
.SelectMany(c => c.Documents)
.OrderBy(d => d.FileName)
.AsDocumentSelectView(true)
.ToFuture();
if (summary.Value == null)
{
throw new NotFoundException(_characterizationRepository.ManualId, "Characterization", id);
}
CheckSlug(name, summary.Value.Title);
var model = new DocumentSectionEditView()
{
CharacterizationSummary = summary.Value,
Documents = documents.ToArray()
};
txn.Commit();
return View(model);
}
}
It seems you are using the wrong context manager, check if you are using the WebSessionContext. This context manager will bind your session to the httpcontext of the current call instead of the thread. What happens now under load (the spider), when you are using the ThreadStaticSessionContext, session will 'jump' to an other 'call'.

Best practice for managing life time of business layer, data layer, datacontext instance asp.net website

Our Asp.net web application is using LINQ-to-SQL (Stored Procs are dragged on dropped on dbml file to create classes) and 3 tier architecture is similar to the one below. I have just created rough methods to give reader proper idea so that he can answer well.
namespace MyDataLayer
{
public class MyDataAccess
{
// global instance of datacontext
MyDataModelDataContext myDB = new MyDataModelDataContext(); (#1)
public void GetUserIDByUsername(string sUserName, ref int iUserID)
{
int? iUserIDout = 0;
// this will make call to SP in SQL DB
myDB.USP_RP_GETUSERIDBYUSERNAME(sUserName, "", ref iUserIDout);
iUserID = (int)iUserIDout;
}
public List<USP_APP_USERDETAILSResult> GetUserDetails(string sUserIDs)
{
// this will make call to SP in SQL DB
return myDB.USP_APP_USERDETAILS(sUserIDs).ToList();
}
...
... // several CRUD methods
}
}
namespace MyBusinessLayer
{
public class SiteUser
{
// global DataAccess instance
MyDataLayer.MyDataAccess myDA = new MyDataAccess(); (#2)
public void GetUserIDByUsername(string sUserName, ref int iUserID)
{
myDA.GetUserIDByUsername(sUserName, ref iUserID);
}
public List<USP_APP_USERDETAILSResult> GetUserDetails(string sUserIDs)
{
// this will make call to SP in SQL DB
return myDA.GetUserDetails(sUserIDs);
}
...
... // several CRUD methods
}
}
namespace MyWebApplication
{
public class BaseWebPage : System.Web.UI.Page
{
// static business layer instance
public static MyBusinessLayer.SiteUser UserBLInstance = new SiteUser(); (#3)
...
}
}
// Index.aspx.cs code fragment
namespace MyWebApplication
{
public class Index : BaseWebPage
{
public void PopulateUserDropDown()
{
// using static business layer instance declared in BaseWebPage
List<USP_APP_USERDETAILSResult> listUsers = UserBLInstance.GetUserDetails("1,2,3");
// do databinding and so on ...
}
...
}
}
Questions
(Ref.#1) Is having global datacontext in DataAccess good approach? yes/no why?
If your suggestion is having datacontext per request what is the best practice for that
(Ref.#2) Is having global DataAccess instance in BusinessLayer good approach? yes/no why?
If your suggestion is having DataAccess instance per request what is the best practice for that
(Ref. #3) Is static business layer instance declared in BaseWebPage good approach? yes/no why?
Best approach to manage life time of BL instance and DL instance in general
We are facing periodic InvalidCastException on production server for a very simple method which works fine if I restart my application from IIS. When this problem is there we can access the same database from SQL Management Studio and can execute same SP
Our prime suspect about this issue is poor DataContext management and I have read many articles on net about managing life time of DataContext but I am now confused about various approach.
That's why I have elaborated my questions so that many in same situation can get clear idea about problem/answer.
(Ref.#1) Is having global datacontext in DataAccess good approach? yes/no why?
Yes.
However, creating it manually inside the dataaccess class means that you can't control the lifetime of the datacontext. Instead, make it then a constructor parameter so that it is injected into the data access
(Ref.#2) Is having global DataAccess instance in BusinessLayer good approach? yes/no why?
Yes. But refer to 1. - make it injectable via the constructor.
(Ref. #3) Is static business layer instance declared in BaseWebPage good approach? yes/no why?
No. Avoid static for complex objects as usually such objects has non-trivial state. And this is when a lot of nasty issues can happen if you share such objects in a concurrent environment.
To summarize.
public class DataAccess {
public DataAccess( DataContext context ) { ... }
}
public class BusinessLayer {
public BusinessLayer( DataAccess access ) { ... }
}
public class MyPage : Page {
...
var ctx = TheDataContext.Current;
var bl = new BusinessLayer( new DataAccess( ctx ) );
}
with data context shared in a request scope:
public partial class TheDataContext {
// Allow the datacontext to be shared in a request-scope
public static TheDataContext Current {
get {
if ( HttpContext.Current.Items["context"] == null )
HttpContext.Current.Items.Add( "context", new TheDataContext() );
return (TheDataContext)HttpContext.Current.Items["context"];
}
}
}
In your sample - your MyDataLayer usually has name Repository. Definitely it is good to have DataContext instance in Repositories and do not try to use them outside. So, only in repositories you will have dependency on Linq-To-Sql, which means that you can create Stub objects for these Repositories and really easy test other parts of your application.
Definitely you should Dispose your Data Context instances, DataContext contains too many objects to keep them alive and let GC to kill them. As you can see you don't create any transaction objects when you are working with DataContextes, so I think that LinqToSql based on idea that you should have everything per transaction (of course you can also try to handle transaction manually, but do you really want to do this?). Disposing datacontextes in methods of Repository is a good approach, because this will not allow you to use cool feature of all ORM frameworks: Lazy Load. If you will try to use Lazy Load - you will like it, but usually it is just one of possible performance degradation cause.
Definitely your should use DataContextes for shorter or the same time of Request, don't try to use LongSession (it is when you trying to keep DataContext for more than one Http Request, it is just pain in ass, nothing else, if you want to read about this, just try to read couple articles about Long Running Session in Hibernate, I tried with nHibernate - don't do this at home ;) ).

Testing a class library that is using different databases based on Session

I have an ASP.NET website project that until recent had all code in App_Code folder. It uses Entity Framework 4 as ORM. Application is divided into three "sections" (let's say one for each customer). Each section has it's own database (but same schema). This is due to performance reasons, databases are over 10GB each with millions of rows.
Each time a context object is created a Session variable which holds section ID is called and proprietary connection string is chosen for this context.
It looks like this (following are members of static Connection class):
public static MyEntities GetEntityContext()
{
if (HttpContext.Current.Session["section"] == null)
{
HttpContext.Current.Response.Redirect("~/Login.aspx");
}
var context = new MyEntities(GetEntityConnectionStringForSection((int)HttpContext.Current.Session["section"]);
return context;
}
private static string GetEntityConnectionStringForSection(int section)
{
switch (section)
{
case 1: return ConfigurationManager.ConnectionStrings["entity_1"].ConnectionString;
case 2: return ConfigurationManager.ConnectionStrings["entity_2"].ConnectionString;
case 3: return ConfigurationManager.ConnectionStrings["entity_3"].ConnectionString;
default: return ConfigurationManager.ConnectionStrings["entity_1"].ConnectionString;
}
}
It works very good and also handles situation when session timed out everytime any data access is performed.
Recently as I needed to share DB classes among two websites I moved all DB classes to separate class library and referenced System.Web library which I know is bad practice, but it's working.
Now the next step is to include unit and module tests which as I read is very difficult or impossible when using HttpContext in library, so I want to get rid of System.Web references. What is the best practice for this situation?
I think I can't just pass HttpContext to GetEntityContext() as it is also called from within my entity classes. Although this probably can be refactored. So maybe this is where I should go?
I also wondered if is it possible to somehow pass current section ID to this whole library? It cannot be just static property because as far as I understand it would be common for all users using the application. This should be user-specific.
Reassuming the objective is to make automated testing possible without loosing transparent Connection String choosing and session timeouts handling.
If I do something fundamentally wrong at this stage please also let me know. I can look again at this question tomorrow morning (8.00 am UTC) so please don't be discouraged by my silence till then.
EDIT:
Example of usage of Connection class in the library:
public partial class Store
{
public static List<Store> GetSpecialStores()
{
using (var context = Connection.GetEntityContext())
{
return context.Stores.Where(qq => qq.Type > 0).OrderBy(qq => qq.Code).ToList();
}
}
}
You can declare interface IContextProvider inside your library ans use it to retrieve context. Something like:
public interface IContextProvider
{
MyEntities GetEntityContext();
}
This will make your library testable. In your web project you can inject IContextProvider implementation into your library.
public class WebContextProvider : IContextProvider
{
public MyEntities GetEntityContext()
{
if (HttpContext.Current.Session["section"] == null)
HttpContext.Current.Response.Redirect("~/Login.aspx");
int sectionId = (int)HttpContext.Current.Session["section"];
string connectionString = GetEntityConnectionStringForSection(sectionId);
var context = new MyEntities(connectionString);
return context;
}
private static string GetEntityConnectionStringForSection(int section)
{
switch (section)
{
case 1: return ConfigurationManager.ConnectionStrings["entity_1"].ConnectionString;
case 2: return ConfigurationManager.ConnectionStrings["entity_2"].ConnectionString;
case 3: return ConfigurationManager.ConnectionStrings["entity_3"].ConnectionString;
default: return ConfigurationManager.ConnectionStrings["entity_1"].ConnectionString;
}
}
}
Inject this interface to repositories or other data access classes.
public partial class Store
{
private IContextProvider contextProvider;
public Store(IContextProvider contextProvider)
{
this.contextProvider = contextProvider;
}
public List<Store> GetSpecialStores()
{
using (var context = contextProvider.GetEntityContext())
{
return context.Stores.Where(qq => qq.Type > 0).OrderBy(qq => qq.Code).ToList();
}
}
}

Attach additional ObjectSets to ObjectContext from separate project

I hope this makes sense. I have a ASP.NET web application that uses Entity Framework. I have added a couple of custom tables to the db and created a separate project to handle the CRUD operations for those tables. I chose the separate project because I don't want future upgrades to the application to overwrite my custom features.
My problem is this. How do I attach/combine my custom ObjectContext to the ObjectContext of the application? I want to use the same UnitOfWorkScope (already in the application) to maintain the one ObjectContext instance per HTTP request. Again, I don't want to add my ObjectSets to the application's ObjectContext for my reason listed above.
Here is some code:
Widget.cs
public partial class Widget
{
public Widget()
{
}
public int WidgetId {get;set;}
public string WidgetName {get;set;}
}
WidgetObjectContext.cs
public partial class WidgetObjectContext : ObjectContext
{
private readonly Dictionary<Type, object> _entitySets;
public ObjectSet<T> EntitySet<T>()
where T : BaseEntity
{
var t = typeof(T);
object match;
if(!_entitySets.TryGetValue(t, out match))
{
match = CreateObjectSet<T>();
_entitySets.Add(t, match);
}
return (ObjectSet<T>)match;
}
public ObjectSet<Widget> Widgets
{
get
{
if((_widgets == null))
{
_widgets = CreateObjectSet<Widget>();
}
return _widget;
}
}
private ObjectSet<Widget> _widgets;
In my WidgetManager class if I was using the application's ObjectContext I would query my tables like this:
var context = ObjectContextHelper.CurrentObjectContext;
var query = from c in context.ObjectSet .... etc
What I want would be to do something like this:
var context = ObjectContextHelper.CurrentObjectContext.Attach(WidgetObjectContext);
I know this won't work but that is the gist of what I am trying to accomplish. Hope this is clear enough. Thanks.
I don't think it is possible. ObjectContext creates entity connection which connects to metadata describing mapping and database. But you have to different sets of metadata - one for ASP.NET application and one for separate project. Simply you need two connection to work with these models => you need two ObjectContexts.
FYI: The previous answer was correct at the time of the answer. It is now possible to do this using the DbContext available in EF 4.1. The caveat is that you must use the code-first strategy in order to build your custom context. In other words, you won't be able to use EDMX files to accomplish this.

How to pass unit of work container into constructor of repository using dependency injection

I'm trying to work out how to complete my implementation of the Repository pattern in an ASP.NET web application.
At the moment, I have a repository interface per domain class defining methods for e.g. loading and saving instances of that class.
Each repository interface is implemented by a class which does the NHibernate stuff. Castle Windsor sorts out the DI of the class into the interface according to web.config. An example of an implemented class is provided below:
public class StoredWillRepository : IStoredWillRepository
{
public StoredWill Load(int id)
{
StoredWill storedWill;
using (ISession session = NHibernateSessionFactory.OpenSession())
{
storedWill = session.Load<StoredWill>(id);
NHibernateUtil.Initialize(storedWill);
}
return storedWill;
}
public void Save(StoredWill storedWill)
{
using (ISession session = NHibernateSessionFactory.OpenSession())
{
using (ITransaction transaction = session.BeginTransaction())
{
session.SaveOrUpdate(storedWill);
transaction.Commit();
}
}
}
}
As pointed out in a previous thread, the repository class needs to accept an unit of work container (i.e. ISession) rather than instantiating it in every method.
I anticipate that the unit of work container will be created by each aspx page when needed (for example, in a property).
How do I then specify that this unit of work container instance is to be passed into the constructor of StoredWillRepository when Windsor is creating it for me?
Or is this pattern completely wrong?
Thanks again for your advice.
David
I have a persistence framework built on top of NHibernate that is used in a few Web apps. It hides the NH implementation behind an IRepository and IRepository<T> interface, with the concrete instances provided by Unity (thus I could in theory swap out NHibernate for, say, Entity Framework fairly easily).
Since Unity doesn't (or at least the version I'm using doesn't) support the passing in of constructor parameters other than those that are dependency injections themselves, passing in an extant NH ISession isn't possible; but I do want all objects in the UOW to share the same ISession.
I solve this by having a controlling repository class that manages access to the ISession on a per-thread basis:
public static ISession Session
{
get
{
lock (_lockObject)
{
// if a cached session exists, we'll use it
if (PersistenceFrameworkContext.Current.Items.ContainsKey(SESSION_KEY))
{
return (ISession)PersistenceFrameworkContext.Current.Items[NHibernateRepository.SESSION_KEY];
}
else
{
// must create a new session - note we're not caching the new session here... that's the job of
// BeginUnitOfWork().
return _factory.OpenSession(new NHibernateInterceptor());
}
}
}
}
In this example, PersistenceFrameworkContext.Current.Items accesses an IList<object> that is stored either ThreadStatic if not in a Web context, or within HttpContext.Current.Items if it is in a Web context (to avoid thread-pool problems). The first call to the property instantiates the ISession from the stored factory instance, subsequent calls just retrieve it from storage. The locking will slow things down slightly but not as much as just locking an appdomain-scoped static ISession instance.
I then have BeginUnitOfWork and EndUnitOfWork methods to take care of the UOW - I have specifically disallowed nested UOWs because frankly they were a pain to manage.
public void BeginUnitOfWork()
{
lock (_lockObject)
{
if (PersistenceFrameworkContext.Current.Items.ContainsKey(SESSION_KEY))
EndUnitOfWork();
ISession session = Session;
PersistenceFrameworkContext.Current.Items.Add(SESSION_KEY, session);
}
}
public void EndUnitOfWork()
{
lock (_lockObject)
{
if (PersistenceFrameworkContext.Current.Items.ContainsKey(SESSION_KEY))
{
ISession session = (ISession)PersistenceFrameworkContext.Current.Items[SESSION_KEY];
PersistenceFrameworkContext.Current.Items.Remove(SESSION_KEY);
session.Flush();
session.Dispose();
}
}
}
Finally, a pair of methods provide access to the domain-type-specific repositories:
public IRepository<T> For<T>()
where T : PersistentObject<T>
{
return Container.Resolve<IRepository<T>>();
}
public TRepository For<T, TRepository>()
where T : PersistentObject<T>
where TRepository : IRepository<T>
{
return Container.Resolve<TRepository>();
}
(Here, PersistentObject<T> is a base class providing ID and Equals support.)
Access to a given repository is thus in the pattern
NHibernateRepository.For<MyDomainType>().Save();
This is then facaded over such that you can use
MyDomainType.Repository.Save();
Where a given type has a specialised repository (ie needs more than it can get from IRepository<T>) then I create an interface deriving from IRepository<T>, an extending implementation inheriting from my IRepository<T> implementation, and in the domain type itself I override the static Repository property using new
new public static IUserRepository Repository
{
get
{
return MyApplication.Repository.For<User, IUserRepository>();
}
}
(MyApplication [which is called something less noddy in the real product] is a facade class which takes care of supplying the Repository instance via Unity so you have no dependency on the specific NHibernate repository implementation within your domain classes.)
This gives me full pluggability via Unity for the repository implementation, easy access to the repository in code without jumping through hoops, and transparent, per-thread ISession management.
There's lots more code than just what's above (and I've simplified the example code a great deal), but you get the general idea.
MyApplication.Repository.BeginUnitOfWork();
User user = User.Repository.FindByEmail("wibble#wobble.com");
user.FirstName = "Joe"; // change something
user.LastName = "Bloggs";
// you *can* call User.Repository.Save(user), but you don't need to, because...
MyApplication.Repository.EndUnitOfWork();
// ...causes session flush which saves the changes automatically
In my Web app, I have session-per-request, so BeginUnitOfWork and EndUnitOfWork get called in BeginRequest and EndRequest respectively.
I have a pretty similar structure to yours, and here's how I solve your question:
1) To specify my container on each method, I have a separate class ("SessionManager") which I then invoke via a static property. By doing so, here's an example using my Save implementation:
private static ISession NHibernateSession
{
get { return SessionManager.Instance.GetSession(); }
}
public T Save(T entity)
{
using (var transaction = NHibernateSession.BeginTransaction())
{
ValidateEntityValues(entity);
NHibernateSession.Save(entity);
transaction.Commit();
}
return entity;
}
2) My container is not created on each ASPX page. I instantiate all of my NHibernate goodness on the global.asax page.
** A few more things spring up **
3) You don't need to have a helper to instantiate the Load. You might as well use Get instead of Load. More information # Difference between Load and Get.
4) Using your current code, you would have to repeat pretty much the same code for each domain object you need (StoredWillRepository, PersonRepository, CategoryRepository, etc..?), which seems like a drag. You could very well use a generic class to operate over NHibernate, like:
public class Dao<T> : IDao<T>
{
public T SaveOrUpdate(T entity)
{
using (var transaction = NHibernateSession.BeginTransaction())
{
NHibernateSession.SaveOrUpdate(entity);
transaction.Commit();
}
return entity;
}
}
In my implementation, I could then use something like:
Service<StoredWill>.Instance.SaveOrUpdate(will);
Technically, the answer to my question is to use the overload of container.Resolve which allows you to specify the constructor argument as an anonymous type:
IUnitOfWork unitOfWork = [Code to get unit of work];
_storedWillRepository = container.Resolve<IStoredWillRepository>(new { unitOfWork = unitOfWork });
But let's face it, the answers provided by everyone else have been much more informative.

Resources