Setting a default object for Entity Framework navi

2019-07-09 11:42发布

问题:

Is it possible to set a default object for an Entity?

Say I have a Person entity that originally did not have a requirement for a Profile.

Now I require a Profile - but there are existing entities that do not currently have a Profile.

Is there a way I can provide a default object for these Entities when they're loaded in future, so anyone using the Person Entity can assume that Profile is never null and always has a value - even if it's a default.

Below you can see what I've tried - which does create a default value - but even when there is something in the database it always return the default object.

  • If the Profile is null I want to return a default initialzied object
  • If the Profile is not null I want to return the object from the database

Additionally - what would be the most sensible way to attach the "default" object to my dbcontext?

How can I achieve this desired behavior?

public class Person
{
    [Key]
    public int Id {get; set;}

    private Profile _profile;
    public virtual Profile Profile
    {
        get
        {
            return _profile ?? (_profile= new Profile
            {
                Person = this,
                PersonId = Id
            });
        }
        set
        {
            _profile = value;
        }

        // properties
    }
}

public class Profile
{
    [Key, ForeignKey("Person")]
    public int PersonId {get; set;}

    [ForeignKey("PersonId")]
    public virtual Person Person{ get; set; }

    // properties
}

I know you can initialize collections so that they're not null, but I'd like to initialize a single object too.

回答1:

Use the ObjectContext.ObjectMaterialized Event.

This event is raised for each entity that is being loaded into context after materialization.

In the constructor for your context, subscribe to this event. And in the event handler, check if the entity type is Person, and if so, create the new profile for the person. Here is a code sample:

public class Context : DbContext
{
    private readonly ObjectContext m_ObjectContext;

    public DbSet<Person> People { get; set; }
    public DbSet<Profile> Profiles { get; set; }

    public Context()
    {
        var m_ObjectContext = ((IObjectContextAdapter)this).ObjectContext;

        m_ObjectContext.ObjectMaterialized += context_ObjectMaterialized;

    }

    void context_ObjectMaterialized(object sender, System.Data.Entity.Core.Objects.ObjectMaterializedEventArgs e)
    {

        var person = e.Entity as Person;

        if (person == null)
            return;

        if (person.Profile == null)
            person.Profile = new Profile() {Person = person};

    }
}

Please note that if you commit changes, the new profiles will be saved back to the database.



回答2:

Yes.

You just need to implement the IdProfile 'get' property in person, where the profile ID must be setted.

Let me try to show you using fluent API:

public class Person
{
    public int Id {get; set;}
    private int _idProfile;
    public int IdProfile {
        get{
            return _idProfile == 0 ? DEFAULT_PROFILE_ID : _idProfile;
        }
        set{
            _idProfile  = value;
        }
    }

    public virtual Profile @Profile;
}

public class Profile
{
    public int Id {get; set;}
    public virtual ICollection<Person> Persons { get; set; }
}

/* MAPPING */

public class PersonMap : EntityTypeConfiguration<Person>
{
    this.HasRequired(t => t.Profile)
            .WithMany(t => t.Persons)
            .HasForeignKey(d => d.IdProfile);
}