I have a problem with save more than one items to database in c# asp.net with fluent nhibernate. I have :
public static readonly ISessionFactory SessionFactory = DbContext.SessionFactory();
public static readonly ISession Session = SessionFactory.OpenSession();
public static readonly ITransaction Transaction = Session.BeginTransaction();
public IEnumerable<Candidate> Candidates => Session.Query<Candidate>();
public Candidate GetUser(int id)
{
return Session.Query<Candidate>().FirstOrDefault(x => x.Id == id);
}
public void AddCandidate(Candidate candidate)
{
try
{
Session.Save(candidate);
Transaction.Commit();
}
catch (Exception exception)
{
throw;
}
}
And the error is : IsolationLevel = '((NHibernate.Transaction.AdoTransaction)Transaction).IsolationLevel' threw an exception of type 'System.NullReferenceException'
More information : can't access the deleted object
My Class DbContext:
public static class DbContext
{
private static ISessionFactory _sessionFactory;
static DbContext()
{
GetFactory();
}
public static void GetFactory()
{
var myEntities = new[]
{
typeof (ApplicationUser)
};
var configuration = Fluently.Configure()
.Database(MsSqlConfiguration.MsSql2012.ConnectionString(c => c.FromConnectionStringWithKey("ConnectionString"))
.ShowSql())
.Mappings(m => m.FluentMappings.AddFromAssembly(Assembly.GetExecutingAssembly()))
.ExposeConfiguration(x =>
{
x.SetProperty("", "");
x.AddDeserializedMapping(MappingHelper.GetIdentityMappings(myEntities), null);
})
.ExposeConfiguration(BuildSchema);
_sessionFactory = configuration.BuildSessionFactory();
}
public static ISessionFactory SessionFactory()
{
return _sessionFactory;
}
public static ISession GetSession()
{
if (!CurrentSessionContext.HasBind(_sessionFactory))
CurrentSessionContext.Bind(_sessionFactory.OpenSession());
return _sessionFactory.GetCurrentSession();
}
public static ISession MakeSession()
{
return _sessionFactory.OpenSession();
}
//public static IUserStore<ApplicationUser, string> Users => new IdentityStore(MakeSession());
private static void BuildSchema(Configuration config)
{
new SchemaUpdate(config)
.Execute(true, true);
}
}
Please, help!
Thanks!
SessionFactory should be singleton and using it as static can be part of the solution. Objects from ISession and ITransaction should be created when necessary. Try change your code to something like this:
public static readonly ISessionFactory SessionFactory = DbContext.SessionFactory();
public Candidate GetUser(int id)
{
Candidate candidate = null;
using (var session = SessionFactory.OpenSession())
{
candidate = Session.Query<Candidate>().FirstOrDefault(x => x.Id == id);
}
return candidate;
}
public void AddCandidate(Candidate candidate)
{
using (var session = SessionFactory.OpenSession())
{
using (var transaction = session.BeginTransaction())
{
try
{
Session.Save(candidate);
transaction.Commit();
}
catch
{
transaction.RollBack();
throw;
}
}
}
}
Avoid to expose Queryable as your code is doing on the Candidates property.
There are some approaches to rightly implement the session management on an application and depending whant environment code is executed it can change.
I recommend you to see these posts:
http://benfoster.io/blog/yet-another-session-per-request-post
How to implement session-per-request pattern in asp.net mvc with Nhibernate
Related
I try to open a sqlite db from a .netstandard library project that is used by a xamarin forms project (uwp and android) I have only tried the uwp project and get a unable to open database exception
I have tried to use the path of the Personal folder und i have tried to open the connection via Sqliteconnection.
Full Project ist available here: https://github.com/blndr83/OutlookCalender
internal class DatabaseProvider
{
private static ISessionFactory _sessionFactory;
private static Configuration _configuration;
private static HbmMapping _mapping;
public static ISession OpenSession()
{
//Open and return the nhibernate session
return SessionFactory.OpenSession();
}
public static ISessionFactory SessionFactory
{
get
{
if (_sessionFactory == null)
{
//Create the session factory
_sessionFactory = Configuration.BuildSessionFactory();
}
return _sessionFactory;
}
}
public static Configuration Configuration
{
get
{
if (_configuration == null)
{
//Create the nhibernate configuration
_configuration = CreateConfiguration();
}
return _configuration;
}
}
public static HbmMapping Mapping
{
get
{
if (_mapping == null)
{
//Create the mapping
_mapping = CreateMapping();
}
return _mapping;
}
}
private static Configuration CreateConfiguration()
{
var configuration = new Configuration();
//Loads properties from hibernate.cfg.xml
configuration.Configure();
IDictionary<string, string> props = new Dictionary<string, string>
{
{ "connection.connection_string", #"Data Source=Calendar.db;FailIfMissing=false;New=false;Compress=true;Version=3"},
{ "connection.driver_class", "NHibernate.Driver.SQLite20Driver" },
{ "dialect", "NHibernate.Dialect.SQLiteDialect" },
{ "connection.provider", "NHibernate.Connection.DriverConnectionProvider" },
{ "show_sql", "false" }
};
configuration.SetProperties(props);
configuration.AddDeserializedMapping(Mapping, null);
return configuration;
}
private static HbmMapping CreateMapping()
{
var mapper = new ModelMapper();
//Add the person mapping to the model mapper
mapper.AddMappings(new List<System.Type> { typeof(EventModelMap) });
//Create and return a HbmMapping of the model mapping in code
return mapper.CompileMappingForAllExplicitlyAddedEntities();
}
}
public class Repository : IRepository
{
private readonly ISession _session;
public Repository()
{
_session = DatabaseProvider.OpenSession();
var schemaUpdate = new SchemaUpdate(DatabaseProvider.Configuration);
schemaUpdate.Execute(Console.WriteLine, true);
}
public void Delete<T>(T entity) where T : Entity
{
using (var transaction = _session.BeginTransaction())
{
_session.Delete(entity);
transaction.Commit();
}
}
public T Find<T>(Expression<Func<T,bool>> expression) where T : Entity
{
return _session.QueryOver<T>().Where(expression).SingleOrDefault();
}
public async Task<IList<T>> FindAll<T>(Expression<Func<T, bool>> expression) where T : Entity
{
return await _session.QueryOver<T>().Where(expression).ListAsync();
}
public void Save<T>(T entity) where T : Entity
{
using (var transaction = _session.BeginTransaction())
{
_session.Save(entity);
transaction.Commit();
}
}
public void Update<T>(T entity) where T : Entity
{
using (var transaction = _session.BeginTransaction())
{
_session.Update(entity);
transaction.Commit();
}
}
}
Solved the Problem by using entity Framework core
You can try .NET Standard implementation of an SQLite wrapper, there are two differences:
Provide a .NET Standard library that sits across the three main platforms: UWP, Android, and iOS with no platform specific libraries.
Provide flat table access with SQL queries. I.e. not being forced to use language integrated ORM like functionality.
https://github.com/MelbourneDeveloper/SQLite.Net.Standard
In my project ,I used repository and used dependency injection (Autofac Mvc).
My IReposiroty =
public interface IRepository<T> where T:class
{
IEnumerable<T> GetAll();
T GetById(int id);
T Get(Expression<Func<T,bool>> expression);
IQueryable<T> GetMany(Expression<Func<T, bool>> expression);
bool Insert(T obj);
bool Update(T obj);
bool Delete(int id);
int Count();
bool Save();
}
My IPropertyOptionLangRepository
public interface IPropertyOptionLangRepository : IRepository<PropertyOptionLang>
{
}
My PropertyOptionRepository (Just Insert and Save Methods)
public bool Insert(PropertyOptionLang obj)
{
try
{
_database.PropertyOptionLang.Add(obj);
var num=_database.SaveChanges();
return true;
}
catch(Exception e)
{
Console.WriteLine(e);
return false;
}
}
public bool Save()
{
try
{
_database.SaveChanges();
return true;
}
catch
{
return false;
}
}
And My Controller ( constructor and Insert Method)
private readonly IPropertyOptionRepository _propertyOptionRepository ;
private readonly IPropertyOptionLangRepository _propertyOptionLangRepository ;
private readonly ILanguageRepository _languageRepository;
public FieldController(IPropertyOptionRepository propertyOptionRepository,
IPropertyOptionLangRepository propertyOptionLangRepository,
ILanguageRepository languageRepository)
{
_languageRepository = languageRepository;
_propertyOptionRepository = propertyOptionRepository;
_propertyOptionLangRepository = propertyOptionLangRepository;
}
public ActionResult Add()
{
PropertyOptionLang test3 = new PropertyOptionLang();
var option = _propertyOptionRepository.GetById(2);
var lang2 = _languageRepository.GetById(2);
test3.Language = lang2;
test3.PropertyOption = option;
test3.Name = "hasan";
test3.Prefix = "test2";
test3.Value = "aaa";
_propertyOptionLangRepository.Insert(test3);
_propertyOptionLangRepository.Save();
}
exception message is : " e.Message "An entity object cannot be referenced by multiple instances of IEntityChangeTracker."
thanks for help..
Note: I search already for this exception message but I failed again
Edit: For Autofac configurate I created a new class and update global.asax for startup. `public static class Bootstrapper
{
public static void RunConfig()
{
BuildAutofac();
}
private static void BuildAutofac()
{
var builder = new ContainerBuilder();
builder.RegisterControllers(typeof(MvcApplication).Assembly);
builder.RegisterType<LanguageRepository>().As<ILanguageRepository>();
builder.RegisterType<PropertyOptionLangRepository>().As<IPropertyOptionLangRepository>();
builder.RegisterType<PropertyOptionRepository>().As<IPropertyOptionRepository>();
var container = builder.Build();
DependencyResolver.SetResolver(new AutofacDependencyResolver(container));
}
}`
I get a weird behavior with NHibernate with Fluent Configuration.
Whenever a generic exception unrelated to the NHibernate occurs i.e. in the view a DivideByZeroException every request after the exception throws.
An exception of type 'NHibernate.LazyInitializationException' occurred in NHibernate.dll but was not handled in user code. Additional information: Initializing[Entity]-Could not initialize proxy - no Session.
Due to nature of the bug the bug is critical due to the fact that 1 user can make the whole website dead if he generates an exception
Following it is my HttpModule for Nhibernate with Asp.Net MVC 5 that takes care of sessions.
NHibernateSessionPerRequest.cs
public class NHibernateSessionPerRequest : IHttpModule
{
private static readonly ISessionFactory SessionFactory;
// Constructs our HTTP module
static NHibernateSessionPerRequest()
{
SessionFactory = CreateSessionFactory();
}
// Initializes the HTTP module
public void Init(HttpApplication context)
{
context.BeginRequest += BeginRequest;
context.EndRequest += EndRequest;
}
// Disposes the HTTP module
public void Dispose() { }
// Returns the current session
public static ISession GetCurrentSession()
{
return SessionFactory.GetCurrentSession();
}
// Opens the session, begins the transaction, and binds the session
private static void BeginRequest(object sender, EventArgs e)
{
ISession session = SessionFactory.OpenSession();
session.BeginTransaction();
CurrentSessionContext.Bind(session);
}
// Unbinds the session, commits the transaction, and closes the session
private static void EndRequest(object sender, EventArgs e)
{
ISession session = CurrentSessionContext.Unbind(SessionFactory);
if (session == null) return;
try
{
session.Transaction.Commit();
}
catch (Exception)
{
session.Transaction.Rollback();
throw;
}
finally
{
session.Close();
session.Dispose();
}
}
// Returns our session factory
private static ISessionFactory CreateSessionFactory()
{
if (HttpContext.Current != null) //for the web apps
_configFile = HttpContext.Current.Server.MapPath(
string.Format("~/App_Data/{0}", CacheFile)
);
_configuration = LoadConfigurationFromFile();
if (_configuration == null)
{
FluentlyConfigure();
SaveConfigurationToFile(_configuration);
}
if (_configuration != null) return _configuration.BuildSessionFactory();
return null;
}
// Returns our database configuration
private static MsSqlConfiguration CreateDbConfigDebug2()
{
return MsSqlConfiguration
.MsSql2008
.ConnectionString(c => c.FromConnectionStringWithKey("MyConnection"));
}
// Updates the database schema if there are any changes to the model,
// or drops and creates it if it doesn't exist
private static void UpdateSchema(Configuration cfg)
{
new SchemaUpdate(cfg)
.Execute(false, true);
}
private static void SaveConfigurationToFile(Configuration configuration)
{
using (var file = File.Open(_configFile, FileMode.Create))
{
var bf = new BinaryFormatter();
bf.Serialize(file, configuration);
}
}
private static Configuration LoadConfigurationFromFile()
{
if (IsConfigurationFileValid == false)
return null;
try
{
using (var file = File.Open(_configFile, FileMode.Open))
{
var bf = new BinaryFormatter();
return bf.Deserialize(file) as Configuration;
}
}
catch (Exception)
{
return null;
}
}
private static void FluentlyConfigure()
{
if (_configuration == null)
{
_configuration = Fluently.Configure()
.Database(CreateDbConfigDebug2)
.CurrentSessionContext<WebSessionContext>()
.Cache(c => c.ProviderClass<SysCacheProvider>().UseQueryCache())
.Mappings(m => m.FluentMappings.AddFromAssemblyOf<EntityMap>()
.Conventions.Add(DefaultCascade.All(), DefaultLazy.Always()))
.ExposeConfiguration(UpdateSchema)
.ExposeConfiguration(c => c.Properties.Add("cache.use_second_level_cache", "true"))
.BuildConfiguration();
}
}
private static bool IsConfigurationFileValid
{
get
{
var ass = Assembly.GetAssembly(typeof(EntityMap));
var configInfo = new FileInfo(_configFile);
var assInfo = new FileInfo(ass.Location);
return configInfo.LastWriteTime >= assInfo.LastWriteTime;
}
}
private static Configuration _configuration;
private static string _configFile;
private const string CacheFile = "hibernate.cfg.xml";
}
Edit
The Repository Implementation i use
public class Repository<T> : IIntKeyedRepository<T> where T : class
{
private readonly ISession _session;
public Repository()
{
_session = NHibernateSessionPerRequest.GetCurrentSession();
}
#region IRepository<T> Members
public bool Add(T entity)
{
_session.Save(entity);
return true;
}
public bool Add(System.Collections.Generic.IEnumerable<T> items)
{
foreach (T item in items)
{
_session.Save(item);
}
return true;
}
public bool Update(T entity)
{
_session.Update(entity);
return true;
}
public bool Delete(T entity)
{
_session.Delete(entity);
return true;
}
public bool Delete(System.Collections.Generic.IEnumerable<T> entities)
{
foreach (T entity in entities)
{
_session.Delete(entity);
}
return true;
}
#endregion
#region IIntKeyedRepository<T> Members
public T FindBy(int id)
{
return _session.Get<T>(id);
}
#endregion
#region IReadOnlyRepository<T> Members
public IQueryable<T> All()
{
return _session.Query<T>();
}
public T FindBy(System.Linq.Expressions.Expression<System.Func<T, bool>> expression)
{
return FilterBy(expression).Single();
}
public IQueryable<T> FilterBy(System.Linq.Expressions.Expression<System.Func<T, bool>> expression)
{
return All().Where(expression).AsQueryable();
}
#endregion
}
Edit 2
The base controller class I use
public class BaseController : Controller
{
private readonly IRepository<UserEntity> _userRepository;
public BaseController()
{
_userRepository = new Repository<UserEntity>();
BaseModel = new LayoutModel {Modals = new List<string>()};
}
public UserEntity LoggedUser { get; set; }
public LayoutModel BaseModel { get; set; }
protected override void OnActionExecuting(ActionExecutingContext ctx)
{
base.OnActionExecuting(ctx);
if (HttpContext.User.Identity.IsAuthenticated)
{
if (Session != null && Session["User"] != null)
{
LoggedUser = (User) Session["User"];
}
var curUsername = HttpContext.User.Identity.Name;
if (LoggedUser == null || LoggedUser.Entity2.un!= curUsername)
{
LoggedUser = _userRepository.FindBy(u => u.Entity2.un== curUsername);
Session["User"] = LoggedUser;
}
BaseModel.LoggedUser = LoggedUser;
BaseModel.Authenticated = true;
}
else
{
LoggedUser = new UserEntity
{
Entity= new Entity{un= "Guest"},
};
BaseModel.LoggedUser = LoggedUser;
}
}
}
The extended question and all the snippets - are finally helping to find out where is the issue.
There is a really big issue: Session["User"] = LoggedUser;
This would hardly work. Why?
because we place into long running object (Web Session)
an instance loaded via very shortly lasting Web Request
Not all its properties will/could be loaded, When we place LoggedUser into session. It could be just a root entity with many proxies representing references and collections. These will NEVER be loaded later, because its Mather session is closed... gone
Solution?
I would use .Clone() of the User object. In its implementation we can explicitly load all needed references and collections and clone them as well. Such object could be placed into the Web Session
[Serializable]
public class User, ICloneable, ...
{
...
public override object Clone()
{
var entity = base.Clone() as User;
entity.Role = Role.Clone() as Role;
...
return entity;
}
So, what would be placed into session?
Session["User"] = LoggedUser.Clone();
As Radim Köhler noted i was saving a lazy-loaded object in Session that caused the problem.
But i wanted to avoid the Serilization of all objects and i fixed it as follows.
I added the following method to eager-load an entity instead of lazy
public T FindByEager(int id)
{
T entity = FindBy(id);
NHibernateUtil.Initialize(entity);
return entity;
}
And changed BaseController to
if (Session != null) Session["User"] = userRepository.FindByEager(LoggedUser.Id);
I'm using Fluent nHibernate and Oracle database. I's working fine, but there is one problem, that i can't figure out.
I'm trying to create new session, after previos has been killed. But there is no new session in session list in pl/sql developer and query in second session throw exception like in first one. So my question is what i'm doing wrong or what i'm missing.
public static class FluentNHibernateHelper
{
private static ISessionFactory _sessionFactory;
public static ISessionFactory SessionFactory
{
get
{
if (_sessionFactory == null)
{
var dbConfig = OracleDataClientConfiguration.Oracle10
.ConnectionString(c => c.Is(RmsConnection.ConnectionString))
.ShowSql()
.FormatSql()
.Driver<OracleDataClientDriver>();
_sessionFactory = Fluently.Configure()
.Database(dbConfig)
.Mappings(m => m.FluentMappings.AddFromAssemblyOf<RmsDu.Data.Model.MessageHead>())
.BuildSessionFactory();
}
return _sessionFactory;
}
}
/// <summary>
/// Open new db session
/// </summary>
/// <returns></returns>
public static ISession OpenSession()
{
return SessionFactory.OpenSession();
}
}
[Test]
public void SessionKillTest()
{
try
{
using (var session = FluentNHibernateHelper.OpenSession())
{
var q = session.Query<Data.Model.MessageType>();
//Here we kill first session
q.ToList();
}
}
catch (Exception ex) {}
using (var session = FluentNHibernateHelper.OpenSession())
{
var q = session .Query<Data.Model.MessageType>();
q.ToList();
}
}
The answer is that you have to dispose session.Connection manually before session dispose
Here is a image from the ANTS memory profiler. It seens that there are a lot of objects hold in memory. How can I find what I am doing wrong?
**UPDATE**
Here is my repository classes:
public class Repository<T> : IRepository<T> where T : class, IDataEntity
{
ObjectContext _context;
IObjectSet<T> _objectSet;
readonly string _entitySetName;
readonly string[] _keyNames;
private ObjectContext Context
{
get
{
if (_context == null)
{
_context = GetCurrentUnitOfWork<EFUnitOfWork>().Context;
}
return _context;
}
}
private IObjectSet<T> ObjectSet
{
get
{
if (_objectSet == null)
{
_objectSet = this.Context.CreateObjectSet<T>();
}
return _objectSet;
}
}
public TUnitOfWork GetCurrentUnitOfWork<TUnitOfWork>() where TUnitOfWork : IUnitOfWork
{
return (TUnitOfWork)UnitOfWork.Current;
}
public virtual IEnumerable<T> GetQuery()
{
return ObjectSet;
}
public virtual IEnumerable<T> GetQuery(params Expression<Func<T, object>>[] includes)
{
return ObjectSet.IncludeMultiple(includes);
}
public virtual IEnumerable<T> GetQuery(
IEnumerable<Expression<Func<T, bool>>> filters,
Func<IQueryable<T>, IOrderedQueryable<T>> orderBy,
IEnumerable<Expression<Func<T, object>>> includes)
{
IQueryable<T> _query = ObjectSet;
if (filters != null)
{
foreach (var filter in filters)
{
_query = _query.Where(filter);
}
}
if (includes != null && includes.Count() > 0)
{
_query = _query.IncludeMultiple(includes.ToArray());
}
if (orderBy != null)
{
_query = orderBy(_query);
}
return _query;
}
public virtual IPaged<T> GetQuery(
IEnumerable<Expression<Func<T, bool>>> filters,
Func<IQueryable<T>, IOrderedQueryable<T>> orderBy,
int pageNumber, int pageSize,
IEnumerable<Expression<Func<T, object>>> includes)
{
IQueryable<T> _query = ObjectSet;
if (filters != null)
{
foreach (var filter in filters)
{
_query = _query.Where(filter);
}
}
if (orderBy != null)
{
_query = orderBy(_query);
}
IPaged<T> page = new Paged<T>(_query, pageNumber, pageSize, includes);
return page;
}
public virtual void Insert(T entity)
{
this.ObjectSet.AddObject(entity);
}
public virtual void Delete(T entity)
{
if (entity is ISoftDeletable)
{
((ISoftDeletable)entity).IsDeleted = true;
//Update(entity);
}
else
{
this.ObjectSet.DeleteObject(entity);
}
}
public virtual void Attach(T entity)
{
ObjectStateEntry entry = null;
if (this.Context.ObjectStateManager.TryGetObjectStateEntry(entity, out entry) == false)
{
this.ObjectSet.Attach(entity);
}
}
public virtual void Detach(T entity)
{
ObjectStateEntry entry = null;
if (this.Context.ObjectStateManager.TryGetObjectStateEntry(entity, out entry) == true)
{
this.ObjectSet.Detach(entity);
}
}
}
Now, if I have class A that holds records from table A, I also create class:
public class ARepository:BaseRepository<A> {
// Implementation of A's queries and specific db operations
}
Here is my EFUnitOfWork class:
public class EFUnitOfWork : IUnitOfWork, IDisposable
{
public ObjectContext Context { get; private set; }
public EFUnitOfWork(ObjectContext context)
{
Context = context;
context.ContextOptions.LazyLoadingEnabled = true;
}
public void Commit()
{
Context.SaveChanges();
}
public void Dispose()
{
if (Context != null)
{
Context.Dispose();
}
GC.SuppressFinalize(this);
}
}
And UnitOfWork class:
public static class UnitOfWork
{
private const string HTTPCONTEXTKEY = "MyProj.Domain.Business.Repository.HttpContext.Key";
private static IUnitOfWorkFactory _unitOfWorkFactory;
private static readonly Hashtable _threads = new Hashtable();
public static void Commit()
{
IUnitOfWork unitOfWork = GetUnitOfWork();
if (unitOfWork != null)
{
unitOfWork.Commit();
}
}
public static IUnitOfWork Current
{
get
{
IUnitOfWork unitOfWork = GetUnitOfWork();
if (unitOfWork == null)
{
_unitOfWorkFactory = ObjectFactory.GetInstance<IUnitOfWorkFactory>();
unitOfWork = _unitOfWorkFactory.Create();
SaveUnitOfWork(unitOfWork);
}
return unitOfWork;
}
}
private static IUnitOfWork GetUnitOfWork()
{
if (HttpContext.Current != null)
{
if (HttpContext.Current.Items.Contains(HTTPCONTEXTKEY))
{
return (IUnitOfWork)HttpContext.Current.Items[HTTPCONTEXTKEY];
}
return null;
}
else
{
Thread thread = Thread.CurrentThread;
if (string.IsNullOrEmpty(thread.Name))
{
thread.Name = Guid.NewGuid().ToString();
return null;
}
else
{
lock (_threads.SyncRoot)
{
return (IUnitOfWork)_threads[Thread.CurrentThread.Name];
}
}
}
}
private static void SaveUnitOfWork(IUnitOfWork unitOfWork)
{
if (HttpContext.Current != null)
{
HttpContext.Current.Items[HTTPCONTEXTKEY] = unitOfWork;
}
else
{
lock(_threads.SyncRoot)
{
_threads[Thread.CurrentThread.Name] = unitOfWork;
}
}
}
}
Here is how I use this:
public class TaskPriceRepository : BaseRepository<TaskPrice>
{
public void Set(TaskPrice entity)
{
TaskPrice taskPrice = GetQuery().SingleOrDefault(x => x.TaskId == entity.TaskId);
if (taskPrice != null)
{
CommonUtils.CopyObject<TaskPrice>(entity, ref taskPrice);
}
else
{
this.Insert(entity);
}
}
}
public class BranchRepository : BaseRepository<Branch>
{
public IList<Branch> GetBranchesList(Guid companyId, long? branchId, string branchName)
{
return Repository.GetQuery().
Where(b => companyId == b.CompanyId).
Where(b => b.IsDeleted == false).
Where(b => !branchId.HasValue || b.BranchId.Equals(branchId.Value)).
Where(b => branchName == null || b.BranchName.Contains(branchName)).
ToList();
}
}
[WebMethod]
public void SetTaskPrice(TaskPriceDTO taskPrice)
{
TaskPrice tp = taskPrice.ToEntity();
TaskPriceRepository rep = new TaskPriceRepository();
rep.Set(tp);
UnitOfWork.Commit();
}
[WebMethod]
public IList<Branch> GetBranchesList()
{
BranchRepository rep = new BranchRepository();
return rep.GetBranchesList(m_User.UserCompany.CompanyId, null, null).ToList();
}
I hope this is enough info to help me solving the problem. Thanks.
UPDATE 2
There is also UnitOfWorkFactory that initializes UnitOfWork:
public class UnitOfWorkFactory : IUnitOfWorkFactory
{
private static Func<ObjectContext> _objectContextDelegate;
private static readonly Object _lockObject = new object();
public static void SetObjectContext(Func<ObjectContext> objectContextDelegate)
{
_objectContextDelegate = objectContextDelegate;
}
public IUnitOfWork Create()
{
ObjectContext context;
lock (_lockObject)
{
context = _objectContextDelegate();
}
return new EFUnitOfWork(context);
}
}
In order to use this, in the application startup I use structuremap:
ObjectFactory.Initialize(x =>
{
x.For<IUnitOfWorkFactory>().Use<UnitOfWorkFactory>();
x.For(typeof(IRepository<>)).Use(typeof(Repository<>));
});
I have a hunch you don't dispose the context.
I suggest disposing the context whenever you done interacting with database.
Use using statement whenever you create the context.
[Edit]
As far as I can see, you cache and don't dispose your EFUnitOfWork object. It is disposable, which is correct, but I don't see when disposable is called. Seems like you hold a reference to the context for all application run time. Moreover, you create and hold one context per thread, which will make it even worse.
I can't tell you for sure where you should put Dispose or using, as I don't know the usages.
You could put it probably to your Commit method, but I don't know if the Commit called only once during database interaction session.
Also, your design might be overcomplicated.
If I were you, I would:
Find the way to dispose the context using current code, as a short-term solution
Simplify the design, as the long-term solution
If I had time I would do long-term solution right away.
But again, I can't tell if the complexity of your design is justified, as I don't know how big your application is and what it does and what the requirements are.
Couple of things come to my mind:
You aren't probably Disposing the ObjectContext. Make sure all your database codes are within using(var context = CreateObjectContext()) block
You have an N-tier architecture and you are passing entities from the data access layer to upper layer without Detaching the entities from ObjectContext. You need to call ObjectContext.Detach(...)
You are most likely returning a full collection of entities, instead of returning a single enity for single Get operations. For ex, you have queries like from customer in context.Customers select customer instead of doing from customer in context.Customers select customer.FirstOrDefault()
I have had hard time making Entity Framework to work in an N-tier application. It's just not suitable for using in N-tier apps as is. Only EF 4.0 is. You can read about all my adventure in making EF 3 work in an N-tier app.
http://www.codeproject.com/KB/linq/ef.aspx
Does this answer your question?
Do you clear the ObjectContext once in a while. If you keep an ObjectContext alive for a long time this will consume memory related to the size of the EntityDataModel and the number of Entities loaded into this ObjectContext.
I had the same problem in a class which uses dependency injection, so the using() option was not an alternative. My solution was to add DbContextOptions<Context> to the constructor and as a private field to the class. Then, you can call
_db.Dispose();
_db = new BlockExplorerContext(_dBContextOptions);
at appropriate times. This fixed my problem where I was running out of RAM and the application was killed by the OS.