0

Firstly, I'm relatively new to Entity Framework stuff and WebAPI but, I'm currently writing a Web API for fun that involves a person being able to create a master "business" account that's able to create 'Businesses' and create and attach child accounts to them. There are separate tables that keep track of the businesses created under business accounts and a list of Workers that are attached to those businesses.

Given the following classes/code:

Business Class:

public class Business
{
    public int Id { get; set; }

    public string BusinessAccount { get; set; }
    public string BusinessName { get; set; }                

    public List<Worker> Workers { get; set; }
}

SchedulerDbContext:

public class SchedulerDbContext : DbContext
{
    public SchedulerDbContext()
        : base("SchedulerDbContext")
    {

    }

    public virtual DbSet<Business> Businesses { get; set; }
    public virtual DbSet<Worker> Workers { get; set; }

}

BusinessController: (only relevant section shown)

[Route("Create")]        
public IHttpActionResult Create(string name)
{
    if (String.IsNullOrWhiteSpace(name))
    {
        ModelState.AddModelError("BusinessNameError", "Invalid Business Name Entered.");
        return BadRequest(ModelState);
    }

    Business business = new Business()
    {
        BusinessName = name,
        BusinessAccount = User.Identity.Name,
        Workers = new List<Worker>() //<----a
    };

    using (var db = new SchedulerDbContext())
    {
        db.Businesses.Add(business); //<----b
        db.SaveChanges();
    }

    return Ok(business);
}

UserController: (only relevant section)

[Route("Register")]
public async Task<IHttpActionResult> Register(UserModel userModel)
{
    if (!ModelState.IsValid)
    {
        return BadRequest(ModelState);
    }

    AccountUser user = new AccountUser
    {
        UserName = userModel.UserName,
        Email = userModel.Email,
        BusinessAccount = User.Identity.Name
    };

    IdentityResult result = await this.SchedUserManager.RegisterUser(user, userModel);
    IHttpActionResult errorResult = GetErrorResult(result);
    if (errorResult != null)
    {
        return errorResult;
    }

    result = await this.SchedUserManager.AddToRoleAsync(user.Id, defaultRole);
    errorResult = GetErrorResult(result);
    if (errorResult != null)
    {
        return errorResult;
    }


    using (var db = new SchedulerDbContext())
    {
        var query = from record in db.Businesses
                    where record.Id == userModel.BusinessId
                    select record;
        var business = query.First();

        var worker = new Worker()
        {
            FirstName = userModel.FirstName,
            LastName = userModel.LastName,
            Email = userModel.Email
        };

        if (business.Workers == null) { business.Workers = new List<Worker>(); } //<----c

        business.Workers.Add(worker); //<----d
        db.SaveChanges();
    }

    return Ok();
}

Now with the code out of the way. There were multiple spots that I marked with arrows. Now the questions I have are...

When creating a Busines in the Create() function of the BusinessController, I assigned the Workers property a new List as seen at //<----a. So after that, it is added to the SchedulerDbContext at //<----b and changes saved.

Now, when creating a user account to attach to that business in the UserController's Register() function I ran into an issue. If running the line at arrow //<----d is done without the line at //<----c existing, I get a NullReferenceException.

What I'm curious about is why the line at //<----a didn't fix this? I'm pretty sure there isn't technically a record to assign to, but is checking to see if it's null (like at //<----c) the cleanest way to handle it? Part of me imagined EF to magically handle this for me. Is there a better way? It just seems to me an odd spot to assign a new List if it's empty, like it should be done somewhere else.

1 Answer 1

1

Because EF does not load all foreign rows by default. You either need to explicitly tell it to load the workers, or enable lazy loading for that property.

Explicitly loading:

var query = from record in db.Businesses
                where record.Id == userModel.BusinessId
                select record;
var business = query.Include("Workers").First();
//                   ^^^^^^^^^^^^^^^^^^

Allow lazy loading to kick in for that relationship:

public virtual List<Worker> Workers { get; set; }
//     ^^^^^^^
Sign up to request clarification or add additional context in comments.

1 Comment

Thank you. I assumed there was something I was missing that dealt with my problem. Did what you said and it works.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.