Nic Estrada
Nic Estrada

Reputation: 532

Can I monitor a property for changes without using a custom set accessor?

Entity Framework Core doesn't like it when I use a custom set accessor on a virtual foreign key navigation property with lazy loading enabled. It throws a runtime exception during startup/configuration. I would like to run some custom code every time one of these properties is set though. Is there any way I can make this happen without EF Core yelling at me? Code examples/clarifications below:

public class CoilUnit
{
    [Key]
    public Guid Id { get; set; }

    [ForeignKey("project")]
    public virtual CoilProject Project { get; set; }
}
public class CoilProject
{
    [Key]
    public Guid Id { get; set; }
}

The above setup works just fine in EF Core, but it doesn't do what I want it to do.

This is what I want:

public class CoilUnit
{
    [Key]
    public Guid Id { get; set; }

    private CoilProject project;

    [ForeignKey("project")]
    public virtual CoilProject Project
    {
        get { return project; }
        set { project = value; /* Other code here */ }
    }
}
public class CoilProject
{
    [Key]
    public Guid Id { get; set; }
}

On the code side of things, this does what I want, but EF Core doesn't like it and fails to run when I do this.

Is there any way in this situation to have my cake and eat it too? Any way to get the /* Other Code */ to run properly while also letting EF Core have its way?

Thanks.

Upvotes: 1

Views: 275

Answers (2)

Ivan Stoev
Ivan Stoev

Reputation: 205589

The issue has nothing to do with lazy loading and custom property setter, but the conflict between the backing field and shadow FK property names.

When applied to navigation property, ForeignKey attribute specifies the FK property name. If no such property exists, EF Core will create shadow property with that name.

With auto-property the backing field name is generated by the compiler, and is not "project", so no problem. But in the second case there is already field named "project", so EF Core decides that this is the FK property specified by the attribute. But of course it isn't, and also is not the expected type (Guid or Guid?), hence the exception.

If you were using conventional FK property/column names (like ProjectId), you won't need that attribute and won't have issue. I'm pretty sure you don't care about shadow property name, but the table column name.

So one way to resolve the issue is to remove the ForeignKey attribute and use fluent API to configure the FK column name:

private CoilProject project;

public virtual CoilProject Project
{
    get { return project; }
    set { project = value; /* Other code here */ }
}

and

modelBuilder.Entity<CoilUnit>()
    .Property<Guid?>(nameof(CoilUnit.Project) + "Id")
    .HasColumnName("project");

Another way is to keep the attribute but rename the navigation property backing field to one of the other supported patterns, for instance "_project":

private CoilProject _project;

[ForeignKey("project")]
public virtual CoilProject Project
{
    get { return _project; }
    set { _project = value; /* Other code here */ }
}

Upvotes: 3

romfir
romfir

Reputation: 402

You can use other property to keep your buisness logic, which won't be mapped by EF.

public class CoilUnit
{
    [Key]
    public Guid Id { get; set; }

    private CoilProject project;

    [ForeignKey("project")]
    public virtual CoilProject Project { get; set; }

    [NotMapped]
    public virtual CoilProject ProjectNotMapped
    {
        get { return project; }
        set { project = value; /* Other code here */ } 
    }
}

additionaly properties which do not have either getters or setters also won't be mapped link:

        public virtual CoilProject ProjectNotMapped
        {
            set { project = value; /* Other code here */ } 
        }

Upvotes: 1

Related Questions