M Kenyon II
M Kenyon II

Reputation: 4264

DBContext create to records on object insert

I'm looking to find a good design for accomplishing the following.

In our C# MVC project using entity frameworks (and codefirst) we have, for sake of this conversation, 3 tables. Resource, Profile, Organization.

When we add a Profile record, we first need to create a Resource record, and using the ResourceId as the ProfileId. The same for adding an Organization.

I'm not sure if there is a good way to handle this. Right now we do something like this:

public IHttpActionResult PostProfile(Profile profile)
{
    if (!ModelState.IsValid)
    {
        return BadRequest(ModelState);
    }
    var res = new Resource();
    _db.Resource.Add(res);
    _db.SaveChanges();

    profile.ProfileId = res.ResourceId;
    _db.Profile.Add(profile);
    _db.SaveChanges();

    return CreatedAtRoute("DefaultApi", new { id = profile.ProfileId }, profile);
}

Is there a better way?

Upvotes: 0

Views: 792

Answers (2)

Marc Cals
Marc Cals

Reputation: 2989

Your code is not atomic what happens if the second SaveChanges crashes for example because database doesn't respond,...

It's better to take a approach with just one SaveChanges()

var res = new Resource();
_db.Resource.Add(res);

profile.ProfileId = res.ResourceId;
_db.Profile.Add(profile);
_db.SaveChanges();

A part from this I think that is better to model objects in a different way, instead of having the ID of Resource in Profile, have the entire object, and the you can get advantage of lazy loading, a more powerful queries.

public class Resource()
{
     public int ID { get; set; }
     .......
}

public class Profile()
{
     public int ID { get; set; } 
     .....
     public virtual Resource Resource { get; set; }
}

And the code to add the new item

var profile = new Profile() 
{
    Resource = new Resource(),
     ....
}

_db.Profile.Add(profile);
_db.SaveChanges();

Upvotes: 1

Jens R.
Jens R.

Reputation: 191

If your Profile class needs the ResourceId, you may add a new property:

public class Profile
{
    public int Id { get; set; }
    public int ResourceId { get; set; }
    public virtual Resource Resource { get; set; }
    ...
}

The virtual keyword is to manage change tracking and lazy loading.

In your db context OnModelCreating() method link both fields:

modelBuilder.Entity<Profile>().HasRequired(x => x.Profile)
    .WithMany().HasForeignKey(x => x.ProfileId);

Instead on HasRequired() use HasOptional(), if required. More on this topic can be found at MSDN - Configuring Relationships with the Fluent API.

Now you can add both objects in a single db operation:

var _profile = new Profile();
// set more _profile properties
_profile.Resource = new Resource 
                         {
                             // set resource properties, but
                             // exclude the Id property
                         };

_db.Profile.Add(_profile);
_db.SaveChanges();

Upvotes: 0

Related Questions