Entity Framework 6: Add to child collections - entity-framework

I am using EF6 code first. I have an parent entity (Expense) which has collection of child entity (Tag). Entities are
public class Expense : Entity
{
//public Expense()
//{
// this.Tags = new HashSet<Tag>();
//}
public string Title { get; set; }
public ICollection<Tag> Tags { get; set; }
//public long TagId { get; set; }
public decimal Amount { get; set; }
public long ByUserId { get; set; }
public User ByUser { get; set; }
public long? ForUserId { get; set; }
public User ForUser { get; set; }
public DateTime ExpenseDate { get; set; }
}
public class Tag : Entity
{
public Tag()
{
this.Expenses = new HashSet<Expense>();
}
public string Name { get; set; }
public virtual ICollection<Expense> Expenses { get; set; }
public long OrganizationId { get; set; }
public Organization Organization { get; set; }
}
Configured as
public ExpenseConfiguration()
{
this.HasRequired(x => x.ByUser)
.WithMany()
.HasForeignKey(x => x.ByUserId)
.WillCascadeOnDelete(false);
this.HasOptional(x => x.ForUser)
.WithMany()
.HasForeignKey(x => x.ForUserId)
.WillCascadeOnDelete(false);
this.HasMany(x => x.Tags)
.WithMany(t => t.Expenses)
.Map(et => {
et.MapLeftKey("ExpenseId");
et.MapRightKey("TagId");
et.ToTable("tblExpenseTags");
});
}
public TagConfiguration()
{
this.HasRequired(x => x.Organization)
.WithMany()
.HasForeignKey(p => p.OrganizationId)
.WillCascadeOnDelete(false);
}
The way I am saving entity
var tags = new List<Tag>();
foreach (var item in expense.Tags)
{
var tag = _TagHandler.Get().Where(x => x.Id == item.Id).FirstOrDefault();
tags.Add(tag);
}
entity.Tags.Clear();
foreach (var item in tags)
{
expense.Tags.Add(item);
}
if (expense.Id == 0)
{
entity.CreatedDate = DateTime.UtcNow;
entity.UpdatedDate = DateTime.UtcNow;
updated = EntityRepository.Add(entity);
}
else
{
expense.UpdatedDate = DateTime.UtcNow;
updated = EntityRepository.Update(entity);
}
UnitOfWork.Commit();
When i Create Expense I add multiple tags to it and it save successfully in tblExpenseTags, but the problem is when I add new tags to Expense they are not saving into tblExpenseTags table. I used sql profiler to see what calls are going to SQL database and I don't see any insert call at the time of update expense.
Please let me know what wrong am I doing ?

Related

EF6 Ignoring related data

Scenario
public class Product : Entity, IAggregateRoot
{
public string Name { get; set; }
public string Dimension { get; set; }
public decimal Volume { get; set; }
public bool Featured { get; set; }
public Farm Farm { get; set; }
public int FarmId { get; set; }
/// <summary>
/// Sell Price
/// </summary>
public decimal BidPrice { get; set; }
public int QuantityAvaliable { get; set; }
public ICollection<Image> Images { get; set; }
public string Description { get; set; }
public Category Category { get; set; }
public int CategoryId { get; set; }
public DateTime Created { get; set; }
public DateTime? Modified { get; set; }
}
public class Category : Entity, IAggregateRoot
{
public string Title { get; set; }
public string CategoryImage { get; set; }
public Category Parent { get; set; }
public DateTime Created { get; set; }
public DateTime? Modified { get; set; }
}
Relationship setup
public class ProductMap : EntityTypeConfiguration<Product>
{
public ProductMap()
{
HasKey(x => x.Id);
Property(x => x.Created).HasColumnType("DateTime");
Property(x => x.Modified).HasColumnType("DateTime");
Property(x => x.BidPrice).HasColumnType("Decimal");
#region RELATIONSHIP
//BelongsTo
HasRequired(x => x.Farm);
HasRequired(x => x.Category);
HasMany(x => x.Images);
#endregion
}
So I have this two model where I need to bring the data from Product model with Category information
I have checked my database, the data is consistent, the Product record have the FK for the Category record.
but when I try to get Product Data using EF6, the category information doesnt come, I get a null object.
Because of = () =>
{
_product = _repository.Find(p => p.Id == 1, p => p.Category);
};
It should_not_be_bull = () =>
_product.Category.ShouldNotBeNull();
the response from data base is for Category is null. but the record is there.
I had it working properly before. for some random magic reason it just stop working.
THE FIND method
public virtual TEntity Find(Expression<Func<TEntity, bool>> predicate = null, params Expression<Func<TEntity, object>>[] includes)
{
var set = CreateIncludedSet(includes);
return (predicate == null) ?
set.FirstOrDefault() :
set.FirstOrDefault(predicate);
}
the CreateIncludeSet
private IDbSet<TEntity> CreateIncludedSet(IEnumerable<Expression<Func<TEntity, object>>> includes)
{
var set = CreateSet();
if (includes != null)
{
foreach (var include in includes)
{
set.Include(include);
}
}
return set;
}
the CreateSet method
private IDbSet<TEntity> CreateSet()
{
return Context.CreateSet<TEntity>();
}
MY DbContext implementation is here
https://github.com/RobsonKarls/FreedomWebApi/blob/dev/Source/Freedom.Infrastructure.DataAccess/Factories/FreedomDbContext.cs
all project is there too for further analisys
any help is valuable.
Thank you
The problem in your code is in this line in CreateIncludedSet method:
set.Include(include);
Yes, you include the data but you do not change you set. You should change it to something like:
set = set.Include(include);
Your code is a bit unclear, but try something like this....
_product = _repository.Include(p => p.Category).SingleOrDefault(x => x.Id == 1);
also see...
https://stackoverflow.com/a/7348694/6200410

Entity Framework Core (EF 7) many-to-many results always null

I have followed the instructions for the workaround for many-to-many described in Issue #1368 and the Docs Here... but when I try to navigate, it always returns null.
My Models:
public class Organization
{
public Guid OrganizationID { get; set; }
//...
public ICollection<OrganizationSubscriptionPlan> OrganizationSubscriptionPlans { get; set; }
}
public class SubscriptionPlan
{
public int SubscriptionPlanID { get; set; }
//...
public ICollection<OrganizationSubscriptionPlan> OrganizationSubscriptionPlans { get; set; }
public class OrganizationSubscriptionPlan
{
[ForeignKey("Organization")]
public Guid OrganizationID { get; set; }
public Organization Organization { get; set; }
[ForeignKey("SubscriptionPlan")]
public int SubscriptionPlanID { get; set; }
public SubscriptionPlan SubscriptionPlan { get; set; }
}
ApplicationDbContext:
protected override void OnModelCreating(ModelBuilder builder)
{
base.OnModelCreating(builder);
builder.Entity<OrganizationSubscriptionPlan>().HasKey(x => new { x.OrganizationID, x.SubscriptionPlanID });
builder.Entity<OrganizationSubscriptionPlan>().HasOne(x => x.Organization).WithMany(x => x.OrganizationSubscriptionPlans).HasForeignKey(x => x.OrganizationID);
builder.Entity<OrganizationSubscriptionPlan>().HasOne(x => x.SubscriptionPlan).WithMany(x => x.OrganizationSubscriptionPlans).HasForeignKey(x => x.SubscriptionPlanID);
}
And my Query:
var organizations = _context.Organizations
.Include(o => o.OrganizationSubscriptionPlans);
foreach (var organization in organizations)
{
//....
var subscriptions = organization.OrganizationSubscriptionPlans
.Select(s => s.SubscriptionPlan);
// ^^^^^^^^^^^ why is subscriptions always null?
}
The "organizations" query returns the results as expected, including the list of OrganizationSubscriptionPlans within each one, but when I try to navigate to them in the foreach loop the "subscriptions" query returns null every time. What am I doing wrong?
Turns out it's a Lazy Loading issue. You have to "Include" the joining entity and then "ThenInclude" the other entity.
var organizations = _context.Organizations
.Include(o => o.OrganizationSubscriptionPlans)
.ThenInclude(s => s.SubscriptionPlan);
ForeignKey attr is to decorate reference properties to indicate them what primitive property hold the FK value.
public class OrganizationSubscriptionPlan
{
public Guid OrganizationID { get; set; }
[ForeignKey("OrganizationID")]
public Organization Organization { get; set; }
public int SubscriptionPlanID { get; set; }
[ForeignKey("SubscriptionPlanID")]
public SubscriptionPlan SubscriptionPlan { get; set; }
}

Entity Framework 6: one-to-many doesn't update foreign key when inserting and removing in the same operation

I have these classes in my project (the names in the code are in Portuguese, if necessary I can translate) :
public class EntityWithGuid : IEntityWithId<string>
{
protected EntityWithGuid()
{
this.Id = Guid.NewGuid().ToString("N").ToLower();
}
[Key]
public string Id { get; set; }
}
public class Servico : EntityWithGuid
{
public DateTime? Data { get; set; }
public string Descricao { get; set; }
public string Cliente_Id { get; set; }
[ForeignKey("Cliente_Id")]
public Cliente Cliente { get; set; }
[Required]
public ICollection<ServicoImagem> Imagens { get; set; }
[Required]
public ICollection<Tag> Tags { get; set; }
}
public class ServicoImagem : EntityWithGuid
{
[Required]
public string Nome { get; set; }
public string Servico_Id { get; set; }
[Required]
public Servico Servico { get; set; }
}
public class Tag : EntityWithGuid
{
[Required]
public string Nome { get; set; }
public string Fonetica { get; set; }
public ICollection<Servico> Servicos { get; set; }
}
And this is the Context configuration:
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Configurations.Add(new ServicoConfiguration());
}
internal class ServicoConfiguration : EntityTypeConfiguration<Servico>
{
internal ServicoConfiguration()
{
this.HasMany(s => s.Tags)
.WithMany(t => t.Servicos)
.Map(mf =>
{
mf.MapLeftKey("Servico_Id");
mf.MapRightKey("Tag_Id");
mf.ToTable("ServicoTag");
});
this.HasMany(s => s.Imagens)
.WithRequired(i => i.Servico)
.HasForeignKey(f => f.Servico_Id);
}
}
After load a Servico entity the update method can do any operation with the Servico.Tags property (add and remove items), mark as modified and finally call Context.SaveChanges(). Everything works perfectly.
var servico = Context.Set<Servico>()
.Include(x => x.Cliente)
.Include(x => x.Tags)
.Include(x => x.Imagens)
.FirstOrDefault(x => x.Id == id);
...
// Remove tags
servico.Tags = servico.Tags.Except(oldTags).ToList();
// Add new tags
servico.Tags = servico.Tags.Concat(newTags).ToList();
...
Context.Entry(servico).State = EntityState.Modified;
Context.SaveChanges();
If I do the same thing with the Images property is only possible to make one type of operation at a time, add OR remove. If added and removed at the same time, the added item does not receive the value of the foreign key and error occurs in Context.SaveChanges() but if I do only one type of operation, it works perfectly.
The only solution I found was to make a loop to mark the item as deleted.
// Mark image as deleted
foreach (var imagem in imagensParaDeletar)
{
Context.Entry(imagem).State = System.Data.Entity.EntityState.Deleted;
}
I would like to understand why the problem ONLY occurs in this type of relationship and ONLY when I need to do both type of operation on the same property.

Entity Framework Many To Many returns null

I have Many To Many relationship defined and when I try to query for the records that should be in the map I get null.
public class Record
{
public int RecordId { get; set; }
public DateTime DateRecordCreated { get; set; }
public ICollection<Street> Streets { get; set; }
public ICollection<Street> CrossStreets { get; set; }
}
public class RecordMap : EntityTypeConfiguration<Record>
{
public RecordMap()
{
// Primary Key
this.HasKey(t => t.RecordId);
this.HasMany(r => r.Streets)
.WithMany(c => c.Records)
.Map(sl =>
{
sl.ToTable("StreetRecordMap", "dbo");
sl.MapLeftKey("RecordId");
sl.MapRightKey("StreetId");
});
this.HasMany(r => r.CrossStreets)
.WithMany(c => c.AnotherRecord)
.Map(sl =>
{
sl.ToTable("AnotherStreetRecordMap", "dbo");
sl.MapLeftKey("RecordId");
sl.MapRightKey("StreetId");
});
this.Property(t => t.DateRecordCreated).IsRequired();
}
}
public class House : Record
{
public string HouseNumber { get; set; }
public string StreeName { get; set; }
public int ZipCode { get; set; }
}
public class Street
{
public int StreetId { get; set; }
public string StreetName { get; set; }
public ICollection<Record> Records { get; set; }
public ICollection<Record> AnotherRecord { get; set; }
}
Now when I run the following query below I get houses.CrossStreets as null, I tried adding enabling lazy loading and had the same out come.
public static void GetRecords()
{
using (var context = new SandboxContext())
{
var entities = context.Houses.Include(r => r.CrossStreets);
var houses = entities.ToList();
}
}

Entity Framework 6, Invalid Column Name - Incorrect Mapping

I solved this by adding in the ForeignKey("Route") data annotation like this:
[ForeignKey("Route")]
public Guid rnh_rtefk { get; set; }
this is my first attempt at using EF.
I have created a model using Code First from database in my MVC app using EF 6.0.0.0
In my database I have a table of Routes and a table of RunSheetHeader. A route can have many RunSheetHeader and a RunSheetHeader can have one Route. The Routes primary key is Routes.rte_pk and this maps to the foreign key: RunSheetHeader.rnh_rtefk.
The code generated is this:
public partial class Route
{
public Route()
{
RunSheetHeaders = new HashSet<RunSheetHeader>();
}
[Key]
public Guid rte_pk { get; set; }
[Required]
[StringLength(50)]
public string rte_name { get; set; }
public virtual ICollection<RunSheetHeader> RunSheetHeaders { get; set; }
}
[Table("RunSheetHeader")]
public partial class RunSheetHeader
{
public RunSheetHeader()
{
RunSheetDetails = new HashSet<RunSheetDetail>();
}
[Key]
public Guid rnh_pk { get; set; }
[Column(TypeName = "date")]
public DateTime rnh_date { get; set; }
public Guid rnh_rtefk { get; set; }
public virtual Route Route { get; set; }
public virtual ICollection<RunSheetDetail> RunSheetDetails { get; set; }
}
This is from the Context class:
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<Route>()
.HasMany(e => e.RunSheetHeaders)
.WithRequired(e => e.Route)
.HasForeignKey(e => e.rnh_rtefk)
.WillCascadeOnDelete(false);
modelBuilder.Entity<RunSheetHeader>()
.HasMany(e => e.RunSheetDetails)
.WithRequired(e => e.RunSheetHeader)
.HasForeignKey(e => e.rnd_rnhfk)
.WillCascadeOnDelete(false);
}
The error I get is:
"Invalid column name 'Route_rte_pk'."
and the SQL shows up in SQL Profiler as:
SELECT
1 AS [C1],
[Extent1].[rnh_pk] AS [rnh_pk],
[Extent1].[rnh_date] AS [rnh_date],
[Extent1].[rnh_rtefk] AS [rnh_rtefk],
[Extent1].[Route_rte_pk] AS [Route_rte_pk]
FROM [dbo].[RunSheetHeader] AS [Extent1]
From reading the other answers here regarding similar problems it seems to be a problem with mapping the correct foreign keys, but it looks to me like that has been done correctly. Can anyone spot what I am missing?
Thanks for any help
I found 2 possible solutions to this:
1.
[ForeignKey("Route")]
public Guid rnh_rtefk { get; set; }
or 2.
[ForeignKey("rnh_rtefk")]
public virtual Route Route { get; set; }
Both get rid of the error, but I was advised that the second option is the better one to use.
the following code is running just fine, are you sure your OnModelCreating is well called ?
namespace testef {
public partial class Route {
public Route() {
RunSheetHeaders = new HashSet<RunSheetHeader>();
}
[Key]
public Guid rte_pk { get; set; }
[Required]
[StringLength(50)]
public string rte_name { get; set; }
public virtual ICollection<RunSheetHeader> RunSheetHeaders { get; set; }
}
[Table("RunSheetHeader")]
public partial class RunSheetHeader {
public RunSheetHeader() {
//RunSheetDetails = new HashSet<RunSheetDetail>();
}
[Key]
public Guid rnh_pk { get; set; }
[Column(TypeName = "date")]
public DateTime rnh_date { get; set; }
public Guid rnh_rtefk { get; set; }
public virtual Route Route { get; set; }
//public virtual ICollection<RunSheetDetail> RunSheetDetails { get; set; }
}
// ---------------
public class TestEFContext : DbContext {
public TestEFContext(String cs)
: base(cs) {
Database.SetInitializer<TestEFContext>(new DropCreateDatabaseAlways<TestEFContext>());
}
protected override void OnModelCreating(DbModelBuilder modelBuilder) {
base.OnModelCreating(modelBuilder);
modelBuilder.Entity<Route>()
.HasMany(e => e.RunSheetHeaders)
.WithRequired(e => e.Route)
.HasForeignKey(e => e.rnh_rtefk)
.WillCascadeOnDelete(false);
//modelBuilder.Entity<RunSheetHeader>()
// .HasMany(e => e.RunSheetDetails)
// .WithRequired(e => e.RunSheetHeader)
// .HasForeignKey(e => e.rnd_rnhfk)
// .WillCascadeOnDelete(false);
}
public DbSet<Route> Routes { get; set; }
}
class Program {
String cs = #"Data Source=ALIASTVALK;Initial Catalog=TestEF;Integrated Security=True; MultipleActiveResultSets=True";
using (TestEFContext ctx = new TestEFContext(cs)) {
Route r = new Route {
rte_pk = Guid.NewGuid(),
rte_name = "test"
};
r.RunSheetHeaders.Add(new RunSheetHeader {
rnh_pk = Guid.NewGuid(),
rnh_date = DateTime.Now
});
ctx.Routes.Add(r);
ctx.SaveChanges();
Console.WriteLine(ctx.Routes.Count());
}
using (TestEFContext ctx = new TestEFContext(cs)) {
foreach (Route r in ctx.Routes) {
Console.WriteLine(r.rte_name);
foreach (RunSheetHeader rsh in r.RunSheetHeaders) {
Console.WriteLine(" {0}", rsh.rnh_date);
}
}
}
}
}
}