Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Update owned entity EF Core 5

I am implementing an api using .net 5. I have a student class which have a property with address type(value object according to ddd).

public class Student 
{
        public long Id{ get; private set; }
        public string FirstName { get; private set; }
        public string LastName { get; private set; }
        public Address Address { get; private set; }
}

public class Address
{
    public string City { get; private set; }
    public string Road { get; private set; }
}

I am using fluent api to configure the database using ef core 5.

 class StudentConfiguration : IEntityTypeConfiguration<Student>
    {

        public void Configure(EntityTypeBuilder<Student> builder)
        {
            builder.ToTable("Students");
            builder.HasKey(x => x.Id);

            builder.Property(x => x.Id).ValueGeneratedNever().IsRequired();
            builder.Property(x => x.FirstName).HasMaxLength(25).IsRequired();
            builder.Property(x => x.LastName).HasMaxLength(50).IsRequired();           


            builder.OwnsOne(x => x.Address, x =>
            {
                x.Property(pp => pp.City)
                    .IsRequired()
                    .HasColumnName("City")
                    .HasMaxLength(20);
                x.Property(pp => pp.Road)
                    .IsRequired()
                    .HasColumnName("Road")
                    .HasMaxLength(40);
            });

        }
    }

As a result I have one table with columns Id,Fistname,lastname,city,road.

Now I am trying to update only the city and the road(for example a student change house) but I have different exceptions and I don't know how to update only these 2 columns

public async Task UpdateAddress(Student student)
{
//Firts try 
            //var studentEntry = context.Entry(student);
            //studentEntry.Property(x => x.Address.City).IsModified = true;
            //studentEntry.Property(x => x.Address.Road).IsModified = true;
            //**Exception** 'The expression 'x => x.Address.City' is not a valid member access expression. The expression should represent a simple property or field access: 't => t.MyProperty'. (Parameter 'memberAccessExpression')'

//Second try 
             //var studentEntry = context.Entry(student.Address);
            //studentEntry.Property(x => x.City).IsModified = true;
            //studentEntry.Property(x => x.Road).IsModified = true;
            //**Exception** 'Database operation expected to affect 1 row(s) but actually affected 0 row(s). Data may have been modified or deleted since entities were loaded. 

//the only method that works is Update but this update the whole object
     context.Update(student);
     await context.SaveChangesAsync();
}

EDIT

public class StudentDbContext : DbContext
    {
        public DbSet<Student> Students { get; set; }

        public StudentDbContext(DbContextOptions<StudentDbContext> options) : base(options)
        {
            ChangeTracker.QueryTrackingBehavior = QueryTrackingBehavior.NoTracking;
        }

        protected override void OnModelCreating(ModelBuilder modelBuilder)
        {
            modelBuilder.ApplyConfigurationsFromAssembly(Assembly.GetExecutingAssembly());
            base.OnModelCreating(modelBuilder);
        }
    }

How can update only these two properties of an owned entity?


1 Answers

Address is owned entity type, hence Student.Address property by EF Core terminology is not a property, but reference navigation property, thus should be accessed via Reference method rather than Property method (none of them supports property path). Then you can use the returned tracking entry to access its members.

To force updating just some properties of the Student.Address, first attach the Student entity instance (to tell EF that it is existing)

var studentEntry = context.Attach(student);

and then use something like this

var addressEntry = studentEntry.Reference(e => e.Address);
addressEntry.TargetEntry.Property(e => e.City).IsModified = true;
addressEntry.TargetEntry.Property(e => e.Road).IsModified = true;

like image 91
Ivan Stoev Avatar answered Sep 07 '25 13:09

Ivan Stoev