Xamarin forms - Realm accessed from incorrect thread - xamarin.forms

Maybe I'm missing something really simple out here but gonna ask anyways.....
I am using Xamarin forms (.NET Standard project), MVVMLight, Realm DB and ZXing Barcode Scanner.
I have a realmobject like so...
public class Participant : RealmObject
{
public string FirstName {get; set;}
public string LastName {get; set;}
public string Email {get; set;}
public string RegistrationCode {get; set;}
//More properties skipped out for brevity
}
I have the corresponding viewmodel as follows:
public class ParticipantViewModel
{
Realm RealmInstance
public ParticipantViewModel()
{
RealmInstance = Realms.Realm.GetInstance();
RefreshParticipants();
}
private async Task RefreshParticipants()
{
//I have code here that GETS the list of Participants from an API and saves to the device.
//I am using the above-defined RealmInstance to save to IQueryable<Participant> Participants
}
}
All the above works fine and I have no issues with this. In the same viewmodel, I am also able to fire up the ZXing Scanner and scan a bar code representing a RegistrationCode.
This, in turn, populates the below property (also in the viewmodel) once scanned...
private ZXing.Result result;
public ZXing.Result Result
{
get { return result; }
set { Set(() => Result, ref result, value); }
}
and calls the below method (wired up via the ScanResultCommand) to fetch the participant bearing the scanned RegistrationCode.
private async Task ScanResults()
{
if (Result != null && !String.IsNullOrWhiteSpace(Result.Text))
{
string regCode = Result.Text;
await CloseScanner();
SelectedParticipant = Participants.FirstOrDefault(p => p.RegistrationCode.Equals(regCode, StringComparison.OrdinalIgnoreCase));
if (SelectedParticipant != null)
{
//Show details for the scanned Participant with regCode
}
else
{
//Display not found message
}
}
}
I keep getting the below error....
System.Exception: Realm accessed from incorrect thread.
generated by the line below....
SelectedParticipant = Participants.FirstOrDefault(p => p.RegistrationCode.Equals(regCode, StringComparison.OrdinalIgnoreCase));
I'm not sure how this is an incorrect thread but any ideas on how I can get around to fetching the scanned participant either from the already populated IQueryable or from the Realm representation directly would be greatly appreciated.
Thanks

Yes, you're getting a realm instance in the constructor, and then using it from an async task (or thread). You can only access a realm from the thread in which you obtained the reference. Since you're only using a default instance, you should be able to simply obtain a local reference within the function (or thread) where you use it. Try using
Realm LocalInstance = Realms.Realm.GetInstance();
at the top of the function and use that. You'll need to recreate the Participants query to use the same instance as its source too. This will be the case wherever you use async tasks (threads), so either change all to get hold of the default instance on entry or reduce the number of threads that access the realm.
Incidentally I'm surprised you don't get a similar access error from within
RefreshParticipants() - maybe you're not actually accessing data via RealmInstance from there.

Related

Using Unity Dependency Injection in Multi-User Web Application: Second User to Log In Causes First User To See Second User's Data

I'm trying to implement a web application using ASP.NET MVC and the Microsoft Unity DI framework. The application needs to support multiple user sessions at the same time, each of them with their own connection to a separate database (but all users using the same DbContext; the database schemas are identical, it's just the data that is different).
Upon a user's log-in, I register the necessary type mappings to the application's Unity container, using a session-based lifetime manager that I found in another question here.
My container is initialized like this:
// Global.asax.cs
public static UnityContainer CurrentUnityContainer { get; set; }
protected void Application_Start()
{
// ...other code...
CurrentUnityContainer = UnityConfig.Initialize();
// misc services - nothing data access related, apart from the fact that they all depend on IRepository<ClientContext>
UnityConfig.RegisterComponents(CurrentUnityContainer);
}
// UnityConfig.cs
public static UnityContainer Initialize()
{
UnityContainer container = new UnityContainer();
DependencyResolver.SetResolver(new UnityDependencyResolver(container));
GlobalConfiguration.Configuration.DependencyResolver = new Unity.WebApi.UnityDependencyResolver(container);
return container;
}
This is the code that's called upon logging in:
// UserController.cs
UnityConfig.RegisterUserDataAccess(MvcApplication.CurrentUnityContainer, UserData.Get(model.AzureUID).CurrentDatabase);
// UnityConfig.cs
public static void RegisterUserDataAccess(IUnityContainer container, string databaseName)
{
container.AddExtension(new DataAccessDependencies(databaseName));
}
// DataAccessDependencies.cs
public class DataAccessDependencies : UnityContainerExtension
{
private readonly string _databaseName;
public DataAccessDependencies(string databaseName)
{
_databaseName = databaseName;
}
protected override void Initialize()
{
IConfigurationBuilder configurationBuilder = Container.Resolve<IConfigurationBuilder>();
Container.RegisterType<ClientContext>(new SessionLifetimeManager(), new InjectionConstructor(configurationBuilder.GetConnectionString(_databaseName)));
Container.RegisterType<IRepository<ClientContext>, RepositoryService<ClientContext>>(new SessionLifetimeManager());
}
}
// SessionLifetimeManager.cs
public class SessionLifetimeManager : LifetimeManager
{
private readonly string _key = Guid.NewGuid().ToString();
public override void RemoveValue(ILifetimeContainer container = null)
{
HttpContext.Current.Session.Remove(_key);
}
public override void SetValue(object newValue, ILifetimeContainer container = null)
{
HttpContext.Current.Session[_key] = newValue;
}
public override object GetValue(ILifetimeContainer container = null)
{
return HttpContext.Current.Session[_key];
}
protected override LifetimeManager OnCreateLifetimeManager()
{
return new SessionLifetimeManager();
}
}
This works fine as long as only one user is logged in at a time. The data is fetched properly, the dashboards work as expected, and everything's just peachy keen.
Then, as soon as a second user logs in, disaster strikes.
The last user to have prompted a call to RegisterUserDataAccess seems to always have "priority"; their data is displayed on the dashboard, and nothing else. Whether this is initiated by a log-in, or through a database access selection in my web application that calls the same method to re-route the user's connection to another database they have permission to access, the last one to draw always imposes their data on all other users of the web application. If I understand correctly, this is a problem the SessionLifetimeManager was supposed to solve - unfortunately, I really can't seem to get it to work.
I sincerely doubt that a simple and common use-case like this - multiple users logged into an MVC application who each are supposed to access their own, separate data - is beyond the abilities of Unity, so obviously, I must be doing something very wrong here. Having spent most of my day searching through depths of the internet I wasn't even sure truly existed, I must, unfortunately, now realize that I am at a total and utter loss here.
Has anyone dealt with this issue before? Has anyone dealt with this use-case before, and if yes, can anyone tell me how to change my approach to make this a little less headache-inducing? I am utterly desperate at this point and am considering rewriting my entire data access methodology just to make it work - not the healthiest mindset for clean and maintainable code.
Many thanks.
the issue seems to originate from your registration call, when registering the same type multiple times with unity, the last registration call wins, in this case, that will be data access object for whoever user logs-in last. Unity will take that as the default registration, and will create instances that have the connection to that user's database.
The SessionLifetimeManager is there to make sure you get only one instance of the objects you resolve under one session.
One option to solve this is to use named registration syntax to register the data-access types under a key that maps to the logged-in user (could be the database name), and on the resolve side, retrieve this user key, and use it resolve the corresponding data access implementation for the user
Thank you, Mohammed. Your answer has put me on the right track - I ended up finally solving this using a RepositoryFactory which is instantiated in an InjectionFactory during registration and returns a repository that always wraps around a ClientContext pointing to the currently logged on user's currently selected database.
// DataAccessDependencies.cs
protected override void Initialize()
{
IConfigurationBuilder configurationBuilder = Container.Resolve<IConfigurationBuilder>();
Container.RegisterType<IRepository<ClientContext>>(new InjectionFactory(c => {
ClientRepositoryFactory repositoryFactory = new ClientRepositoryFactory(configurationBuilder);
return repositoryFactory.GetRepository();
}));
}
// ClientRepositoryFactory.cs
public class ClientRepositoryFactory : IRepositoryFactory<RepositoryService<ClientContext>>
{
private readonly IConfigurationBuilder _configurationBuilder;
public ClientRepositoryFactory(IConfigurationBuilder configurationBuilder)
{
_configurationBuilder = configurationBuilder;
}
public RepositoryService<ClientContext> GetRepository()
{
var connectionString = _configurationBuilder.GetConnectionString(UserData.Current.CurrentPermission);
ClientContext ctx = new ClientContext(connectionString);
RepositoryService<ClientContext> repository = new RepositoryService<ClientContext>(ctx);
return repository;
}
}
// UserData.cs (multiton-singleton-hybrid)
public static UserData Current
{
get
{
var currentAADUID = (string)(HttpContext.Current.Session["currentAADUID"]);
return Get(currentAADUID);
}
}
public static UserData Get(string AADUID)
{
UserData instance;
lock(_instances)
{
if(!_instances.TryGetValue(AADUID, out instance))
{
throw new UserDataNotInitializedException();
}
}
return instance;
}
public static UserData Current
{
get
{
var currentAADUID = (string)(HttpContext.Current.Session["currentAADUID"]);
return Get(currentAADUID);
}
}
public static UserData Get(string AADUID)
{
UserData instance;
lock(_instances)
{
if(!_instances.TryGetValue(AADUID, out instance))
{
throw new UserDataNotInitializedException();
}
}
return instance;
}

Update database items from the website

My current problem is (probably) not necessarily directly related to MVC 6, but how working with database actually works, and therefore any help/suggestions in this matter would be more than appreciated.
For the sake of this question, let's say that we have a very simple database with the following tables (C# classes) [we are using Entity Framework to work with the database]:
public class ShoppingUser
{
public int Id { get; set; }
public string UserName { get; set; }
public ICollection<ShoppingItem> Items { get; set; }
}
public class ShoppingItem
{
public int Id { get; set; }
public string Quantity { get; set; }
public string Text { get; set; }
public bool ToRemove { get; set; }//if item has been bought, it can be removed from the shopping list
}
This demo will be for a super duper simple shopping list app, where user (ShoppingUser who is registered in the system can have a List of ShoppingItem where user can decide on what is the text of the item (e.g. Bread, Butter, Tomatoes, ...) and also a quantity (3 pieces, 5kg, ... simple string)
Afterwards in my ASP.NET Core app, I have defined a repository which is communicating with the database and has access to the ShoppingItem class (as we are only interested in shopping items of currently logged in user).
Example of some method we could use here:
public IEnumerable<ShoppingItem> ReturnUserItems(string sUsername)
{
if (string.IsNullOrWhiteSpace(sUsername))
return null;
var result = _context.ShoppingUsers.Include(n => n.Items).Where(n => n.UserName == sUsername).FirstOrDefault();
if (result != null)
return result.Items;
else
return null;
}
Finally we have an API controller with JsonResult for either GET, POST, DELETE, ..., which is used for communication between client side AngularJs App and our server side logic.
Example of GET Method:
// GET: /<controller>/
[HttpGet("")]
public JsonResult Get(string sUserName)
{
try
{
var results = _repository.ReturnUserItems(User.Identity.Name);
if (results != null)
{
var result = Mapper.Map<IEnumerable<ShoppingItemViewModel>>(results);
return Json(result);
}
Response.StatusCode = (int)HttpStatusCode.OK;
}
catch (Exception ex)
{
Response.StatusCode = (int)HttpStatusCode.BadRequest;
return Json(new { Message = ex.Message });
}
return null;
}
Here comes the tricky part (at least for me). From video tutorials I have learned, that I should never (or almost never) expose my real database model to the website (I guess it's for security reasons). Due to that (as visible from my GET method above) I have declared my ShoppingItemViewModel which contains only properties I want to expose to the user (e.g. meaning that Id of my item is not visible).
This is how it looks like:
public class ShoppingItemViewModel
{
public string Quantity { get; set; }
[Required]
public string Text { get; set; }
[Required]
public bool ToRemove { get; set; }//if item has been bought, it can be removed from the shopping list
}
And for the communication from my AngularJS App I am using simple $http.get and $http.post calls for retrieving / posting updated data.
Finally the question:
My problem is, that if a user decides to either delete an item from his shopping list, or decides to change the content of either text / quantity (meaning that originally in the database it was tomatoes - 5 kg but he manages to buy only 2 kg and therefore changes the quantity to tomatoes - 3kg), how can the app understand which elements have actually been changed and how? The problem I have in this case is, that we are no longer exposing the database Id of the items.
If I was writing a desktop app, where I wouldn't have to create this sub view (ShoppingItemViewModel), my EntityFramework is intelligent enough to check & update all the changes in my database. Unfortunately in this case, I do not understand how this is achievable.
When I was thinking about it I came with the following: Add a new property into the ShoppingItem and ShoppingItemViewModel: public string sCustomKey {get; set; }, which would serve as a unique key for every item. This way, we no longer need to expose our database Id, but we are exposing the 'fake' one.
Second question:
I case my solution would be accurate, what is the best way to update items in the database? The only way I can think of is iterating through all the items in the database and manually check for changes?
Example of what I have in mind:
//IEnumerable<ShoppingItem> would be re-mapped result of ShoppingItemViewModel we have received back from the website
public void UpdateValues(IEnumerable<ShoppingItem> items, string sUserName)
{
//retrieves list of shopping items for specified customer
var allItems = _context.ShoppingUsers
.Include(n => n.Items)
.FirstOrDefault(n => n.UserName == sUserName);
//updates the values
foreach (var sItem in items)
{
var updatedItem = allItems.Items.FirstOrDefault(n => n.Text == sItem.sCustomKey);
if (updatedItem == null)
{
//create new item
var newItem = new ShoppingItem();
newItem.Text = sItem.Text;
newItem.ToRemove = sItem.ToRemove;
allItems.Items.Add(newItem);
}
else
updatedItem.ToRemove = sItem.ToRemove;
}
_context.SaveChanges();
}
But this approach does not seem right to me.
Any help regarding these matters would be more than appreciated as I am still learning how to work with ASP.NET Core and web projects.
In your first question, exposing the item ID in the ViewModels is fine. In your domain layer, you can add validation logic that those ID exists/valid item.
Alternatively, you can use a Guid for your item/product because the ID (int) can easily be predicted.
As far as updating the items, you should not use the "username" as Identifier (of the cart) because that can be predicted/altered by the calling client. You can use Guid either persisted(to Db) or
in-memory. You can add validation as well if this Guid belongs to this username/emailAddress. So updating the items in the cart, consider adding/removing one at a time if that is doable
instead of sending list of items.
I think you have misunderstood something.
Here comes the tricky part (at least for me). From video tutorials I have learned, that I should never (or almost never) expose my real database model to the website (I guess it's for security reasons). Due to that (as visible from my GET method above) I have declared my ShoppingItemViewModel which contains only properties I want to expose to the user (e.g. meaning that Id of my item is not visible).
ViewModel <=> Domain Model <=> ReadModel (Database Model)
The point is that you shouldn't use your ReadModel(Database model) as your ViewModel in Presentation Layer (MVC). All three models will have identity.

How to handle exceptions in a multi-layered ASP.NET Web Application

I have an ASP.NET Web Forms application with UI, Service layer and Repository layer.
Some of the methods in my Service Layer communicates with a Web Service, therefore I would like to wrap all the calls to the Web Methods in a Try-Catch-Finally construct.
Suppose I have the following methods in my Service Layer:
public RegistrationDetails GetRegistrationDetails(int userId)
public bool RegisterUser(UserData myUserData)
Where RegistrationDetails and myUserData are object types (classes).
My concern is the following: if I create a Try-Catch-Finally to wrap the call to the Web Service within the implementation of the methods listed above, in case there is an exception how can I return the message string if the return types are RegistrationDetails and bool?
I was thinking about adding a property to every return object but I do not know if that is a good solution. For instance instead of using bool:
public class RegisterResponse
{
public bool isRegistered { get; set; }
public string ExceptionMessage { get; set; }
}
public RegisterResponse RegisterUser(UserData myUserData)
And then check if ExceptionMessage is null or String.Empty. Is it a good approach? Thanks
1) As mentioned by IrishChieftain, bubbling the exception down to the forms is good, you will be able to respond to the exception better
2) You can also have a reference parameter as array which stores exception messages generated from the method
public bool RegisterUser(UserData myUserData, optional ref ArrayList<string> errors)
{
if(error)
errors.Add("This error occured")
}
3) For Instance Object, you could have an Instance variable of ArrayList for errors and have that returned in a property
public class MyClass
{
private ArrayList<string> errors = new ArrayList<string>
public ArrayList<string> ExceptionMessages()
{
get
{
return errors;
}
}
public RegistrationDetails GetRegistrationDetails(int userId) { }
}
//Used like this:
MyClass c = new MyClass();
c.GetRegistrationDetails();
if(c.ExceptionMessages > 0)
{
//output errors
}
But I would prefer the first approach - for flexibility like output formatting
Passing raw exceptions on to your client (web forms layer) from a service could be risky. If it's a database exception, it might expose details of your database. A malicious user might call your service layer methods from their own application.
You can expect two types of exceptions on the client level:
Communication Exception (problems connecting to the service)
Server-side error (database problem, username not unique, password invalid... other business rule exceptions)
The first type should be caught in try-catch-finally in your web forms layer, but the second kind should be caught in the service layer, logged, then wrapped up in the RegisterResponse object, as you suggest. But, instead of sending Exception.Message, you might consider using an enum of expected errors (with a ServerError member to cover anything else.) You could also add an EventId to the response and log entry so that you can investigate errors.
public enum RegisterResponseError { NoError = 0, SystemError = 1,
UserNameNotUnique, PasswordInvalid, etc. }
public class RegisterResponse
{
public bool isRegistered { get; set; }
public RegisterResponseError ErrorCode { get; set; }
}
Then in your client code,
if(myRegisterResponse.ErrorCode == RegisterResponseError.NoError)
// everything was fine
else
// show a suitable error message for ErrorCode and display EventId (if logging)
You could return an error string from your service, but it's probably better to manage any content in your web forms layer, in case you need to localize the content or use a CMS later on.

Entity to Model and foreign key objects

I have an EF object called SportDivision. For simplicity's sake, I won't include every field, just the ones that are relevant:
[Table("SportDivision", Schema = "dbo")]
public class SportDivision: BaseReferenceEntity
{
public int Id { get; set; }
public string Name { get; set; }
public int SportId { get; set; }
[ForeignKey("SportId")]
public virtual Sport Sport { get; set; }
}
So it has a SportId and it's a foreign key that points to the table Sport.
Now, I can't just use an EF object in my views, so I have a model class that's mapped to SportDivision called SportDivisionModel:
public class SportDivisionModel: BaseReferenceModel
{
public int Id { get; set; }
public string Name { get; set; }
public int SportId { get; set; }
//Read only fields
public string Sport { get; set; }
}
I use automapper to transfer data from SportDivision to SportDivisionModel and vice versa. The mapping looks like this:
Mapper.CreateMap<SportDivision, SportDivisionModel>()
.ForMember(x => x.Sport, c => c.MapFrom(e => e.Sport.Name));
Mapper.CreateMap<SportDivisionModel, SportDivision>();
And I have a genericized service that CRUDs and translates data from entity to model or model to entity. Everything works fine except on Create, of which the function is shown below:
public TModel Create<TModel, TEntity>(TModel entry)
where TModel : BaseReferenceModel
where TEntity : BaseReferenceEntity
{
var dm = ServiceLocator.Current.GetInstance<ICrudService<TEntity>>();
var raw = Mapper.Map<TModel, TEntity>(entry);
var created = dm.CreateOrUpdate(raw);
return Mapper.Map<TEntity, TModel>(dm.FindById(created.Id));
}
In the very last line, where you see dm.FindById(created.Id), it returns a SportDivisionModel object with no Sport name. A null reference exception is found in .ForMember(x => x.Sport, c => c.MapFrom(e => e.Sport.Name));. It didn't load Sport after the entry was just created in the database.
I've debugged the code, and I see that the entry with a valid SportId is entered into the SportDivision table of my database, but when I try and bring it over to my MVC application, it doesn't get all the information.
This only is an issue on create. If I simply get data from the database without creating it beforehand, or if I edit the information, then the Sport field in my model object does get populated. I don't know why this is happening, and I can't use the .Include in my generic service call (because not all BaseReferenceEntity classes have a foreign key pointing to Sport).
Please advise. Thanks in advance.
I must play Sherlock Holmes and try to derive what could be the content of CreateOrUpdate and FindById from the indications in your question:
You say that you don't use Include because of the generic service. I assume that you also don't use explicit loading (Load) because you would face the same problem that you cannot really make it generic.
Conclusion: Because the Sport navigation property in the SportDivision gets loaded in certain scenarios (Edit) this can only happen due to lazy loading. The conclusion is backed by the fact that the Sport property is marked as virtual.
Lazy loading relies on proxies. If your SportDivision entity is a proxy then
either loading the Sport entity works
or you get an exception telling you that the context is already disposed (if you have disposed the context)
Number 2 is not the case -> Conclusion: Number 1 must be the case if the pre-condition is fulfilled
But Number 1 also isn't the case (loading Sport does not work)
Conclusion: The pre-condition that your SportDivision entity is a proxy is not true.
So: SportDivision is not a proxy. Could this mean that you have lazy loading in the context disabled? No: Because you are saying that editing works it means that when you load entities from the database they are loaded as proxies and support lazy loading.
Editing works, lazy loading isn't disabled but creating a new entity does not work in the way that the Sport entity is loaded when you proceed to use the newly created entity.
Conclusion: Your newly created entity (returned from CreateOrUpdate) is not a proxy and CreateOrUpdate looks similar to this:
public TEntity CreateOrUpdate(TEntity raw) where TEntity : class
{
if (blabla)
; //update
else
{
context.Set<TEntity>().Add(raw);
context.SaveChanges();
return raw;
}
}
and FindById is just:
public TEntity FindById(int id)
{
return context.Set<TEntity>().Find(id);
}
Since you are passing raw directly into the Add method of the DbSet<T> the question raises where does raw come from and how is it created.
Obviously AutoMapper creates the entity after this line: var raw = Mapper.Map<TModel, TEntity>(entry);
How does Automapper create an entity? Probably by calling new TEntity or by using some reflection code like Activator.CreateInstance or...
It doesn't really matter how, but for sure AutoMapper doesn't instantiate an Entity Framework proxy which had to be created by:
var entity = context.Set<TEntity>().Create();
If all this is true, I feel totally screwed by AutoMapper and generic excesses. If all this wouldn't be generic we could solve the problem by:
context.Set<SportDivision>().Add(raw);
context.SaveChanges();
context.Entry(raw).Reference(r => r.Sport).Load();
Instead we must try some ugly tricks now:
context.Set<TEntity>().Add(raw);
context.SaveChanges();
context.Entry(raw).State = EntityState.Detached;
// We hope that raw is now really out of the context
raw = context.Set<TEntity>().Find(raw.Id);
// raw must be materialized as a new object -> Hurray! We have a proxy!
return raw;
(I'm really not sure if the Detached trick above does work. Aside from that you are forced to reload an entity from the database you just have created and saved which is stupid somehow.)
Potential trick number 2 (without reloading from DB but for the price of being a further step more ugly):
context.Set<TEntity>().Add(raw);
context.SaveChanges();
context.Entry(raw).State = EntityState.Detached;
// We hope that raw is now really out of the context
var anotherRaw = context.Set<TEntity>().Create(); // Proxy!
anotherRaw.Id = raw.Id;
context.Set<TEntity>().Attach(anotherRaw);
context.Entry(anotherRaw).CurrentValues.SetValues(raw);
context.Entry(anotherRaw).State = EntityState.Unchanged;
return anotherRaw; // Proxy! Lazy loading will work!
Does AutoMapper have a feature of a "custom allocator or instantiator" and can custom user data (a context) be supplied? Then there would be a chance to let AutoMapper call context.Set<TEntity>().Create();. Or is it possible to instantiate the object by hand, pass it to AutoMapper and AutoMapper just updates the object's properties?
BTW: The line...
context.Entry(anotherRaw).CurrentValues.SetValues(raw);
...is kind of EF's built-in "AutoMapper". The parameter of SetValues is a general System.Object (could be your ...Model object) and the method maps property values from the supplied object to properties of attached entities by identical property names. Maybe you can leverage this feature somehow instead of using the mapping from model to entity done by AutoMapper.

Error displaying details (ObjectContext instance has been disposed)

I'm building an ASP.NET MVC 3 app and I've got a model that looks something like so:
public partial class Flavor
{
// ...
public string Name { get; set; }
public bool HasNuts {get; set; }
public virtual ICollection<SaleData> Sales {get; set;}
// ...
}
which retrieves some data from a db as such:
public PartialViewResult Details(int id)
{
using (var db = new IceCreamDBFlavors())
{
Flavor someFlavor = db.Flavors.Find(id);
someFlavor.Sales = db.Sales.Where(c => c.FlavorID == id).ToList();
return PartialView("details", someFlavor);
}
}
over on the view I do something like this:
<fieldset>
<legend>Sales Data</legend>
#foreach (var sale in Model.Sales)
{
<div>Weekly</div>
<div>#sale.Weekly</div>
}
</fieldset>
If I don't retrieve the Sales data, my Flavor data displays fine with no errors, but adding the call to retrieve the list of sales data causes an error "The ObjectContext instance has been disposed and can no longer be used for operations that require a connection." to occur.
I've read a few other posts about this and guess I'm missing something here. I believe this error happens due to lazy loading, at least based on what I've read here and elsewhere. Setting a breakpoint in the Controller before returning the PartialView and checking the object, I believe, causes the evaluation to take place, so everything displays as I would want.
I was under the impression that the ToList() call would force the Sales collection to be filled in. Since I don't have the issue when that line's commented out, I assume the problem is still related to that and when the View is attempting to iterate the Sales, it can't. Am I correct here? I guess I thought I was forcing the evaluation. How do I resolve this?
My suspicion is that Flavor has other collections (and not just Sales) and it is in fact at the time of accessing those that it breaks.
Here you are replacing only Sales while other collections or complex properties would still need the object context.
Don't dispose the IceCreamDBFlavors class that inherits from ObjectContext, it needs to have a lifetime greater than is currently allowed.
Change
using (var db = new IceCreamDBFlavors())
{
Flavor someFlavor = db.Flavors.Find(id);
someFlavor.Sales = db.Sales.Where(c => c.FlavorID == id).ToList();
return PartialView("details", someFlavor);
}
To
try
{
var db = new IceCreamDBFlavors();
Flavor someFlavor = db.Flavors.Find(id);
someFlavor.Sales = db.Sales.Where(c => c.FlavorID == id).ToList();
return PartialView("details", someFlavor);
}
catch(Exception ex)
{
// log exeption
}

Resources