I am working on an application using Entity Framework 4.1 with DbContext API, in a disconnected environment. I have two basic entities, Person and Degree. Degree has a non-mandatory one-to-many relationship to Person.
The issue is occurring when I update the DegreeId property on the Person entity to a different value. When I save the changes, EF generates an Update statement on the actual Degree table. This in turn causes a concurrency error violation when two or more users are using the application. I was able to find the issue while using SQL Profiler. I’ve tried several configuration variations using the Fluent API, but nothing seems to suppress the additional Update statement on the Degree table.
Here are my entities:
public partial class Person
{
public int PersonId { get; set; }
public string FirstName { get; set; }
public string MiddleName { get; set; }
public string LastName { get; set; }
public Nullable<int> DegreeId { get; set; }
public Degree Degree { get; set; }
}
public partial class Degree
{
public int DegreeId { get; set; }
public string Name { get; set; }
}
In my Repository class, I am loading the Person object graph as such:
public override Person GetById(int id)
{
return DataContext.People
.Include(d => d.Degree)
.FirstOrDefault(x => x.PersonId == id);
}
In my Service layer, I am getting a person record, and then updating the DegreeId property to a specific value. Note: UnitOfWork.Commit method exposes SaveChanges on DbContext.
using (var unitOfWork = IoC.Resolve<IUnitOfWork>())
{
var personRepository = new PersonRepository(unitOfWork);
var person = personRepository.GetById(240);
person.DegreeId = 1;
personRepository.Update(person);
unitOfWork.Commit();
}
My repository update method attaches the person entity and marks the entity state as modified:
var state = DataContext.Entry(entity).State;
dbSet.Attach(entity);
DataContext.Entry(entity).State = EntityState.Modified;
Here is the SQL statement found in the Profiler session:
exec sp_executesql N'declare #p int
update [Client].[Degree]
set #p = 0
where (([DegreeId] = #0) and ([RowVersion] = #1))
select [RowVersion]
from [Client].[Degree]
where ##ROWCOUNT > 0 and [DegreeId] = #0',N'#0 int,
#1 binary(8)',#0=1,#1=0x0000000000004469
Does anyone know how to stop EF from sending this update statement to SQL Server? Is there something apparent in my entity configuration that causes EF to assume the Degree is also affected?
Thank you.
I was able to find the cause of this issue and prevent it from occurring, but I cannot really explain why it was occurring.
My tables include a TimeStamp column and a corresponding property in the base class for my entities.
I did not show the base class in my original question because it only includes the RowVersion and other audit properties, which I assumed were irrelevant.
One would think I would've learned by know not assume anything about Entity Framework.
Here is my base class definition for the Degree entity:
public abstract class EntityBase : ValidableObject, IEntityBase
{
public virtual byte[] RowVersion { get; protected set; }
public virtual DateTime? CreateDate { get; set; }
public virtual string CreateUser { get; set; }
public virtual DateTime? ModifyDate { get; set; }
public virtual string ModifyUser { get; set; }
}
Here is my context model configuration for the Degree entity:
internal class DegreeConfiguration : EntityTypeConfiguration<Degree>
{
internal DegreeConfiguration()
: base()
{
ToTable("Degree", "dbo");
Property(x => x.RowVersion).IsRowVersion();
}
}
Because of my application requirements, I must load the Person entity using the Include method to eagerly load the Degree entity so the object graph is
fully populated when the consumer requests the entity.
return ctx.People.Include(p => p.Degree).Where(x => x.PersonId == id).First();
When the DegreeId property of the Person object is modified and attached to the Context, the following Update statement is generated
upon calling SaveChanges():
exec sp_executesql N'declare #p int
update [dbo].[Degree]
set #p = 0
where (([DegreeId] = #0) and ([RowVersion] = #1))
select [RowVersion]
from [dbo].[Degree]
where ##ROWCOUNT > 0 and [DegreeId] = #0',N'#0 int,
#1 binary(8)',#0=2,#1=0x00000000000007DF
This is occurring even though I am not knowingly updating the Degree entity and causes havoc when two or more users using the application simultaneously.
To suppress the Update statement from being generated on the Degree navigation property, I commented out the concurrency check on the model configuration as such:
internal class DegreeConfiguration : EntityTypeConfiguration<Degree>
{
internal DegreeConfiguration()
: base()
{
ToTable("Degree", "dbo");
//Property(x => x.RowVersion).IsRowVersion();
}
}
Upon re-executing the process, EF no longer generates the problematic Update statement.
I've done a considerable number of searches both on MS site for EF 4.1, as well as general Google searches. I cannot come up with any concrete explanations.
Thank you.
Related
I have the following table structure as shown in the picture. (see: Table structure). Both tables ("Batches" and "Methods") reference to a "Project" table.
When I now create a new Project I would like to get all childs created as well.
Doing so I did the follwoing:
_dbContext.Projects.Where(x => x.Id == prjId)
.Include(x => x.Batches)
.Include(x => x.Batches.Select(y => y.Measurements))
.Include(x => x.Methods).AsNoTracking().FirstOrDefault();
Now the problem is the following:
New Batch and Method instances are created - thus they get a new ID(PK). The referenced Project_Id (FK) is set correct. But in my new Measurement instance only the Batch_Id(FK) is set correct and the Method_Id remains unchanged (has the old value) (see: result).
What I need is that the Measurements.Mehtod_Id is set from the Methods table. Is there any suitable solution for that?
My entities look like the following
public class Project
{
[Key]
public long Id { get; set; }
public string Name { get; set; }
public bool IsActive { get; set; }
public virtual List<Batch> Batches { get; set; }
public virtual List<Method> Methods { get; set; }
}
public class Batch : BaseObject
{
public Batch()
{
BatchFiles = new List<FileAttachment>();
Measurements = new List<Measurement>();
}
public long Id { get; protected set; }
public long Project_Id { get; set; }
public virtual Project Project { get; set; }
public virtual List<Measurement> Measurements { get; set; }
}
public class Method : BaseObject
{
public Method()
{
Parameters = new List<Parameter>();
}
public long Id { get; protected set; }
public long Project_Id { get; set; }
public virtual Project Project { get; set; }
public virtual List<Measurement> Measurements { get; set; }
}
public class Measurement
{
public int Id { get; protected set; }
[ForeignKey("Batch")]
public long? Batch_Id { get; set; }
[Required]
public virtual Batch Batch { get; set; }
[ForeignKey("Method")]
public long? Method_Id { get; set; }
public virtual Method Method { get; set; }
}
// creation code (just a copy with new IDs for all childs)
Project newProjectVersion = _dbContext.Projects.Where(x => x.Id == prjId)
.Include(x => x.Batches)
.Include(x => x.Batches.Select(y => y.Measurements))
.Include(x => x.Methods)
.AsNoTracking().FirstOrDefault();
_dbContext.Projects.Add(newProjectVersion);
_dbContext.SaveChanges();
Thanks for any help!
The first problem is that your Select statement doesn't connect Measurements to Methods because of the AsNoTracking() addition. Only Projects and Methods are connected because they are explicitly Included off of the Project entity. The Measurements have a Method_id but this is value is not accompanied by a Method in their Method property. You could check that in the debugger if you walk through the object graph (with lazy loading disabled though!). Because of this, when all entities will be Add-ed to the context, EF won't notice that measurements receive new methods.
You could get tempted to fix that by Include-ing Measurement.Method as well:
...
.Include(x => x.Batches.Select(y => y.Measurements.Select(m => m.Method)))
...
Now you'll see that Measurement.Method will be populated everywhere in the object graph.
However, there's a gotcha here. When using AsNoTracking, EF6 doesn't keep track of entities it materialized (duh). This means that for each Measurement it creates a new Method instance, even if an identical Method (by id) was materialized before for another Measurement. (And in this case it will always materialize duplicates, because you already include Project.Methods.)
That's why you can't do this in the quick way with AsNoTracking and Add using one context instance. You'll get an error that EF tries to attach duplicate entities.
You must build the object graph using one context, with tracking, so EF will not materialize duplicates. Then you must Add this object graph to a new context. Which will look like this:
Project project;
using(var db = new MyContext())
{
db.Configuration.ProxyCreationEnabled = false;
project = db.Projects.Where(x => x.Id == prjId)
.Include(x => x.Batches)
.Include(x => x.Batches.Select(y => y.Measurements))
.Include(x => x.Methods).FirstOrDefault();
}
using(var db = new MyContext())
{
db.Projects.Add(project);
db.SaveChages();
}
Three remarks:
Proxy creation is disabled, because you can't attach a proxy to another context without explicitly detaching it first.
No, I didn't forget to include Measurement.Method. All methods are loaded by including them in the Project and now (because of tracking, and assuming that measurement will only have methods of the project they belong to), EF connects them with the Measurements by relationship fixup.
EF-core is smarter here: when adding AsNoTracking it won't track materialized entities, but still, it won't create duplicates either. It seems to have some temporary tracking during the construction of an object graph.
thanks for your answer so far. This works quite fine right now. Unfortunately I noticed that the Measurements entity has another required relationship to a table named 'MeasurementTypes':
[Required]
public virtual MeasurementType MeasurementType { get; set; }
[ForeignKey("MeasurementType")]
public long MeasurementType_Id { get; set; }
In contrast to Batches and Methods these entries must not be copied and the entries already exist in the MeasrementTypes table.
What would be a good way to put the required reference to the Measurements?
there are two tables named project and city like this:
public class Project
{
[Key]
public int ProjectId { get; set; }
public string ProjectName { get; set; }
public int CityID { get; set; }
public City City { get; set; }
}
public class City
{
[Key]
public int CityID { get; set; }
public string CityName { get; set; }
public ICollection<Project> Projects { get; set; }
}
Here, CityID in the project class is the foreign key to City class joining both tables.I have properly configured and checked that both are inter connected to each other.
Now, i have a Json action method to fetch the Desired property like this:
public JsonResult GetProjects()
{
var ret = (from project in db.Projects.ToList()
orderby project.ProjectId
select new
{
CityName = project.City.CityName,
ProjectId = project.ProjectId
}).AsEnumerable();
return Json(ret, JsonRequestBehavior.AllowGet);
}
here, i am trying to push out the cityName but i am unable to get back cityname.It is giving System.NullRefernceException at line CityName = project.City.CityName.Please suggest me what to do now. Is something wrong in my code. i am able to get other properties.
Whe you use somehting like .ToList(), .ToArray(), and so on, you are materializing the query. In LINQ to EF materailizing the query means running the SQL query and populating your classes with the data received form the DB. From that point on, there is no chance that the following referred properties are retrieved from the dtabase.
LINQ to EF uses IQueryable<T> interface. An IQueryable<T> is a "potential query" that has not already been "executed". While you do things that doesn't materialize the query, your query will keep being a IQueryable<T> and it won't be executed in the DB.
There is also another thing that materializes the queryable: enumerating it.
So, what you need to is to not materialize the query until you have provided all the information necessary to run the query. In this case you have to remove the .ToList()
I noticed something interesting when I was performing a delete using EF code first. I use the following domain model:
public class User
{
public virtual long Id { get; set; }
public virtual string Name { get; set; }
public virtual ICollection<Playlist> Playlists { get; set; }
}
public class Playlist
{
public virtual long Id { get; set; }
public virtual string Title { get; set; }
public virtual User User { get; set; }
public virtual ICollection<Track> Tracks { get; set; }
}
public class Track
{
public virtual long Id { get; set; }
public virtual string Title { get; set; }
public virtual Playlist Playlist { get; set; }
}
The model is configured using:
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<User>().HasMany(x => x.Playlists).WithRequired(x => x.User).Map(x => x.MapKey("UserId"));
modelBuilder.Entity<Playlist>().HasMany(x => x.Tracks).WithRequired(x => x.Playlist).Map(x => x.MapKey("PlaylistId"));
}
I use a generic repository:
public virtual void Delete(T entity)
{
Database.Set<T>().Remove(entity);
}
I also have a dto that looks like:
public class PlaylistDTO
{
public PlaylistDTO(Playlist playlist)
{
Id = playlist.Id;
Title = playlist.Title;
User = playlist.User.Name;
}
}
In one of my services I am trying to do the following:
public PlaylistDTO Delete(long id)
{
Playlist playlist = playlistRepository.GetById(id);
playlistRepository.Delete(playlist);
unitOfWork.Commit();
return PlaylistDTO(playlist);
}
This code fails. When I stepped through the debugger I noticed something interesting. The moment I call playlistRepository.Delete the navigational properties (User and Tracks) get set to null and empty respectively. Playlist however stays in memory. So when I pass in the playlist to the DTO the code will fail when it is trying to access playlist.User.Name. I wanted to pass this data to the client to display a verification.
Is this behavior correct? Is this by design?
This is how EF works. The problem is that your Playlist forms entity graph with other relations and EF uses very simple rule for tracking entity graphs: All entities in the graph must be tracked - there cannot be reference to entity which is not tracked. I don't give you reference to description of this rule, it is just my observation but I didn't find any single exception to this rule.
Edit: Updated version - I just checked internal implementation and relations are indeed nulled during calling Delete
So what happened in your code.
You marked your Playlist as deleted
EF passes delete operation to the state manager which does the fixup - it will null all relations
You saved changes to the database
Because there are no cascade deletes from Playlist all related objects remain undeleted
Once you saved changes EF internally accepted them and set change tracker to current state
Because the current state of Playlist is non existing (deleted in the database) it was detached from the context
Detaching has broken entity graph and EF fixed it by modifying navigation properties on both ends
The code responsible for nulling from System.Data.Objects.EntityEntry.Delete(doFixup) (doFixup is true) - the class is internal:
if (doFixup && (base.State != EntityState.Deleted))
{
this.RelationshipManager.NullAllFKsInDependentsForWhichThisIsThePrincipal();
this.NullAllForeignKeys();
this.FixupRelationships();
}
In your scenario this should have simple workaround - create DTO before you delete entity.
As I already have classes for my LINQ to SQL data access solution, what trouble might I run into if I wanted to migrate them over to EFCF instead? I'm hesistant to call this code first as the database does already exist. To be clear, the application is not yet in production so if EFCF wipes out the data it's no real loss.
Can I take a class such as the one that follows and simply use it in EFCF? Should I or must I remove the data annotation attributes?
What needs to change where I have EntityRef and EntitySet?
[Table]
public class PlanMember {
private EntityRef<SystemUser> _caseManager;
private EntityRef<PlanMemberStatus> _status;
public PlanMember() {
this.PlanMemberView = new Views.PlanMember();
}
[Column(
IsPrimaryKey = true,
IsDbGenerated = true,
AutoSync = AutoSync.OnInsert
)]
public Int64 ID { get; set; }
[Column]
public DateTime? BirthDate { get; set; }
[Column]
public String City { get; set; }
[Column]
public String FirstName { get; set; }
[Association(ThisKey = "CaseManagerID", Storage = "_caseManager")]
public SystemUser CaseManager {
get { return (this._caseManager.Entity); }
set { this._caseManager.Entity = value; }
}
[Column]
public String CaseManagerID { get; set; }
[Column]
public Boolean IsActive { get; set; }
public Boolean IsEligible {
get { return (this.PlanMemberView.IsEligible); }
}
[Column]
public String LastName { get; set; }
[Column]
public String MedicalRecord { get; set; }
[Column]
public String MemberNumber { get; set; }
[Column(Name = "PCPFullName")]
public String PrimaryCarePhysicianFullName { get; set; }
[Association(OtherKey = "PlanMemberID")]
public Views.PlanMember PlanMemberView { get; set; }
[Column]
public Int32 PostalCode { get; set; }
[Column]
public String Sex { get; set; }
[Column]
public String State { get; set; }
[Association(ThisKey = "StatusID", Storage = "_status")]
public PlanMemberStatus Status {
get { return (this._status.Entity); }
set { this._status.Entity = value; }
}
[Column]
public Int32 StatusID { get; set; }
}
We migrated an application from Linq to Sql to EF POCO generation but haven't tried code first as it wasn't baked at the time. Was really not horribly difficult. The main pain point in our case was the following differences:
Linq to Sql handles many to many relationships using a separate "bridge" object, EF treats those relationships as collections of various sorts. This changes lots of semantics and can cause lots of code to change, especially if you let entities creep into the UI.
Another pain point was nullable and non-nullable relationships. Linq to Sql was a bit more forgiving here, but for EF to play well we needed to allow nullable columns some places we traditionally had not.
Linq to Sql and EF data mapping sometimes have different ideas about what CLR types to map to. Xml columns were our major pain point but you might not have any of those.
Big trick/nightmare was how to get rid of the l2s bits without breaking everything horribly as linq to sql generates your entities.
This is not something I would try without a pretty effective set of unit tests to give you an automated basis to give you pretty regular temperature readings. Our other godsend was we had a pretty solid Repository pattern implementation -- nothing was talking directly to the EF/Linq2Sql bits but two classes implementing IRepository. Basically, this is a great test for how disciplined you were in implementing your architecture. Also, it is an occasion when you realize that resharper is worth every cent.
To answer your one direct question, I don't think the attributes will necessarily matter but I would remove them so as not to have any potential confusion and/or namespace collisions.
Assuming your classes are named the same as your database tables, and the properties of your classes match the database column names, you should be able to delete all the attributes and use these same classes as your EF code-first model (I don't think you have to delete the attributes, but unless you plan to continue using them in a Linq2Sql model, there's no reason to keep them, and since some things will probably change in the migration, it would probably be best to delete them since your new entities may not still be able to work in Linq2Sql). If your classes don't match your database schema, Scott Guthrie has a blog post about Entity Framework 4 "Code-First": Custom Database Schema Mapping.
What needs to change where I have EntityRef and EntitySet?
A relation defined as EntityRef<OtherEntity> can be replaced with a property of just type OtherEntity, and an EntitySet<OtherEntity> can become an ICollection<OtherEntity> or anything that implements ICollection<T> such as an IDbSet<OtherEntity> (I believe a DbSet<T> is what you would get if you were generating the model from your existing database).
I'm trying to learn Entity Framework Code First development with ASP.NET MVC3.
Let's say I have a simple data Model for an Auction and Bids and I'd like to query all the Auctions and their Bids.
I have turned off LazyLoadingEnabled and ProxyCreationEnabled.
Here is the code I have:
public class MiCoreDb2Context : DbContext
{
public MiCoreDb2Context()
: base()
{
this.Configuration.LazyLoadingEnabled = false;
this.Configuration.ProxyCreationEnabled = false;
}
public DbSet<Auction> Auctions { get; set; }
public DbSet<Bid> Bids { get; set; }
}
public class Auction
{
public int AuctionId { get; set; }
public virtual ICollection<Bid> Bids { get; set; }
}
public class Bid
{
public long BidId { get; set; }
public int AuctionId { get; set; }
[ForeignKeyAttribute("AuctionId")]
public virtual Auction Auction { get; set; }
}
public JsonResult Thing()
{
List<Auction> auctions;
using (var db = new MiCoreDb2Context())
{
var auctions = (from a in db.Auctions.Include("Bids") select a).ToList();
}
return Json(auctions, JsonRequestBehavior.AllowGet);
}
When I load the page, a circular reference occurs. How will I get around this?
When I load the page, a circular reference occurs. How will I get around this?
By using view models (and by the way that's the answer to any question you might have concerning ASP.NET MVC :-)). Ayende Rahien has an excellent series of blog posts on this topic.
Conclusion: absolutely always pass/take view models to/from a view. Absolutely never pass/take models (EF, domain, ...) to/from a view. Once this fundamental rule is being respected you will find out that everything works.
I solved this problem by doing a projection in the Linq to Entities query. This will create anonymous types which can be serialized to json without any circular reference issues.
var result =
from Item in dbContext.SomeEntityCollection
where SomePredicate
select new { Property1 = Item.Property1, Property2 = Item.Property2 };
Return Json(result, JsonRequestBehavior.AllowGet);
BOb