How to access entity's properties outside context using Entity Framework? - c#

I'm new to Entity Framework (working mostly with NHibernate with ActiveRecord before) and I'm stuck with something, that I think should be easy...
I have a User Entity, and created partial User class so I can add some methods (like with NHibernate). I added GetByID to make getting user easier:
public static User GetByID(int userID)
{
using (var context = new MyEntities())
{
return context.Users.Where(qq => qq.UserID == userID).Single();
}
}
Now in the same class I want to log moment of logging in, and I try to do:
public static void LogLoginInfo(int userID)
{
using (var context = new MyEntities())
{
var user = User.GetByID(userID);
var log = new LoginLog { Date = DateTime.Now };
user.LoginLogs.Add(log);
context.SaveChanges();
}
}
The problem is I can't access user.LoginLogs because user's context is already disposed... Most likely I'm missing something obvious here, but creating always full queries like:
context.Users.Where(qq => qq.UserID == userID).Single().LoginLogs.Add(log);
doesn't seem like a good option...
I've read about Repository pattern but I think it's too big gun for such task. Please explain me what am I doing wrong. Thanks in advance!
EDIT
To picture what I'd like to do:
//somewhere in business logic
var user = User.GetByID(userID);
var posts = user.GetAllPostsForThisMonth();
foreach(var post in posts)
{
Console.WriteLine(post.Answers.Count);
}
Normally I'm not allowed to do this because I can't get post.Answers without context...

You are closing the object context and then trying to add a log to the user that is detached. You need to attach the user so the objectContext know what has been changed or added.
public static void LogLoginInfo(int userID)
{
using (var context = new MyEntities())
{
var user = context.User.Where(p=>p.UserID == userID); //<= The Context now knows about the User, and can track changes.
var log = new LoginLog { Date = DateTime.Now };
user.LoginLogs.Add(log);
context.SaveChanges();
}
}
Update
You can also attach the object.
public static void LogLoginInfo(int userID)
{
using (var context = new MyEntities())
{
var user = User.GetByID(userID);
var log = new LoginLog { Date = DateTime.Now };
user.LoginLogs.Add(log);
context.User.Attach(user);
context.SaveChanges();
}
}
Update
var getFirstLogin = from p in User.GetUserById(userId)
select p.LoginLogs.FirstOrDefault();
NB if LoginLogs is a different table you will need to use Include.
public static User GetByID(int userID)
{
using (var context = new MyEntities())
{
return context.Users.Include("LoginLogs").Where(qq => qq.UserID == userID).FirstOrDefault();
}
}

If you are open to using stored procedures (and they work nicely with EF), you can return the user object and simultaneously add to the log table with a single call to the database.
I used to do everything with SP's in my pre-EF/ORM days, when I went to EF I tried very hard to avoid using stored procedures to avoid falling back into my old habits, but now I have found that the selective use of stored procedures you can have the benefits of both -the EF way of doing things, and the super functionality/performance that a well written SP can provide.

Related

Convert System.Data.Entity.DynamicProxies to (non proxy) class in C#

I am getting some data from the database and storing this in a global variable as shown:
//Global Variable
public static List<stuff> Stuff;
using (var context = new StuffContext())
{
stuff = new List<stuff>();
stuff = (from r in context.Stuff
select r).ToList();
}
The problem I am having is that the context closes and when I wish to access some of the data stored in the global variable, I cannot.
The data is of System.Data.Entity.DynamicProxies.Stuff instead of Application.Model.Stuff which means I then receive this error when I try to do something with the data:
"The ObjectContext instance has been disposed and can no longer be used for operations that require a connection."
My question is how can I, using the above code as an example, convert / cast to the type that I want so that I can use the data else where in my application?
Edit: Quick screen grab of the error:
The Solution was due to lazy loading after all.
I had to tell the query to grab everything so that when the context closes I still had access to the data.
This is the change I had to make:
public static List<stuff> Stuff;
using (var context = new StuffContext())
{
stuff = new List<stuff>();
stuff = (from r in context.Stuff
.Include(s => s.MoreStuff).Include(s => s.EvenMoreStuff)
select r).ToList();
}
Try to Disable ProxyCreationEnabled In Your Project BbContext constructor As Follow:
Configuration.ProxyCreationEnabled = false;

Entity Framework 5.0 db.savechange not working

When I try to update my entry in update function it's execute successfully but database not updated.
Please find the following code
public static string UpdateEmployee(Employee employee)
{
using (var db = new RandDEntities())
{
var empObj = db.Employees.First(x => x.EmpID == employee.EmpID);
db.Entry(empObj).State = EntityState.Modified;
db.SaveChanges();
}
return "";
}
The problem is that you're saving the wrong entity (i.e. empObj) instead of the entity that has the changes (i.e. employee). Your code pulls empObj out of the database, and then turns around and saves it, without making any changes to it. You need to modify your code as follows:
public static string UpdateEmployee(Employee employee)
{
using (var db = new RandDEntities())
{
db.Employees.Attach(employee);
db.Entry(employee).State = EntityState.Modified;
db.SaveChanges();
}
return "";
}
That has absolutely nothing to do with Entity Framework but more with the fact that you likely use localdb and your database is not where you think it is, and reinitialized (reset) to empty every time you press start.
This question, in dozens of variants, gets asked over and overy by people not bothering to check where they look (and then realizing the database files have different paths).

Update detached objects in EF that already exist in object state manager

I'm hoping this will be an easy one but for the life of me, I cannot find the specific answer elsewhere on SO or any other site.
I have a basic repository/unit of work pattern with Entity Framework Code First. It all works smoothly except for certain cases of Update. THe problem is I have a set of Entity Framework model objects, all prefixed with "Db" which EF returns, but I then convert them to plain DataContract Model objects to pass to the Web layer to give separation of concerns. I have a basic conversion interface that just populates a WebModel object from the DataModel object, copying field by field verbatim.
So if you retrieve a DbUser object from EF with ID of 1, then convert to a User object, then convert that BACK to a DbUser object, you end up with a DbUser with ID of 1, but it is a DIFFERENT object to the one you started with, though they have the same primary key field, the actual CLR objects themselves are different.
The following works
User user;
using (var work = new UnitOfWork())
{
var repository = new UserDataRepository(work);
user = repository.Get(1);
repository.save();
}
var modelUser = DataConverter.Convert(user);
modelUser.Name = "new name";
user = BusinessConverter.Convert(modelUser);
using (var work = new UnitOfWork())
{
var repository = new UserDataRepository(work);
repository.Update(user);
repository.save();
}
As they are using two different unit of works/contexts, so the second block has nothing in the ObjectStateManager to compare to and can just attach the detached object in the Update() methods
This, however does NOT work
using (var work = new UnitOfWork())
{
var repository = new UserDataRepository(work);
user = repository.Get(1);
repository.save();
var modelUser = DataConverter.Convert(user);
modelUser.Name = "new name";
user = BusinessConverter.Convert(modelUser)
repository.Update(user);
repository.save();
}
NOTE: I know logically this doesn't make much sense to convert and just convert back but go with it, I've simplified the example greatly to make it easier to put into paper, in my actual code there is a reason for doing it that way.
I get the usual error "an object with the same key already exists in the objectstatemanager...". I'm assuming because the Get() loads the object into EF and then the update sees that the object is detached, then tries to attach it and it already exists.
My Update method in my repository is as below
public override bool UpdateItem(DbUser item)
{
if (Work.Context.Entry(item).State == EntityState.Detached)
Work.Context.Users.Attach(item);
Work.Context.Entry(item).State = EntityState.Modified;
return Work.Context.Entry(item).GetValidationResult().IsValid;
}
I made this Extension method to the DbContext to ReAttach the Entity without problems try it out:
public static void ReAttach<T>(this DbContext context, T entity) where T : class
{
var objContext = ((IObjectContextAdapter) context).ObjectContext;
var objSet = objContext.CreateObjectSet<T>();
var entityKey = objContext.CreateEntityKey(objSet.EntitySet.Name, entity);
Object foundEntity;
var exists = objContext.TryGetObjectByKey(entityKey, out foundEntity);
// Detach it here to prevent side-effects
if (exists)
{
objContext.Detach(foundEntity);
}
context.Set<T>().Attach(entity);
}
Then just update your method :
public override bool UpdateItem(DbUser item)
{
Work.Context.ReAttach(item);
Work.Context.Entry(item).State = EntityState.Modified;
return Work.Context.Entry(item).GetValidationResult().IsValid;
}
You might get a manged Entity, and again verbatim map the new DbUser's properties to the managed Object:
public override bool UpdateItem(DbUser item)
{
using (var work = new UnitOfWork())
{
var repository = new UserDataRepository(work);
DbUser managedUser = repository.Get(item.PK);
//foreach DbUser property map the item to managedUser
managedUser.field1 = item.field1;
[..]
repository.Update(managedUser);
repository.Save();
}
}
If you set your context to AsNoTracking() this will stop aspmvc tracking the changes to the entity in memory (which is what you want anyway on the web).
_dbContext.Products.AsNoTracking().Find(id);
I would recommend you read more about this at http://www.asp.net/mvc/tutorials/getting-started-with-ef-using-mvc/advanced-entity-framework-scenarios-for-an-mvc-web-application
Yash

Multiple instances of IEntityChangeTracker Error

I've read through at least a dozen other questions just like this one, but I am having trouble grasping some of this stuff.
I'm used to developing ASP.NET MVC3 with repositories and code-first entities linking to the entity framework.
I've recently switched to database-first ADO.NET with services development.
I find this to be very clean since I can access stuff through my foreign keys.
Anyway, my old save methods seem to be broken since I constantly get this error
An entity object cannot be referenced by multiple instances of
IEntityChangeTracker
So here's a look at my save action and my service:
Action:
[HttpPost]
public ActionResult AddReview(Review review, int id)
{
User loggedInUser = userService.GetUserByusername(User.Identity.Name);
review.WriterId = loggedInUser.UserId;
review.ProductId = id;
if (ModelState.IsValid)
{
reviewService.Save(review);
Product product = productService.GetProduct(id);
if(product.Quantity>=1)
product.Quantity--;
product.TimesBought++;
productService.UpdateRating(product, reviewService);
loggedInUser.GoldCoins -= product.Price;
Session["goldCoins"] = loggedInUser.GoldCoins;
userService.Save(loggedInUser);
productService.Save(product);
}
else
{
return View(review);
}
return RedirectToAction("Index", "Answers", new { reviewId = review.ReviewId });
Service:
public class ReviewService : Service<Review, CapstoneEntities>
{
...
public void Save(Review review)
{
using (var db = new CapstoneEntities())
{
if (review.ReviewId == 0)
{
db.Reviews.Add(review);
db.Entry(review).State = EntityState.Added;
}
else
{
db.Entry(review).State = EntityState.Modified;
}
db.SaveChanges();
}
}
}
My suspicion is with this line of code: using (var db = new CapstoneEntities()) but I'm not sure how else to do this. Again, this worked perfectly with my old way of doing things but now I get errors on just about ever CRUD operation.
Thank you.
It looks like this is being caused by having an entity belong to multiple DataContexts. Whatever code that is calling that action should use the same DataContext to create the entity as the one used to persist it to the datastore.
In most instances you should only keep one instance of the DataContext. You can use a DI framework like Castle to define/store a dependency (in this case the DataContext) as Transient or PerWebRequest and inject it into the service and controller, so you'll always have a reference to the same instance of the DataContext.
I am new to MVC & Entity frame work. I got same problem after fighting a lot
This solution worked for me. Hope it can be useful for you guys.
var mediaItem = db.MediaItems.FirstOrDefault(x => x.Id == mediaItemViewModel.Id);
mediaItem.Name = mediaItemViewModel.Name;
mediaItem.Description = mediaItemViewModel.Description;
mediaItem.ModifiedDate = DateTime.Now;
mediaItem.FileName = mediaItem.FileName;
mediaItem.Size = KBToMBConversion(mediaItemViewModel.Size);
mediaItem.Type = mediaItem.Type;
//db.Entry(mediaItem).State = EntityState.Modified;// coment This line
db.SaveChanges();
Cause you are reading the the whole object from db and holding it in the current context and when you try to modify the entity state its tells you already one entity attached to the current context. just call save changes it will save it.

How to relate objects from multiple contexts using the Entity Framework

I am very new to the entity framework, so please bear with me...
How can I relate two objects from different contexts together?
The example below throws the following exception:
System.InvalidOperationException: The
relationship between the two objects
cannot be defined because they are
attached to different ObjectContext
objects.
void MyFunction()
{
using (TCPSEntities model = new TCPSEntities())
{
EmployeeRoles er = model.EmployeeRoles.First(p=>p.EmployeeId == 123);
er.Roles = GetDefaultRole();
model.SaveChanges();
}
}
private static Roles GetDefaultRole()
{
Roles r = null;
using (TCPSEntities model = new TCPSEntities())
{
r = model.Roles.First(p => p.RoleId == 1);
}
return r;
}
Using one context is not an option because we are using the EF in an ASP.NET application.
You will have to use the same context (you can pass the context to the getdefaultrole method) or rethink the relationships and extend the entity.
EDIT: Wanted to add this was for the example provided, using asp.net will require you to fully think out your context and relationship designs.
You could simply pass the context.. IE:
void MyFunction()
{
using (TCPSEntities model = new TCPSEntities())
{
EmployeeRoles er = model.EmployeeRoles.First(p=>p.EmployeeId == 123);
er.Roles = GetDefaultRole(model);
model.SaveChanges();
}
}
private static Roles GetDefaultRole(TCPSEntities model)
{
Roles r = null;
r = model.Roles.First(p => p.RoleId == 1);
return r;
}
Another approach that you could use here is to detach objects from one context, and then attach them to another context. That's a bit of a hack, and it may not work in your situation, but it might be an option.
public void GuestUserTest()
{
SlideLincEntities ctx1 = new SlideLincEntities();
GuestUser user = GuestUser.CreateGuestUser();
user.UserName = "Something";
ctx1.AddToUser(user);
ctx1.SaveChanges();
SlideLincEntities ctx2 = new SlideLincEntities();
ctx1.Detach(user);
user.UserName = "Something Else";
ctx2.Attach(user);
ctx2.SaveChanges();
}
Yep - working across 2 or more contexts is not supported in V1 of Entity Framework.
Just in case you haven't already found it, there is a good faq on EF at http://blogs.msdn.com/dsimmons/pages/entity-framework-faq.aspx
From what I understand, you want to instantiate your model (via the "new XXXXEntities()" bit) as rarely as possible. According to MS (http://msdn.microsoft.com/en-us/library/cc853327.aspx), that's a pretty substantial performance hit. So wrapping it in a using() structure isn't a good idea. What I've done in my projects is to access it through a static method that always provides the same instance of the context:
private static PledgeManagerEntities pledgesEntities;
public static PledgeManagerEntities PledgeManagerEntities
{
get
{
if (pledgesEntities == null)
{
pledgesEntities = new PledgeManagerEntities();
}
return pledgesEntities;
}
set { pledgesEntities = value; }
}
And then I retrieve it like so:
private PledgeManagerEntities entities = Data.PledgeManagerEntities;

Categories

Resources