I have got an app I'm going to write in ASP.NET MVC and I want to create a DatabaseFactory object something like this:-
public class DatabaseFactory
{
private string dbConn get { return <gets from config file>; }
public IDatabaseTableObject GetDatabaseTable()
{
IDatabaseTableObject databaseTableObject = new SQLDatabaseObject(dbConn);
return databaseTableObject;
}
}
and this works fine, but I obviously have to instantiate the DatabaseFactory in every controller that needs it. If I made this static, so I could, in theory just call DatabaseFactory.GetDatabaseTable() it would cause a memory leak, wouldn't it?
---------edit-------
maybe I need to add in a bit more detail.
If I had the above code like this:-
public static class DatabaseFactory
{
private static string dbConn get { return <gets from config file>; }
public static IDatabaseTableObject GetDatabaseTable()
{
IDatabaseTableObject databaseTableObject = new SQLDatabaseObject(dbConn);
return databaseTableObject;
}
}
then instead of my controller code saying
DatabaseFactory databaseFactory = new DatabaseFactory();
var tableObject = databaseFactory.GetDatabaseTable();
List<DbRecord> records = tableObject.Table.ToList();
I just said
var tableObject = DatabaseFactory.GetDatabaseTable();
List<DbRecord> records = tableObject.Table.ToList();
wouldn't that result in the DB connection remaining open once the controller's action method had been GCed?
Since you don't store the object anywhere, this would not leak memory.
Apparently this will cause a memory leak.
Related
I have simple classes to saves and get data (not like repository pattern). But while saving data to multiple tables I want to maintain a transaction. So I just went through Unit of work pattern, but that will require me to do a lot of changes. So I'm thinking if my approach will do the same as UOF.
Here's my code:
CalalogueRepository:
public interface ICalalogueRepository
{
void Create(string guid, string fileName);
}
public class CalalogueRepository : ICalalogueRepository
{
private CatalogueContext _catalogueContext;
public CalalogueRepository(CatalogueContext catalogueContext)
{
_catalogueContext = catalogueContext;
}
public void Create(string guid, string fileName)
{
_catalogueContext.Catalogues.Add(new Catalogue
{
CatalogueId = guid,
FileName = fileName
});
}
}
StuffRepo:
public interface IStuffRepo
{
void Create(string guid, List<StuffModel> myStuff);
}
public class StuffRepo : IStuffRepo
{
private CatalogueContext _catalogueContext;
public StuffRepo(CatalogueContext catalogueContext)
{
_catalogueContext = catalogueContext;
}
public void Create(string guid, List<StuffModel> myStuff)
{
//add stuff to _catalogueContext.StuffTable.Add
}
}
Finally a class that does the SaveChanges and Commit:
public class UOW : IUOW
{
private CatalogueContext _catalogueContext;
private ICalalogueRepository _calalogueRepo;
private IStuffRepo _stuffRepo;
public UOW(CatalogueContext catalogueContext,
ICalalogueRepository calalogueRepo,
IStuffRepo stuffRepo)
{
_catalogueContext = catalogueContext;
_calalogueRepo = calalogueRepo;
_stuffRepo = stuffRepo;
}
public void Save (string guid, string fileName, List<StuffModel> myStuff)
{
using (IDbContextTransaction transection = _catalogueContext.Database.BeginTransaction())
{
_calalogueRepo.Create(guid, fileName);
_stuffRepo.Create (guid, myStuff);
_catalogueContext.SaveChanges();
transection.Commit();
}
}
}
I think there is only 1 CatalogueContext throughout the call.
Ok, so as you can see here, AddDbContext is the right way to register it as you wrote in the comment on the question.
Here it says that AddDbContext will register the context as scoped.
And here you can find what scoped means.
Overall I think you are right that your code will use the same context throughout the Save method.
Couple thoughts:
Probably you want to have a try-catch in case an exception is thrown and you want to rollback
If you are not sure if it's working why not try it? You should test your code/application anyways.
Probably this could be done in a better way, but I don't have the context about the rest of your code/application, so I cannot tell. (Not sure what you mean by "...Unit of work pattern, but that will require me to do a lot of changes." for example.)
Now the Create methods not self-contained, meaning if you just want to add a new item to the table it is not enough to call Create, but separately call SaveChanges(). This is not an explicit problem, but has to be kept in mind and might be a little bit confusing for new developers on the project.
I have the following example which will illustrate my question:
namespace ConsoleApplication1
{
class Program
{
static void Main(string[] args)
{
UserService.CreateUser("NewUser");
}
}
public static class UserService
{
public static void CreateUser(string userName)
{
// DB Context created to CREATE the new user
using (var db = new TestEntities())
{
User userToAdd = new User();
userToAdd.name = userName;
userToAdd.jobTitle = JobService.LookupJob(userName);
db.Users.Add(userToAdd);
db.SaveChanges();
}
}
}
public static class JobService
{
public static string LookupJob(string userName)
{
// 'nested' using, which opens up a new connection to 'read'
using (var db = new TestEntities())
{
return db.JobTitlesNames.Single(a => a.userName = userName).title;
}
}
}
public class TestEntities : DbContext
{
}
}
This is a very noddy example of a scenario I have on a bigger scale. I guess my question is: can this method of 'nested' DbContext creations cause application/sql problems in a large scale application. I know that this sort of structure is bad practice, and generally you should use the same DbContext per business transaction ( in this case, the business transaction being creating a new user ), but if the 'nested' DbContext is only used to read data, never modify, is it still dangerous?
The real-world system I have uses this sort of structure massively, occasionally going down to 4-5 levels of nesting. I'm having ( not going to cover it in detail here, this question is more around theory ) database read/commit problems and I'm wondering if this could be the route cause.
Thanks
I am creating a view that displays all the information of a product on for my website. I want to create a "Details" service class, that handles most of my querying.
I binded my session to to my data in a GeneralWebApplicationDataNinjectModule:
Bind<IContentService>().To<ContentService>().InRequestScope();
Bind<ISession>()
.ToMethod(
context =>
context.Kernel.Get<IMasterSessionSource>()
.ExposeConfiguration()
.BuildSessionFactory()
.OpenSession()
)
.WhenInjectedInto<IContentService>()
.InRequestScope();
I have a ContentService class that pull all my data in that looks like this:
public interface IContentService
{
IQueryable<Program> Programs { get; }
}
public class ContentService : IContentService
{
private readonly ISession _session;
public ContentService(ISession session)
{
_session = session;
}
public IQueryable<Program> Programs
{
get { return _session.Query<Program>(); }
}
}
...this works fine when i load it into my Controller:
public class ProgramController : Controller
{
private static IContentService _contentService;
public ProgramController (IContentService contentService)
{
_contentService = contentService;
}
public ActionResult Details(string item)
{
var ProgramDetails = DetailsService.ProgramsFind();
//this is where i want to use the service class to pull data in DetailsService.
var model = new DetailPageViewModel
{
ProgramTables = GetUpComingCourses(),
};
return View("../Shared/DetailView", model);
}
so i created a new service class to pull details trying to using ContentService:
public class DetailsService
{
private static IContentService _contentService;
public DetailsService(IContentService contentService)
{
_contentService = contentService;
}
public static IEnumerable<Programs> ProgramsFind()
{
var results =
_contentService
.Programs
.Where(
d => d.TypeId >= 16 || d.TypeId <= 18);
return results;
}
i get a System.NullReferenceException was unhandled by user code, Message=Object reference not set to an instance of an object on the line in ProgramsFind() where im calling _contentService. When i debug that it says its null.
So i realized this is because the data isnt binded to Details Service. When i enter in my InjectionDependency this:
Bind<IContentService>().To<DetailsService>().InRequestScope();
i get a lot of errors where it wants me to implement everything thats in ContentService in DetailsService but i dont want to replicate that. i just want to use the content that ContentService found in my DetailsService. Much like how the controller easily does it.
I feel like theres something im missing...
UPDATE
i think i might have figured something out. I added IDetailsService to the Controller, and it seems to be successfully pulling queries.
private static IContentService _contentService;
// new
private static IDetailsService _detailsService;
public MobileController(IContentService contentService, IDetailsService detailsService)
{
_contentService = contentService;
//new
_detailsService = detailsService;
}
Yes that was the answer!
Create Service
Bind Sessions to Service
Add Service to the Controller.
Call Service when necessary.
As always, just something overlooked!
I have a class that handles all database operations. I've read that it's better to use multiple DataContext instances for different read/write/update/delete operations as opposed to one DataContext instance that lives a long time.
That means every function that does read/write/update/delete on the DB needs to do this:
public int GetSomeID(string name)
{
using (XXDataContext context = new XXDataContext(connStr))
{
...
}
}
public int GetAnotherID(string name)
{
using (XXDataContext context = new XXDataContext(connStr))
{
...
}
}
public void WriteSomething(string text)
{
using (XXDataContext context = new XXDataContext(connStr))
{
...
}
}
opposed to having this using() only in constructor and having the context as a private member variable available to every function call.
With keeping the functionality of creating a new DataContext with every function call, is it possible to move this using() somewhere else so not every single function has to have this line in it?
You can use a method like this to avoid re-writing the using code:
private static void WithContext(Action<XXDataContext> action)
{
using(XXDataContext context = new XXDataContext(connStr))
action(context);
}
private static T WithContext<T>(Func<XXDataContext, T> function)
{
using(XXDataContext context = new XXDataContext(connStr))
return function(context);
}
This allows you to write:
public int GetSomeID(string name)
{
WithContext(context =>
{
//TODO use context
});
}
If that helps you.
Sorry for not answering your question directly:
You have read right thing. Context implements Unit Of Work pattern and supposed to be used like that.
However there can be a valid case when you need to do several operations within the same context and it would've been nice to right code like that:
using(var dal = new MyDalUOW()) {
dal.Delete(s1);
dal.Update(s2);
dal.Get(s3);
dal.Commit()
}
In order to have this you will create your Dal class which will implement IDisposable and will have method Commit
public class BaseDal: IDisposable {
private MyDbContext _context;
public BaseDal() { _context = new MyDbContext; }
public void Commit() { _context.SaveChanges(); }
}
And all your methods will use _context to perform operation.
So you will still have all those usings but in code using your DAL, not in the DAL itself.
If you use it in desktop/windows application it is no problem to have single DataContext, but you have to handle it wisely (according to the db technology, eg. EF or linq2sql) against synchronizing the datacontext cache data with database. And the one thing more, you have to use separate datacontext per thread.
I you use it in WEB app (ASP NET) then it is recommended and it is good practise to create a new instance of the datacontext per request (and dispose it on finish the request).
So it depends on your solution.
Alright, consider this code:
using (var fileStream = new FileStream(#"C:\temp\test.txt", FileMode.Open))
{
var bytes = new byte[fileStream.Length];
fileStream.Read(bytes, 0, (int)fileStream.Length);
var text = Encoding.Default.GetString(bytes);
Console.WriteLine(text);
}
It leverages a class that uses IDisposable; just like the DataContext. Now, let's say we'd like to get rid of that using; we can do this:
Read(fileStream =>
{
var bytes = new byte[fileStream.Length];
fileStream.Read(bytes, 0, (int)fileStream.Length);
var text = Encoding.Default.GetString(bytes);
Console.WriteLine(text);
}, #"C:\temp\test2.txt");
static void Read(Action<FileStream> action, string path)
{
using (var fileStream = new FileStream(path, FileMode.Open))
{
action(fileStream);
}
}
Here you are injecting the code you want to execute while ensuring that the using statement is removed from the method. Further, you can guarantee that the using statement is going to be used. The output of this program is as expected:
Hello, World!
Hello, World; from an injected function!
Press any key to continue . . .
Sorry if i dont explain myself clearly but I can't seem to wrap my head around how to present the problem at hand.
We have some utility classes used in web and windows forms development. We use the following pattern in order to dynamically call the corresponding methods depending on whether the code runs inside a web or in windows forms.
The following code is a stripped down version so you can observe the logic clearlier:
public static class Constants
{
private static ConstantsWinFormsWebCommonProvider _Provider;
public static ConstantsWinFormsWebCommonProvider Provider
{
get
{ /*in websites the static variables reset to null in case of Appdomain recycle
*so we check beforehand, this also serves for first time use in both: web and winforms */
//lazy loading, initializes when provider is required for the first time or if gets null because appdomain recycle...
if (_Provider != null) return _Provider;
_Provider = Fun.WebSite()? (ConstantsWinFormsWebCommonProvider)new ConstantsWebProvider() : new ConstantsWinFormsProvider();
Initialize();
return _Provider;
}
}
public static void Initialize()
{
Provider.Initialize();
}
public static string DataBaseName
{
get { return Provider.DataBaseName ; }
set { Provider.DataBaseName = value; }
}
}
public class ConstantsWinFormsWebCommonProvider
{
internal bool isInitialized { get; set; } //variable only used in the winform part for now, shouldnt affect the web part issue
internal string DataBaseName { get; set; }
}
public class ConstantsWebProvider: ConstantsWinFormsWebCommonProvider
{
public override void Initialize()
{
base.Initialize();
string errordetails= "";
if (!Fun.InitializeWeb(ref errordetails)) //fills all the Provider properties and connects, otherwise returns false
{
throw new Exception("Problem initializating web " + errordetails));
}
}
}
public class ConstantsWinFormsProvider: ConstantsWinFormsWebCommonProvider
{
public new string DataBaseName
{
get
{//lazy loading so it connects only when required
if (isInitialized) //(!string.IsNullOrEmpty(base.DataBaseName))
{
return base.DataBaseName ;
}
bool resul = Fun.InicializeWinForms();
if (resul) return base.DataBaseName ;
MessageBox.Show("Problem initializing");
return null;
}
set
{
Fun.WinFormsValueSavingSistemx(value); //example call, not related with the issue at hand
}
}
}
The problem is that occasionally in the case of the web we get errors because the properties inside the Provider variable are null, but the Provider itself is not null.
In theory it shouldn't be possible (in the case of the web we initialize all the Provider properties in the Initialize() function and if the appdomain recycles causing the properties to go null the Provider variable should be null too causing it to call Initialize() again when you try to access one of its properties).
What's the cause and recommended solution to this problem?
Edit:
In case this provides more hindsight, the problem for now has happened in the first page load of one aspx (isPostback = false, where it would access the Provider.DataBaseName for the first time in the first visit to that aspx or if coming again from another aspx). Also in the documented cases they where using IE8 and IE9...