How come that when I try to add a case by using the bool method in CaseRepository.cs by assign a user to the case object to determine which user is the owner of the case (who created it?) - but when I try to assign it then it tries to add a new User object to the table too although it's an existing User I'm passing in as parameter?
In short: I'm trying to let a User create a case and assign that case to the user.
The reason for mapping MembershipUser to User object is to enable the EF (code first) to could use it for CRUD operations.
// CaseController.cs
[AuthWhere(AuthorizeRole.Developer)]
[HttpPost]
public ActionResult Create(CreateCaseViewModel model)
{
var success = false;
string message;
User user = Mapper.Map<MembershipUser, User>(_membershipService.GetUserByUserName(User.Identity.Name));
Case createCase = _caseService.CreateCase(model.Topic, model.Message);
bool addCase = _caseService.AddCase(createCase, user);
if(!addCase)
{
message = ControllerResources.CaseCreateFail;
}
else
{
success = true;
message = ControllerResources.CaseCreateSuccess;
}
return Json(new
{
Success = success,
Message = message,
Partial = RenderPartialViewToString("List", GetCases)
});
}
// RepositoryBase.cs
public class RepositoryBase<T> : IRepository<T> where T : class
{
public IDbContext Context { get; private set; }
public IDbSet<T> ObjectSet { get; private set; }
public RepositoryBase(IDbContext context)
{
Contract.Requires(context != null);
Context = context;
if (context != null)
{
ObjectSet = Context.CreateDbSet<T>();
if (ObjectSet == null)
{
throw new InvalidOperationException();
}
}
}
[ContractInvariantMethod]
private void Invariant()
{
Contract.Invariant(Context != null);
Contract.Invariant(ObjectSet != null);
}
public IRepository<T> Add(T entity)
{
ObjectSet.Add(entity);
return this;
}
public IRepository<T> SaveChanges()
{
Context.SaveChanges();
return this;
}
}
// CaseRepository.cs
public class CaseRepository : RepositoryBase<Case>, ICaseRepository
{
public CaseRepository(IDbContext context)
: base(context)
{
Contract.Requires(context != null);
}
public bool AddCase(Case #case, User user)
{
#case.User = user;
ObjectSet.Add(#case);
Context.SaveChanges();
return true;
}
}
If you are modifying existing object, then you don't need to add to context again. Therefore just remove this line for updates:
ObjectSet.Add(#case);
You may need to consider to create two separate methods, Add and Update in CaseRepository class.
Forgot to make 2 of my mappings in the POCO classes virtual.
Related
So I have a generic repository like this:
using System.Collections.Generic;
using System.Data.Entity;
using System.Threading.Tasks;
namespace TrackIt.UI.Data.Repositories
{
public class GenericRepository<TEntity, TContext> : IGenericRepository<TEntity>
where TContext: DbContext
where TEntity: class
{
protected readonly TContext Context;
protected GenericRepository(TContext context)
{
this.Context = context;
}
public virtual async Task<TEntity> GetByIdAsync(int id)
{
return await Context.Set<TEntity>().FindAsync(id);
}
public bool HasChanges()
{
return Context.ChangeTracker.HasChanges();
}
}
}
And a FriendRepository that inherits from it, with some of its own methods:
using System.Data.Entity;
using System.Threading.Tasks;
using TrackIt.DataAccess;
using TrackIt.Model;
namespace TrackIt.UI.Data.Repositories
{
public class FriendRepository : GenericRepository<Friend, TrackItDbContext>,
IFriendRepository
{
public FriendRepository(TrackItDbContext context): base(context)
{
}
public override async Task<Friend> GetByIdAsync(int id)
{
return await Context.Friends.Include(f => f.PhoneNumbers).SingleAsync(f => f.Id == id);
}
}
}
where TrackItDbContext is just a class inheriting from DbContext, where it has properties that "maps" to the database's tables.
Then, I have an implementing class like this:
namespace TrackIt.UI.ViewModel
{
public class FriendDetailViewModel : DetailViewModelBase, IFriendDetailViewModel
{
public bool HasChanges
{
get { return _hasChanges; }
set
{
if(_hasChanges != value)
{
_hasChanges = value;
OnPropertyChanged();
((DelegateCommand)SaveCommand).RaiseCanExecuteChanged();
}
}
}
private Friend _friend;
public Friend Friend
{
get { return _friend; }
private set
{
_friend = value;
OnPropertyChanged();
}
}
private ICommand _addPhoneNumberCommand;
public ICommand AddPhoneNumberCommand
{
get { return _addPhoneNumberCommand; }
set { _addPhoneNumberCommand = value; }
}
public ObservableCollection<FriendPhoneNumberDecorator> PhoneNumbers { get; }
private IFriendRepository _friendRepository;
private IMessageDialogService _messageDialogServiceProvider;
public FriendDetailViewModel(IFriendRepository friendRepository,
IEventAggregator eventAggregator,
IMessageDialogService messageDialogServiceProvider) : base(eventAggregator)
{
_friendRepository = friendRepository;
_messageDialogServiceProvider = messageDialogServiceProvider;
PhoneNumbers = new ObservableCollection<FriendPhoneNumberDecorator>();
AddPhoneNumberCommand = new DelegateCommand(OnAddPhoneNumberExecute);
DeletePhoneNumberCommand = new DelegateCommand(OnDeletePhoneNumberExecute, OnDeletePhoneNumberCanExecute);
}
private void FriendPhoneNumberWrapper_PropertyChanged(object sender, PropertyChangedEventArgs e)
{
// Ensures that the HasChanges property of the viewmodel is updated
if (!HasChanges)
{
HasChanges = _friendRepository.HasChanges();
}
// If the change in phone number causes validation errors, disable the save button
if (e.PropertyName == nameof(FriendPhoneNumberDecorator.HasErrors))
{
((DelegateCommand)SaveCommand).RaiseCanExecuteChanged();
}
}
private void OnAddPhoneNumberExecute()
{
var newNumber = new FriendPhoneNumber();
newNumber.PropertyChanged += FriendPhoneNumberWrapper_PropertyChanged;
PhoneNumbers.Add(newNumber);
Friend.PhoneNumbers.Add(newNumber);
newNumber.Number = "";
}
#endregion
}
}
When using this class, an instance of IFriendRepository is injected by a DI container and assigned to _friendRepository. The line Friend.Model.PhoneNumbers.Add in the OnAddPhoneNumberExecute method adds a PhoneNumber instance (newNumber) to Friend.PhoneNumber. This addition is tracked by Context in HasChanges method in the GenericRepository method (I checked this by debugging), and thus when the _friendRepository.HasChanges() method in FriendPhoneNumberWrapper_PropertyChanged event handler is triggered by the newNumber.Number = "" line, it returns true.
But, when I test if the HasChanges property of the class is updated when I raise the command AddPhoneNumberCommand using the following test:
namespace TrackIt.UITests.ViewModel
{
public class FriendDetailViewModelTests
{
private FriendDetailViewModel _viewModel;
private Mock<IEventAggregator> _eventAggregatorMock;
private Mock<IMessageDialogService> _messageDialogServiceMock;
private Mock<IFriendRepository> _friendRepositoryMock;
private readonly Friend testFriend = new Friend
{
Id = 2,
FirstName = "asdf",
LastName = "asdfasdf",
Email = "test#test.com",
FavoriteLanguage = new ProgrammingLanguage { Id = 1, Name = "C++"},
PhoneNumbers = new List<FriendPhoneNumber> {
new FriendPhoneNumber(){ id = 1, Number = "0123" },
new FriendPhoneNumber(){ id = 2, Number = "4567" },
}
};
public FriendDetailViewModelTests()
{
// Set up dependencies for FriendDetailViewModel class.
_messageDialogServiceMock = new Mock<IMessageDialogService>();
_eventAggregatorMock = new Mock<IEventAggregator>();
_friendRepositoryMock = new Mock<IFriendRepository>();
// Mock the friendrepository getbyidasync method
_friendRepositoryMock.Setup(dp => dp.GetByIdAsync(testFriend.Id)).ReturnsAsync(testFriend);
// Finally, create the FriendDetailViewModel!
_viewModel = new FriendDetailViewModel(_friendRepositoryMock.Object, _eventAggregatorMock.Object, _messageDialogServiceMock.Object);
}
[Fact]
public void ShouldAddNewPhoneNumberEvent()
{
// ARRANGE
// Assign testFriend to Friend (required so that the created phone number can be assigned to an instance, otherwise we get null reference exception)
PrivateObject _viewModelPrivated = new PrivateObject(_viewModel);
_viewModelPrivated.SetProperty(nameof(Friend), testFriend);
// Create a test empty phone number, to be compared later against the phone number created when user presses "Add"
var emptyPhoneNumber = new FriendPhoneNumber();
emptyPhoneNumber.Number = "";
// ACT
_viewModel.AddPhoneNumberCommand.Execute(null);
// ASSERT
// Check if the created phone number is an empty phone number, the dbContext sees there's a change,
// and that the save button can be pressed
Xunit.Assert.Contains(emptyPhoneNumber, _viewModel.PhoneNumbers,
new ByPropertyComparer<FriendPhoneNumberDecorator>(nameof(emptyPhoneNumber.Number)));
Xunit.Assert.True(_viewModel.HasChanges);
Xunit.Assert.True(_viewModel.SaveCommand.CanExecute(null));
}
}
}
the test fails at Xunit.Assert.True(_viewModel.HasChanges); line, which means the _friendRepository.HasChanges() is not updating automatically (i.e. not tracking) when the Friend.Model.PhoneNumbers.Add is called. I realize that this would be due to me mocking the IFriendRepository, and so it may not actually be tracking any changes. Am I right? Should I also mock HasChanges() of _friendRepositoryMock ? Or is there a better way to do this, maybe an in-memory database? But then it would not be a unit test?
Im runing a nancyfx with owin on centos 6.5 with mono 5.10.0.140, I change the default ViewLocationProvider to ResourceViewLocationProvider for the default ViewLocationProvider causes memory leak of somekind after running for days, and the ResourceViewLocationProvider dont have the same problem. I would like to hot update Views just like what we can do with a default ViewLocationProvider, but it seems impossibe when googling around.
I did find a partial solution though, by implenting a custom IViewLocator and a IViewCache, I did achieve someking of hot update. But It didn`t feel right aside from those ugly static class
//Here is what I did in the custom IViewLocator
//...class definition fallback viewlocator and other staffs
private static ConcurrentDictionary<string, ViewLocationResult> _cachedViewLocationResults;
//..other code
public ViewLocationResult LocateView(string viewName, NancyContext context)
{
//...lock and others
if (_cachedViewLocationResults != null && _cachedViewLocationResults.ContainsKey(viewName))
{
return _cachedViewLocationResults[viewName];
}
//...lock and others
return fallbackViewLocator.LocateView(viewName, context);
}
//...other class
//here is how I update Views
public static void UpdateCachedView(IDictionary<string, ViewLocationResult> replacements)
{
lock (CacheLock)
{
if(_cachedViewLocationResults == null)_cachedViewLocationResults = new ConcurrentDictionary<string, ViewLocationResult>();
foreach (var replace in replacements)
{
_cachedViewLocationResults.AddOrUpdate(replace.Key, x=>replacements[x], (x,y)=>y);
}
}
}
//END OF IViewLocator
//here is what I did in the custom IViewCache
//another static for ViewCache to tell if the view has been updated
public static List<ViewLocationResult> Exceptions { get; private set; }
//...some other code
//here is how I ignore the old cache
public TCompiledView GetOrAdd<TCompiledView>(ViewLocationResult viewLocationResult, Func<ViewLocationResult, TCompiledView> valueFactory)
{
if (Exceptions.Any(x=>x.Name == viewLocationResult.Name && x.Location == viewLocationResult.Location && x.Extension == viewLocationResult.Extension))
{
object old;
this.cache.TryRemove(viewLocationResult, out old);
Exceptions.Remove(viewLocationResult);
}
return (TCompiledView)this.cache.GetOrAdd(viewLocationResult, x => valueFactory(x));
}
With those implentions and a little bit of settings on the bootstrapper plus a router for some mysql update, I can update the View the way I want, but here is the problem:
1. now I have to manually map all the Location,Name,Extension for the ViewLocationResult to use and there are too many of them (243...), I would like to use the some sort of built-in function to identify the changes, something like the IsStale function of the ViewLocationResult, but I didnt know which and how...
2. those static class are ugly and I think it could be problematic but I didnt know a better way to replace them.
Could some one kindly give me a hint, thank in advance.
Well, I finally figure out how to do this myself, just in case anyone else want to use the same method as I do, Here is how you update your view in memory:
Make a interface
public interface INewViewLocationResultProvider
{
bool UseCachedView { get; set; }
ViewLocationResult GetNewerVersion(string viewName, NancyContext context);
void UpdateCachedView(IDictionary<string, ViewLocationResult> replacements);
}
Make a new ViewLocationResultProvider
public class ConcurrentNewViewLocationResultProvider : INewViewLocationResultProvider
{
private Dictionary<string, ViewLocationResult> _cachedViewLocationResults;
private readonly object _cacheLock = new object();
public bool UseCachedView { get; set; }
public ConcurrentNewViewLocationResultProvider()
{
lock (_cacheLock)
{
if(_cachedViewLocationResults == null)_cachedViewLocationResults = new Dictionary<string, ViewLocationResult>();
}
}
public ViewLocationResult GetNewerVersion(string viewName, NancyContext context)
{
if (UseCachedView)
{
if (Monitor.TryEnter(_cacheLock, TimeSpan.FromMilliseconds(20)))
{
try
{
if (_cachedViewLocationResults != null && _cachedViewLocationResults.ContainsKey(viewName))
{
return _cachedViewLocationResults[viewName];
}
}
finally
{
Monitor.Exit(_cacheLock);
}
}
}
return null;
}
public void UpdateCachedView(IDictionary<string, ViewLocationResult> replacements)
{
lock (_cacheLock)
{
if(_cachedViewLocationResults == null)_cachedViewLocationResults = new Dictionary<string, ViewLocationResult>();
foreach (var replace in replacements)
{
if (_cachedViewLocationResults.ContainsKey(replace.Key))
{
_cachedViewLocationResults[replace.Key] = replace.Value;
}
else
{
_cachedViewLocationResults.Add(replace.Key,replace.Value);
}
}
}
}
}
In your Bootstrapper,register the new ViewLocationResultProvider with tinyIoc or equivalent
container.Register<INewViewLocationResultProvider, ConcurrentNewViewLocationResultProvider>().AsSingleton();
Make a derived class from ViewLocationResult
public class OneTimeUsedViewLocationResult : ViewLocationResult
{
private bool _used = false;
public OneTimeUsedViewLocationResult(string location, string name, string extension, Func<TextReader> contents)
: base(location, name, extension, contents)
{
}
public override bool IsStale()
{
if (_used) return false;
_used = true;
return true;
}
}
And a new IViewLocator:
public class CachedViewLocator : IViewLocator
{
private readonly INewViewLocationResultProvider _newVersion;
private readonly DefaultViewLocator _fallbackViewLocator;
public CachedViewLocator(IViewLocationProvider viewLocationProvider, IEnumerable<IViewEngine> viewEngines, INewViewLocationResultProvider newVersion)
{
_fallbackViewLocator = new DefaultViewLocator(viewLocationProvider, viewEngines);
_newVersion = newVersion;
}
public ViewLocationResult LocateView(string viewName, NancyContext context)
{
if (_newVersion.UseCachedView)
{
var result = _newVersion.GetNewerVersion(viewName, context);
if (result != null) return result;
}
return _fallbackViewLocator.LocateView(viewName, context);
}
public IEnumerable<ViewLocationResult> GetAllCurrentlyDiscoveredViews()
{
return _fallbackViewLocator.GetAllCurrentlyDiscoveredViews();
}
}
}
Tell nancy about the new ViewLocator
protected override NancyInternalConfiguration InternalConfiguration
{
get
{
return NancyInternalConfiguration.WithOverrides
(
nic =>
{
nic.ViewLocationProvider = typeof(ResourceViewLocationProvider);//use this or your equivalent
nic.ViewLocator = typeof(CachedViewLocator);
}
);
}
}
Then you can update it through a API like this:
public class YourModule : NancyModule
{
public YourModule(INewViewLocationResultProvider provider)
{
Get["/yourupdateinterface"] = param =>
{
if(!provider.UseCachedView) return HttpStatusCode.BadRequest;//in case you turn off the hot update
//you can serialize your OneTimeUsedViewLocationResult with Newtonsoft.Json and store those views in any database, like mysql, redis, and load them here
//data mock up
TextReader tr = new StringReader(Resources.TextMain);
var vlr = new OneTimeUsedViewLocationResult("","index","cshtml",()=>tr);
var dir = new Dictionary<string, ViewLocationResult> {{"index",vlr}};
//mock up ends
provider.UpdateCachedView(dir);
return HttpStatusCode.OK;
}
}
}
Note: Those code above doesn't solve the manually map all the Location,Name,Extension for the ViewLocationResult thing menthions in my question, but since I endup build a view editor for my colleges to upload their views, I don't need to solve it anymore.
The Data Access Layer is currently a repetition of 3 function: Create, Get, Set.
On a few Dlo type : Foo, Bar , FooBar.
Where Foo and FooBar have the same implementation and Bar has a more complexe one.
public static bool CreateFooBar(FooBarDlo newFooBar)
{
bool result = false;
using (var db = new FooModelDBcontext())
{
db.FooBars.Add(newFooBar);
result = db.SaveChanges() > 0;
}
return result;
}
public static FooBarDlo GetCustomer(int idFooBar)
{
FooBarDlo result;
using (var db = new FooModelDBcontext())
{
result = db.FooBars.FirstOrDefault(x => x.Id == idFooBar);
}
return result;
}
public static bool SetCustomer(FooBarDlo newFooBar)
{
bool result = false;
using (var db = new FooModelDBcontext())
{
var temp = db.FooBars.SingleOrDefault(x => x.Id == newFooBar.Id);
db.Entry(temp).CurrentValues.SetValues(newFooBar);
result = db.SaveChanges() > 0;
}
return result;
}
How can those be refactor, while keeping the specificities of the Bar implementation ?
There are several ways to go about it.
You could provide a base class that takes generics as a parameter with all virtual methods (pseudocode)
public abstract class DbLayer<T> {
public virtual T Get(int Id) {
// default implementation here
// but virtual allows overriding
}
public virtual T Create(T obj) {
// default implementation here
// but virtual allows overriding
}
}
public class FooBarDlo: DbLayer {
public override FooBarDlo Get(int Id) {
// override Get handling
}
}
But if I were you, I'd find a pre-built database layer on CodeProject and go with that.
We are using HttpSessionStateBase to store messages in a set up similar to this working example:
public class HttpSessionMessageDisplayFetch : IMessageDisplayFetch
{
protected HttpSessionStateBase _session;
private IList<ICoreMessage> messages
{
get
{
if (_session[EchoCoreConstants.MESSAGE_KEY] == null)
_session[EchoCoreConstants.MESSAGE_KEY] = new List<ICoreMessage>();
return _session[EchoCoreConstants.MESSAGE_KEY] as IList<ICoreMessage>;
}
}
public HttpSessionMessageDisplayFetch()
{
if (HttpContext.Current != null)
_session = new HttpSessionStateWrapper(HttpContext.Current.Session);
}
public void AddMessage(ICoreMessage message)
{
if (message != null)
messages.Add(message);
}
public IEnumerable<IResultPresentation> FlushMessagesAsPresentations(IResultFormatter formatter)
{
var mToReturn = messages.Select(m => m.GetPresentation(formatter)).ToList();
messages.Clear();
return mToReturn;
}
}
When we pass in a QualityExplicitlySetMessage (which inherits from ICoreMessage, see below) it is saved correctly to messages.
This is how the object looks after being inserted into the messages list, at the end of AddMessage(ICoreMessage message) above.
But when we come to access it after changing controllers the inherited member's properties are null, which causes a variety of null reference exceptions.
This is how the object now looks after we call FlushMessagesAsPresentations. I've commented out var mToReturn... as this tries to access one of these null ref properties.
I'd like to ask the following:
Why is the HttpSessionStateBase failing to capture these values taken
by the inherited type?
Is this an issue in saving to the HttpSession or in retrieving?
Is this anything to do with, as I suspect, inheritance?
Or is the fact I'm potentially calling a new controller that dependency injects the HttpSessionMessageDisplayFetch causing an issue?
I'm a first-time poster so please let me know if I'm making any kind of faux pas - Super keen to learn! Any input is very welcome.
Some potentially useful code snippets:
QualityExplicitlySetMessage
public class QualityExplicitlySetMessage : QualityChangeMessage
{
public QualityExplicitlySetMessage(IQPossession before, IQPossession after, IQEffect qEffect)
: base(before, after, qEffect)
{
IsSetToExactly = true;
}
}
QualityChangeMessage - Working example
public abstract class QualityChangeMessage : CoreMessage, IQualityChangeMessage
{
protected PossessionChange Change;
public PossessionChange GetPossessionChange()
{
return Change;
}
protected QualityChangeMessage(IQPossession before, IQPossession after, IQEffect qEffect)
{
Change = new PossessionChange(before, after, qEffect);
StoreQualityInfo(qEffect.AssociatedQuality);
}
public override IResultPresentation GetPresentation(IResultFormatter formatter)
{
return formatter.GetQualityResult(this);
}
#region IQualityChangeMessage implementation
public int LevelBefore
{
get { return Change.Before.Level; }
}
//... And so on with values dependent on the Change property.
}
CoreMessage - Working example
public abstract class CoreMessage : ICoreMessage
{
public string MessageType
{
get { return GetType().ToString(); }
}
public string ImageTooltip
{
get { return _imagetooltip; }
set { _imagetooltip = value; }
}
public string Image
{
get { return _image; }
set { _image = value; }
}
public int? RelevantQualityId { get; set; }
protected void StoreQualityInfo(Quality q)
{
PyramidNumberIncreaseLimit = q.PyramidNumberIncreaseLimit;
RelevantQualityId = q.Id;
RelevantQualityName = q.Name;
ImageTooltip = "<strong>" + q.Name + "</strong><br/>" + q.Description + "<br>" +
q.EnhancementsDescription;
Image = q.Image;
}
public virtual IResultPresentation GetPresentation(IResultFormatter formatter)
{
return formatter.GetResult(this);
}
}
UserController - Working example.
public partial class UserController : Controller
{
private readonly IMessageDisplayFetch _messageDisplayFetch;
public UserController(IMessageDisplayFetch messageDisplayFetch)
{
_messageDisplayFetch = messageDisplayFetch;
}
public virtual ActionResult MessagesForStoryletWindow()
{
var activeChar = _us.CurrentCharacter();
IEnumerable<IResultPresentation> messages;
messages = _messageDisplayFetch.FlushMessagesAsPresentations(_storyFormatter);
var vd = new MessagesViewData(messages)
{
Character = new CharacterViewData(activeChar),
};
return View(Views.Messages, vd);
}
}
I read all the similar titles questions, but i didn't find the answer to my problem, so I open a new question:
I have two mysql tables:
tb1 (int_id, code, description, ..., my_id);
tb2 (int_id, code, description, ..., tb1_id);
I create a generic repository to manage DbContext and GetAll, GetById, GetByLambda, Insert, Update and Delete methode.
namespace Model.DataAccessLayer
{
public class Repository<T> : IRepository<T> where T : EntityObject, IEntity
{
protected DbContext dbContext = null;
public virtual DbContext DbContext
{
get { return dbContext; }
set { dbContext = value; }
}
public ObjectContext ObjectContext
{
get { return ((IObjectContextAdapter)DbContext).ObjectContext; }
}
public void Dispose()
{
ObjectContext.Dispose();
System.GC.SuppressFinalize(this);
}
public virtual IQueryable<T> GetAll()
{
return ObjectContext.CreateObjectSet<T>();
}
public virtual IEnumerable<T> GetByLambda(Func<T, bool> p)
{
return GetAll().Where(p);
}
public virtual void Save()
{
DbContext.SaveChanges();
}
...
}
}
and the inherited class which I used:
namespace Model.DataAccessLayer
{
public class RepositoryP<T> : Repository<T> where T : EntityObject, IEntity
{
public myEntities MyContext
{
get { return (myEntities)ObjectContext; }
//set { ObjectContext = value; }
}
public override DbContext DbContext
{
get
{
if (dbContext == null)
{
dbContext = new DbContext("myEntities");
}
return dbContext;
}
set
{
base.DbContext = value;
}
}
}
}
It works great when using only one table.
When I try to use my two tables and the foreign key relation between them, it doesn't work.
For example I try to get all records from table tb2 where tb1.my_id=5 with the following join tb1.int_id = tb2.tb1_id.
List<tb2> lj = new Tb2DAO().GetByLambda(l => l.tb1.my_id == 5).ToList();
(Tb2DAO inherited from my generic repository class.)
I have the following MySQL error:
"MySql.Data.MySqlClient.MySqlException: There is already an open DataReader associated with this Connection which must be closed first."
I think this comes from my DbContext which is not common to my two tables entities.
So I tried to implement the UnitOfWork Pattern to solve this problem, like this:
namespace Model.DataAccessLayer
{
public class UnitOfWork : IDisposable
{
private DbContext dbContextUnit = null; //= new DbContext();
private Tb1DAO tb1DAO;
private Tb2DAO tb2DAO;
public Tb1DAO tb1
{
get
{
if (this.tb1DAO == null)
{
if (dbContextUnit != null)
{
this.tb1DAO = new Tb1DAO { DbContext = dbContextUnit };
}
else
{
this.tb1DAO = new Tb1DAO();
dbContextUnit = this.tb1DAO.DbContext;
}
}
return tb1DAO;
}
}
public Tb2DAO tb2
{
get
{
if (this.tb2DAO == null)
{
if (dbContextUnit != null)
{
this.tb2DAO = new Tb2DAO { DbContext = dbContextUnit };
}
else
{
this.tb2DAO = new Tb2DAO();
dbContextUnit = this.tb2DAO.DbContext;
}
}
return tb2DAO;
}
}
public void Save()
{
dbContextUnit.SaveChanges();
}
private bool disposed = false;
protected virtual void Dispose(bool disposing)
{
if (!this.disposed)
{
if (disposing)
{
dbContextUnit.Dispose();
}
}
this.disposed = true;
}
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
}
}
And now in my code I tried to use the Unit of Work like this :
UnitOfWork unitOfWork = new UnitOfWork();
List<tb2> tb2List = unitOfWork.tb2.GetByLambda(l => l.index_job.job_id == job_id).ToList();
But I have always the same error message :
"MySql.Data.MySqlClient.MySqlException: There is already an open DataReader associated with this Connection which must be closed first."
is there something I am doing wrong ? Please can you help me ? This notion of repository and unit Of work are new for me
and I am confused else I read lot of think about it may be not the right one...
I also to try to add MultipleActiveResultSets=true to my connection but it is not recognized.
many thank to all & regards,
wst
Your GetByLambda() method is calling GetAll() which creates a new context using ObjectContext.CreateObjectSet<T>(). So you now have more than one context open. I would advise using the standard EF associations and the repository pattern, then you can avoid this entire mess. Here is a link that may help you get started - http://blogs.msdn.com/b/adonet/archive/2011/03/15/ef-4-1-code-first-walkthrough.aspx.
I'm not sure about this method
public virtual IQueryable<T> GetAll()
{
return ObjectContext.CreateObjectSet<T>();
}
It's create new object set about which EF context doesn't know.
You can try to pass Func<ObjectContext, ObjectResult<T>> from derived class to base. (It should return ObjectResult which are got from EF context. F.e. context => context.Entities).