EF 6 optional FK , delete still produces error - entity-framework

So i have these two simple models
public class Blog
{
public int BlogId { get; set; }
private string _Name;
[Column("sNameColumn")]
public string Name { get { return _Name; } set { _Name = value; } }
public virtual List<Post> Posts { get; set; }
}
public class Post
{
public int PostId { get; set; }
public string Title { get; set; }
public string Content { get; set; }
public int? blog_id { get; set; }
[ForeignKey("blog_id")]
public virtual Blog Blog { get; set; }
}
I haven't done anything unusual at the dbContext definition. Then , i try to do something like this.
db.Blogs.Remove(db.Blogs.Find(2));
db.SaveChanges();
And i get a FK violation error.Notice that the FK blog_id is null-able, so i thought that EF whould handle the delete, and make the corresponding FK Null.
Can you tell me what i am missing?

The entities have to be loaded for EF to be able to handle setting their foreign key to null.
var b = db.Blogs.Find(2);
db.Entry(b).Collection(b => b.Posts).Load();
db.Blogs.Remove(b);
db.SaveChanges();

Keep in mind that Entity Framework can only update entities it has loaded.
Of course there are ways to update database records by raw SQL statements that you can also execute through EF, but that's not the core of EF as ORM.
So if you want to use EF only, you have no choice. You have to load collections in Blogs explicitly for them to get dissociated from their parent. For instance by Include:
var b = db.Blogs.Include(b => b.Posts).Include(b => b.Comments)
.Single(b => b.BlogId == 2);
db.Blogs.Remove(b);
db.SaveChanges();
Or by Load as in the other answer.
Another option could be to use Entity Framework Extented. One of its features is batch updates, which allows one-shot update statements of records in an IQueryable given a template record. This would look like this:
using EntityFramework.Extensions;
...
db.Posts.Where(p => p.BlogId == 2)
.Update(p => new Post { BlogId = default(int?) });
db.Blogs.Remove(b);
db.SaveChanges();
Only the properties that are set in the template record are modified. To make this transactional, you should wrap all statements in a TransactionScope.

Related

how to load several related entities but not with all his fields, with include?

I have a method that loads many related entities using include clauses, but this to create a query is too big. I need to load many related entities, but I want to load only the fields that are important to me.
public RECEIPT_REMITS GetByIDWithIncludes(string UUID)
{
return Context.RECEIPT_REMITS.Where(h => h.UUID == UUID) .
Include(r => r.DEPOSITE)
.Include(r => r.PROVIDER)
.Include(r => r.RECEIPT_KINDS)
.Include(r => r.RECEIPT_REMITS_DETAIL.Select(d =>
d.RECEIPT_REMITS_SERIES)).FirstOrDefault();
}
Now the sentence loads the data correctly, but only slowly and also bringing fields that I do not need. How can I do this?
Entities represent data records. Complete data records. This makes them poor choices to be used for other purposes such as models for views. Instead you should adopt view models then map entities to view models either via .Select() or using Automapper with it's .ProjectTo<T>() method which integrates with EF's IQueryable implementation. Even if some of the view models end up being identical to the EF model, they serve separate purposes. An entity should always represent the row it is associated to, so you can't expect to tell EF to return a partially filled entity.
For instance if I have a table ReceiptRemit with 10 columns that I care about, and I also want to include related Deposits, but I only care about the ID and amount from the Deposit table:
Entities:
[Table("RECEIPT_REMIT")]
public class ReceiptRemit
{
[Key]
public string UUID { get; set; }
public string Field1 { get; set; }
public string Field2 { get; set; }
// etc. etc.
public virtual ICollection<Demerit> Demerits { get; set; } = new List<Demerit>();
}
[Table("DEMERITE")]
public class Demerit
{
[Key]
[Column("DEMERITE_ID")]
public int DemeritId { get; set; }
public decimal Amount { get; set; }
public string Field1 { get; set; }
public string Field2 { get; set; }
// etc. etc. to match the table, but stuff I don't care about...
}
View Models:
[Serializable]
public class ReceiptRemitViewModel
{
public string UUID { get; set; }
public string Field1 { get; set; }
public string Field2 { get ; set; }
// etc. etc.
public IEnumerable<DemeritSummaryViewModel> Demerits { get; set; } = new List<DemeritSummaryViewModel>();
}
[Serializable]
public class DemeritSummaryViewModel
{
public int DemeritId { get; set;}
public decimal Amount { get; set; }
}
Then to read: (.Select())
public ReceiptRemitViewModel GetByID(string UUID)
{
return Context.ReceiptRemits.Where(h => h.UUID == UUID)
.Select(x => new ReceiptRemitViewModel
{
UUID = x.UUID.
Field1 = x.Field1,
Field2 = x.Field2,
Demerits = x.Demerits.Select(d => new DemeritSummaryViewModel
{
DemeritId = d.DemeritId,
Amount = d.Amount
}).ToList(),
}.Single();
}
Which can be a bit of a pain with several related summary details to load, but this can be simplified by using Automapper. Automapper can figure out most common mapping details by convention or be configured for anything specific that doesn't work. Once set up, the above becomes:
return Context.ReceiptRemits.Where(h => h.UUID == UUID)
.ProjectTo<ReceiptRemitViewModel>()
.Single();
Alternatively for things like bulk operations you can define a different entity definition for the related data and register these alternative entities to a new DbContext definition. It has to be a separate DbContext declaration because a DbContext cannot have 2 entities mapped to the same table. This works well for situations where you might need to load relatively large #s of records to inspect and potentially update only a subset of related entities and fields.

Entity Framework Core - recursive parent/child linking

I have an "account" table that includes a string foreign-key ("parent_guid") to its "parent" account (if one exists). I would like to create an entity that knows its parent, as well as all of its children.
Here is my entity:
[Table(name:"accounts")]
public class Account
{
[Key]
public string Guid { get; set; }
public string Name { get; set; }
[Column("guid")]
public string accountGuid { get; set; }
[Column(name: "parent_guid")]
public string parentGuid { get; set; }
[ForeignKey("parentGuid")]
public Account Parent { get; set; }
[InverseProperty("Parent")]
public ICollection<Account> Children { get; set; }
}
Here's my dbContext:
public DbSet<Split> Splits { get; set; }
public DbSet<Account> Accounts { get; set; }
public ReportContext(DbContextOptions<ReportContext> options)
: base(options)
{ }
My query is through the 'splits' context as the source table, but I end up returning Accounts. Maybe there's a better way?
When I query for an Account by Guid, I get a result, but 'Parent' and 'Children' are always null, even though 'parentGuid' contains the correct value, and I have confirmed that there should be child records.
Any idea how to make this work, either through annotations or fluent API?
Yes, EF Core requires explicit inclusion of relational entities.
var accounts = await dbContext.Accounts.Include(account => account.Parent)
.Include(account => account.Children)
.ToListAsync();
##EDIT
As per the edits to the question, this is one way to Eager Load relational entities, but I cannot speak to the efficiency of this query without knowing the relations and indexes.
public IQueryable<Split>
FindAllByAccountGuidsPostedBefore(IEnumerable<string> accounts,
DateTime endDate) {
using (reportContext) {
return reportContext.Splits.Include(s => s.Account)
.ThenInclude(a => a.Parent)
.ThenInclude(a => a.Children)
.Where(s => accounts.Contains(s.Account.Guid)
&& s.Transaction.postDate < endDate);
}
}
One way to obtain that information is by looking at the console when this query is run to find the SQL statement(s) this produces, or by asking someone who is more experienced in Relational Databases :)

how to add foreign keys in EF 7 alpha

How do I make a one-to-one relationship in EF 7 alpha3?
The old way of just defining navigation properties does not work, and the modelBuilder does not have the previously used HasRequired/HasOptional methods.
Can anyone shed some light on that?
Until recently, there weren't any model builder APIs for defining relationships. Instead, you have to manipulate the underlying modelBuilder.Model object. Here is an example of a one-to-many relationship.
class Blog
{
public Blog()
{
Posts = new List<Post>();
}
public int Id { get; set; }
public ICollection<Post> Posts { get; set; }
}
class Post
{
public int Id { get; set; }
public int BlogId { get; set; }
public Blog Blog { get; set; }
}
class BlogContext : DbContext
{
public DbSet<Blog> Blogs { get; set; }
public DbSet<Post> Posts { get; set; }
protected override void OnModelCreating(ModelBuilder builder)
{
builder.Entity<Post>().ForeignKeys(x => x.ForeignKey<Blog>(p => p.BlogId));
var model = builder.Model;
var blog = model.GetEntityType(typeof(Blog));
var post = model.GetEntityType(typeof(Post));
var fk = post.ForeignKeys.Single(k => k.ReferencedEntityType == blog);
blog.AddNavigation(new Navigation(fk, "Posts", pointsToPrincipal: false));
post.AddNavigation(new Navigation(fk, "Blog", pointsToPrincipal: true));
}
}
You can read more about our current (as of 2014-07-31) thinking for what these APIs will look like. The end result would look something like the following.
modelBuilder.Entity<Blog>()
.OneToMany(b => b.Posts, p => p.Blog).ForeignKey(b => b.BlogId);
With EF7 beta7, new set of methods are introduced for defining relationships between entities.
For one to many relationship,
modelBuilder.Entity<Post>()
.Reference(typeof(Blog), "Blog")
.InverseCollection("Posts")
.ForeignKey(new string[] { "BlogId" });
With, .Reference(typeof(Blog), "Blog") relationship from Entity Post to Blog is configured. First arguament is the type of the entity that Post targets and second arguament is the name of the navigation property.
With, .InverseCollection("Posts"), one to many relationship is configured. Arguament to this function is the name of the navigation collection.
With, .ForeignKey(new string[] { "BlogId" }), foreign key is configured. If this foreign key is not set, then shadow foreign key is automatically generated for you.

Entity Framework TPH Inheritance Data Modeling Issues

I'm new to Entity Framework and C#/.Net and trying to create a TPH inheritance model, I'm not sure if I should be or not, so if not, please advise,
Here's the model:
public abstract class Vote
{
public int VoteID { get; set; }
public int UserID { get; set; }
public bool Value { get; set; }
public DateTime DateCreated { get; set; }
public User User { get; set; }
}
public class ProjectVote_ : Vote
{
public int ProjectID { get; set; }
public virtual Project Project { get; set; }
}
public class CommentVote_ : Vote //There are three more like this, votes for different hings
{
public int CommentID { get; set; }
public virtual Comment Comment { get; set; }
}
Now the Project model (comment and model is similar)
public class Project
{
public int ProjectID { get; set; }
public string Title { get; set; }
public virtual ICollection<Vote> Vote { get; set; }
}
What happens is that ICollection creates a database column Project_ProjectID as the foreign key in the Vote table (I think) instead of using the ProjectID I defined. How do I fix it or should I model it differently. If the fluent API is the way to fix it, I don't know how to do that.
In the end I want to be able to use one table to store 5 different types of votes.
When you have related entities you don't need to have a property to store the FK in your model. Entity framework knows that it needs to make a FK to the Project table in ProjectVote when it detects Project in your ProjectVote_ model. Same thing with User and UserId and Comment and CommentId. You don't need to have a property that stores the FK in your model.
You are getting the FK column with the name you don't like "Project_ProjectID" because Entity framework is detecting that it needs to create a FK for your navigation property "Project". It's using it's own naming convention to create the column hence "Project_ProjectID".
If you want to provide your own name for the column override OnModelCreating in your DBContext class and add this fluent mapping.
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Project>()
.HasMany(p => p.Vote)
.HasRequired(v => v.Project) //or .WithOptional(v => v.Project)
.Map(m => m.MapKey("ProjectId")); //or any other name you want.
}
And for the future this is a helpful reference for how to use the Fluent API. For example here is some documentation on how to custimize TPH with fluent.
Hope that helps!

Error in Entity Framework Saving with Child Entity

I have the following model:
public class Job
{
[Key]
public int JobID { get; set; }
public string Status { get; set; }
public DateTime JobDate { get; set; }
public string JobTitle { get; set; }
public int? Cleaner { get; set; }
public int? Client { get; set; }
public int EstTime { get; set; }
public virtual Client ClientInfo { get; set; }
public virtual Valeter ValeterInfo { get; set; }
}
This in OnModelCreating:
// Relationship Job -> Valeter
modelBuilder.Entity<Job>()
.HasOptional<Valeter>(u => u.ValeterInfo)
.WithMany()
.HasForeignKey(e => e.Cleaner);
(NOTE: it is using an existing database). When I try to perform the following:
if (ModelState.IsValid)
{
db.Entry(job).State = EntityState.Modified;
db.SaveChanges();
}
It generally works fine UNLESS I change the Cleaner value to something else and then I get the error:
A referential integrity constraint violation occurred: The property
values that define the referential constraints are not consistent
between principal and dependent objects in the relationship.
This exception usually occurs if job.ValeterInfo != null and job.ValeterInfo.ValeterId != job.Cleaner. So, the simplest solution is to set the navigation property to null before you attach the job to the context:
if (ModelState.IsValid)
{
job.ValeterInfo = null;
db.Entry(job).State = EntityState.Modified;
db.SaveChanges();
}
This looks a bit strange and like a hack. But the question is why job.ValeterInfo is NOT null when you post the data to the controller action. When you set the state of the job to Modified you are only updating the job's scalar properties (including Cleaner) but not any properties of job.ValeterInfo or any relationships. So, you don't need to send job.ValeterInfo properties to the server in the first place.
Anyway, you have an inconsistency: The FK job.Cleaner is changed but the related entity job.ValeterInfo (especially its primary key property ValeterId) is not. EF doesn't know which represents the correct relationship: The foreign key property value or the navigation property value? This ambiguity causes the exception.