简体   繁体   中英

Persisting Entity Framework nested/related entities

I have the following entities:

public class ModuleCriteria
{
    public int ModuleCriteriaId { get; set; }

    public string Criteria { get; set; }

    public List<ModuleCriteriaLookup> ModuleCriteriaLookups { get; set; 
}
}

public class ModuleCriteriaLookup
{
    public int ModuleCriteriaLookupId { get; set; }

    public int ModuleCriteriaId { get; set; } // ** (Foreign Key) **

    public int SiteId { get; set; }

    public string CategoryId { get; set; }

    public ModuleCriteria ModuleCriteria { get; set; }
}

I have the following EF configuration in my Context class (edited for brevity):

protected override void OnModelCreating( DbModelBuilder modelBuilder )
{
    base.OnModelCreating( modelBuilder );

    modelBuilder.Conventions.Remove<PluralizingTableNameConvention>();

    modelBuilder.Entity<ModuleCriteriaLookup>().HasRequired( mc => mc.ModuleCriteria );

    // I tried adding the below line but it made no difference.
    //modelBuilder.Entity<ModuleCriteria>().HasMany( mc => mc.ModuleCriteriaLookups );
}

...and I have the following DbSet properties defined in my Context class:

public DbSet<ModuleCriteria> ModuleCriteria { get; set; }

public DbSet<ModuleCriteriaLookup> ModuleCriteriaLookup { get; set; }

I have a CriteriaRepository class, which has a Save method, for persisting changes for my ModuleCriteria entities:

public void Save( ModuleCriteria moduleCriteria )
{
    using ( var ctx = new MyAppContext() )
    {
        ctx.Entry( moduleCriteria ).State = moduleCriteria.ModuleCriteriaId == 0 ? EntityState.Added : EntityState.Modified;

        ctx.SaveChanges();
    }
}

A ModuleCriteria object can exist without a ModuleCriteriaLookup object, but ModuleCriteriaLookup object has to relate to an existing ModuleCriteria object (related on ModuleCriteriaId ).

You can have multiple ModuleCriteraLookup objects all relating to the same one ModuleCriteria object.

The behaviour that I would like, and expect with EF, is:

1) If I create a new ModuleCriteria object (without any ModuleCriteriaLookups ), call the Save method in my repository, I would expect to see new ModuleCriteria record in the db with no related ModuleCriteriaLookup records in the db.

2) If I create a new ModuleCriteria object and assign a List<ModuleCriteriaLookup> to it, call the Save method in my repository, I would expect to see new ModuleCriteria record in the db and x new ModuleCriteriaLookup rows in the db which relate to that particular ModuleCriteria .

3) If I add/edit/remove any of the ModuleCriteriaLookup objects that related to one of my ModuleCriteria objects, then call the Save method in my repository, I would expect to see any of the ModuleCriteria 's deleted ModuleCriteriaLookup objects to get removed from the db, any new ones added and any edited ones simply to get updated.

So all I ever need worry about is that whatever the ModuleCriteria.ModuleCriteriaLookups property contains for a given ModuleCriteria , that's what will be reflected in the 2 tables in my DB by simply calling the Save method for the ModuleCriteria object in my repository.

Unfortunately at the moment, if I'm adding a new ModuleCriteria object with associated List<ModuleCriteriaLookup> it adds both ModuleCriteria and x ModuleCriteriaLookup rows in the db nicely. But when I want to edit or delete entries in the ModuleCriteria.ModuleCriteriaLookups property, this is not being reflected in the db. Nothing is happening with the ModuleCriteriaLookups rows.

I'm not sure where exactly the problem is, whether its whether the EF mapping configuration, or something to do with how the repository works?

The problem is located in the repository. The DbContext needs to be aware of the existence of entities. So when editing and/or deleting entities the entities need to be fetched from the database first.

This description clearly states:

The .Entry property returns objects from the context that are being tracked by the context.

Because you directly use this properties right after creating the context, the context isn't tracking these entities and is therefore not aware that something has changed. And thereby is unable to generate the correct SQL statements.

There are several ways to deal with this, depending on the rest of your design.

One way to delete it would be:

    public void DeleteModuleCriteriaLookup(ModuleCriteriaLookup[] lookups)
    {
        using (var ctx = new MyAppContext())
        {
            var moduleCriteriaId = lookups.First().ModuleCriteriaId;

            var moduleCritria = (
                from criteria in ctx.ModuleCriteria
                where criteria.ModuleCriteriaId == moduleCriteriaId
                select criteria
                ).Single();

            var lookupIdsToDelete = lookups.Select(l => l.ModuleCriteriaLookupId);

            var lookupsToDelete = (
                from lookup in moduleCritria.ModuleCriteriaLookups
                where lookupIdsToDelete.Contains(lookup.ModuleCriteriaLookupId)
                select lookup
                ).ToArray();

            foreach (var lookup in lookupsToDelete)
            {
                moduleCritria.ModuleCriteriaLookups.Remove(lookup);
            }

            ctx.SaveChanges();
        }
    }

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM