Linq2Sql: Manage DataContext - c#

In the following code doesn't work as
public void Foo()
{
CompanyDataContext db = new CompanyDataContext();
Client client = (select c from db.Clients ....).Single();
Bar(client);
}
public void Bar(Client client)
{
CompanyDataContext db = new CompanyDataContext();
db.Client.Attach(client);
client.SomeValue = "foo";
db.SubmitChanges();
}
This doens't work, I get error msg. "An attempt has been made to Attach or Add an entity that is not new, perhaps having been loaded from another DataContext. This is not supported."
How do you work with DataContexts throughout an application so you don't need to pass around a reference?
What

They really mean it with 'This is not supported.'. Attaching to an object fetched from another data context is not implemented.
There are a number of workarounds to the problem, the recommended way is by serializing objects, however this is not easy nor a clean approach.
The most simple approach I found is to use a readonly DataContext for fetching objects like this:
MyDataContext dataContext = new MyDataContext()
{
DeferredLoadingEnabled = false,
ObjectTrackingEnabled = false
};
The objects obtained from this context can be attached to another context but only applies to some scenarios.

The PLINQO framework generates detach for all entities making it easy to detach and reattach objects without receiving that error.
public void Foo()
{
CompanyDataContext db = new CompanyDataContext();
Client client = (select c from db.Clients ....).Single();
// makes it possible to call detach here
client.Detach();
Bar(client);
}
public void Bar(Client client)
{
CompanyDataContext db = new CompanyDataContext();
db.Client.Attach(client);
client.SomeValue = "foo";
db.SubmitChanges();
}
Here is the article that describing how the detach was implemented.
http://www.codeproject.com/KB/linq/linq-to-sql-detach.aspx

Yep. That's how it works.
You have tagged this asp.net so I guess it's a web app. Maybe you want one datacontext per request?
http://blogs.vertigo.com/personal/keithc/Blog/archive/2007/06/28/linq-to-sql-and-the-quote-request-scoped-datacontext-quote-pattern.aspx
(P.S. It's a lot harder in WinForms!)

I've created data access classes that encapsulate all the communication with Linq2Sql.
These classes have their own datacontext that they use on their objects.
public class ClientDataLogic
{
private DataContext _db = new DataContext();
public Client GetClient(int id)
{
return _db.Clients.SingleOrDefault(c => c.Id == id);
}
public void SaveClient(Client c)
{
if (ChangeSetOnlyIncludesClient(c))
_db.SubmitChanges();
}
}
Ofcourse you will need to keep this object instantiated as long as you need the objects.
Checking if only the rigth object has been changed is altso somewhat bothersom, you could make methods like
void ChangeClientValue(int clientId, int value);
but that can become a lot of code.
Attaching and detaching is a somewhat missing feature from Linq2Sql, if you need to use that a lot, you sould probably use Linq2Entities.

I took a look at this and found that it appears to work fine as long as the original DataContext has been disposed.
Try wrapping the DataContext with using() and make sure your changes occur after you've attached to the second DataContext? It worked for me..
public static void CreateEntity()
{
User user = null;
using (DataClassesDataContext dc = new DataClassesDataContext())
{
user = (from u in dc.Users
select u).FirstOrDefault();
}
UpdateObject(user);
}
public static void UpdateObject(User user)
{
using (DataClassesDataContext dc = new DataClassesDataContext())
{
dc.Users.Attach(user);
user.LastName = "Test B";
dc.SubmitChanges();
}
}

You need to handle object versioning.
An entity can only be attached as modified without original state if it declares a version member or does not have an update check policy.
So, if there's no timestamp member or other 'versioning' mechanism provided there's no way for LINQ to determine whether that data has changed - hence the error you are seeing.
I resolved this issue by adding a timestamp column to my tables but there are other ways around it. Rick Strahl has written some decent articles about exactly this issue.
Also, see this and this for a bit more info.

Related

Entity Framework 6 context not retrieving navigation properties

I have found many other posts but they are nt facing exactly the same problem. And they are using a slightly different code. SO I think it is worth reviewing this.
I´m using EF6 code first, and I created a Client Entity that has some navigation properties.
I´ll post just the relevant code, consider there are a few more properties and foreign keys as well, but not relevant to the problem. Model is generating ok.
public class Client
{
public Client()
{
JobsExperiences = new Collection<JobsExperience>();
CapacitationCourses = new Collection<CapacitationCourse>();
ScholarLevelDetails = new Collection<ScholarLevelDetail>();
Relatives = new Collection<Relative>();
}
public long ClientID { get; set; }
public virtual ICollection<ScholarLevelDetail> ScholarLevelDetails { get; set; }
public virtual ICollection<JobsExperience> JobsExperiences { get; set; }
}
Now I created a ClientServices class where I put all methods that get or send data from and to the data base., ther I have this code, which is working randomly, I´ll try to explain clearly.
internal Client GetClient(string userId, bool lazyLoadingEnabled = true)
{
using (var context = new ApplicationDbContext())
{
context.Configuration.LazyLoadingEnabled=lazyLoadingEnabled;
var client = (from _client in context.Client
where _client.ApplicationUserId == userId
select _client).FirstOrDefault();
return client;
}
}
My objective some cases is to retrieve just the client attributes, and sometimes all attributes including navigation properties.
In my controller I have a line like this
var client = uuc.GetClient(user.Id, false);
or this
var client = uuc.GetClient(user.Id);
When I run the first sentence, the navigation properties are initialized but all has Count=0, even when my DB has records associated. I think, if lazy loading is disabled, it means eager loading is enabled, but it seems not. However, there is no Load() Method in the navigation properties to force load.
When I run the second sentence, the navigation properties throws an exception 'client.ScholarLevelDetails' threw an exception of type 'System.ObjectDisposedException'. This is thrown one line after the sentence, loking at the navigation properties in the watch. However, and this is the weirdest part, if I step back to the sentence and debug stepping into the method, All navigation properties are loaded correctly.
Why the code behaves differently if running at once than running stepping into the method?
I presume the using statement scope finishes before that the navigation properties load, but why disabling lay loading doe snot retrieve them either?
How can I code this to have a consistent behaviour?
I change the query code Ihad in Linq with this one.
internal Client GetClient(string userId, bool lazyLoadingEnabled = true)
{
using (var context = new ApplicationDbContext())
{
context.Configuration.LazyLoadingEnabled = lazyLoadingEnabled;
var client = context
.Client
.Include(s => s.ScholarLevelDetails)
.Include(s => s.JobsExperiences)
.Include(s => s.CapacitationCourses)
.Include(s => s.Relatives)
.FirstOrDefault(s => s.ApplicationUserId == userId);
return client;
}
}
And now it works. however I still have some questions I´d lve to discuss with you readers and colleagues.
Why plain Linq doesn´t work?
Why it doesn matter if lazyloading is enabled or not, this code works the same everytime?
The problem is that your context fell out of scope before the navigational properties could be loaded.
To do what you want, you would need to change how you are working with your context, or just eager load the entities you are going to need via table join(s) using the query syntax that you are using, or via .Include() lambda expressions if you use the lambda query syntax.
using (var context = new ApplicationDbContext())
{
context.Configuration.LazyLoadingEnabled=lazyLoadingEnabled;
var client = (from _client in context.Client
where _client.ApplicationUserId == userId
select _client).FirstOrDefault();
return client; //at this point, your context is gone, and no
//navigational properties will be loaded onto your client object,
//even if you try to navigate them. You may even get exceptions if
//attempting to navigate to some properties.
}
Here is a join example:
var client = (from _client in context.Client
join t in context.Table on _client.Val equals t.val //this will eager load Table property on client.
where _client.ApplicationUserId == userId
select _client).FirstOrDefault();
You should use Include method for properties.
_context.Client.Include(c=>c.JobsExperiences) ... and all props like this
but for you is better not to use lazy loading.
Cause context become inactive after you return from method.
Using EF 6, I had to use (lamba's and Client keyword is unavailable):
using (var context = new SyntheticData.EF.DBContext())
{
var item = (from t in context.TEMPLATEs
.Include("DATASET")
.Include("COLUMNs")
.Include("SORTs")
.Include("FILTERs")
where t.USERID == identityname && t.ID == id select t).FirstOrDefault();
return item;
}
This filled in relationship classes with syntax like:
item.DATASET.xxx
item.COLUMNs[0].xxx
The "using (var context" construct is good practice because it insures your connection to the database will be released back to the pool. If you don't, you can end up running out of conenctions for busy systems.
Just in case this helps someone I was not getting any navigation properties and my problem seemed to be that EF did not properly hookup the properties because I was using an interface as the navigational property type and I needed to use actual type. Unless anyone knows how to use annotations or something to tell EF the actual type that the property is mapped to.

When is it necessary to dispose db connection

I have a class that contains several common methods customized for use in my MVC app, that I use in several places. Here is an example of some:
private MyEntities db = new MyEntities();
public List<SelectListItem> GetLocationList()
{
var query =
db.v_LocationsAlphabetical.OrderByDescending(x => x.Category).ThenBy(x => x.LocationName).ToList()
.Select(x => new SelectListItem
{
Value = x.LocationID.ToString(),
Text = x.LocationName
});
return (query).ToList();
}
public IEnumerable<SelectListItem> GetStates()
{
var query = db.States.Select(x => new SelectListItem
{
Value = x.Abbr,
Text = x.Name
});
return(query);
}
public List<Person> GetPeople()
{
var query = db.Person.OrderBy(m => m.LastName).ThenBy(m => m.FirstName).ToList();
return (query);
}
Each one of these methods makes a call to the database to get data and I was wondering if I need to add a dispose to each method. If not, why? Thanks.
You shouldn't call dispose in each method, because the lifetime of db is the same as that of the enclosing class since it's not a local variable in a method.
The typical way to handle this is to make the current class IDisposable and call db.Dispose() in the Dispose() method.
There are multiple ways of handling db connection in .NET
One of my favorites is the one called one dbcontext per request, which basically means you initialize your dbcontext when needed, do the work without thinking about instantiating or disposing, and dispose automatically when the request is done. (kinda UnitOfWork-ish)
I've already shown this approach here. It's not only applicable to EF, but to Linq2SQL, ADO.NET, etc. as well.
No. DbContexts don't have to be manually disposed, unless you manually manage the connection yourself. Therefore, disposing them is usually optional.

Confused about attaching/detaching entities in EF

Using ASP.NET MVC and Entity Framework, I encounter some attach/detach errors when I need it to write changes to the database.
First here's how I get the records from my repository:
public PublishedApplication GetPublishedApplication(int id)
{
return dal.ExecuteFirstOrDefault(
context.PublishedApplication.Include("Customers")
.Where(w => w.Id == id));
}
Note that this is not being detached, and it has MergeOption = AppendOnly.
Another place, I call a method in my repo to attach a customer to the application:
public void AttachCustomer(int applicationId, int customerId)
{
var app = new PublishedApplication{ Id = applicationId};
var customer = new Customer { Id = customerId};
try
{
context.AttachTo("PublishedApplication", app);
context.AttachTo("Customer ", customer );
app.Customers.Add(customer);
context.SaveChanges();
}
catch
{
throw;
}
}
This, of course, gives me an error that the objects (app and customer) are already attached to an object context.
So I need to detach my objects in my repository. This is also more correct as I don't need to throw "query related information" around the web. But how can I do this? If I detach the entity in my GetPublishedApplication method, my graph of related data is lost, and I can't afford that. I need that graph elsewhere on my site. Can't use merge option NoTracking either.
So what options do I have?
Thanks in advance!
I think, there isn't easy solution for reattaching entities.
May be this post will help you:
Reattaching Entity Graphs with the Entity Framework on codeproject.com

Entity Framework (6.1.3) updates not reflected in the DataContext

1) I add a new entity to the context and call _context.SaveChanges(). Entity is added as expected
2) I update the same entity and call _context.SaveChanges() I put a break point immediately after and inspect the _context and my update is reflected on the entity AND is indeed saved to the DB.
3) I call _context.Set< T >().ToList() later in my code base and the update is NOT reflected on the entity. (_context does not reflect the updated value at this point)
What could possibly be the cause and what can I do to resolve? Any help would be greatly appreciated
Responding to request for code..
From the repository...
public List<T> GetAll()
{
return _context.Set<T>().ToList();
}
public void SaveChanges()
{
_context.SaveChanges();
var xxx = _context.Customers.ToList();
}
From the call to get all...
var customersToUpdate = _customerManager.GetAllCustomers();
From the CustomerManager...
public List<Customer> GetAllCustomers()
{
return _customerRepository.GetAll();
}
Pretty basic stuff.
Are you sure that you use only one instance of the _customerRepository? And that every time you update the _context you do it in the same context under _customerRepository ? It sounds like you have multiple instance, one get updated while the others dont
Basically you need to tell EF that you updated something.
Easiest way of doing that is:
var customer = _context.Customers.First();
customer.Name = "new name";
_context.Entry(customer).State = EntityState.Modified;
_context.SaveChanges();
Or you can be more specific of what is changed like below:
customer.Name = "new name";
context.Entry(customer).Property(u => u.Name).IsModified = true;
_context.SaveChanges();
You can enable automatic changes detection like that:
context.Configuration.AutoDetectChangesEnabled = true;
In this case
DbSet.Find
DbSet.Local
DbSet.Remove
DbSet.Add
DbSet.Attach
DbContext.SaveChanges
DbContext.GetValidationErrors
DbContext.Entry
DbChangeTracker.Entries
all will automatically detect changes.

Attaching entities to data contexts

In LINQ to SQL, is it possible to check to see if an entity is already part of the data context before trying to attach it?
A little context if it helps...
I have this code in my global.asax as a helper method. Normally, between requests, this isn't a problem. But right after signing in, this is getting called more than once, and the second time I end up trying to attach the Member object in the same unit of work where it was created.
private void CheckCurrentUser()
{
if (!HttpContext.Current.User.Identity.IsAuthenticated)
{
AppHelper.CurrentMember = null;
return;
}
IUserService userService = new UserService();
if (AppHelper.CurrentMember != null)
userService.AttachExisting(AppHelper.CurrentMember);
else
AppHelper.CurrentMember = userService.GetMember(
HttpContext.Current.User.Identity.Name,
AppHelper.CurrentLocation);
}
I believe there are two methods to do this.
DataContext.TableName.Contains(Item)
or we use the id field. If the item is inserted in the Database, then it will be assigned a row.
if(Item.id == 0)
DataContext.Insert(Item)
else
DataContext.Update(Item)
Rather than attaching to a new data context why not just requery the object in the new datacontext? It believe it is a more reliable and stateless strategy.

Categories

Resources