4

I have two entity classes that have a one-to-many relationship.

public class Call : IEntity
{
    public int Id { get; set; }
    public int UserId { get; set; }
    public virtual User User { get; set; }
}

public class User : IEntity
{
    public int Id { get; set; }

    public string Username { get; set; }
    public virtual ICollection<Call> Calls { get; set; }
}

And I have a view model for 'Call' operations on the web layer.

public class CallVm : IViewModel
{
    public string Id { get; set; }
    public string UserFullname { get; set; }
}

And I use a method to convert my 'Call' object to 'CallVm' object. This method is briefly as follows.

public CallVm MapCallVm(Call call)
{
    return call == null ? null : new CallVm { Id = call.Id, UserFullname = call.User?.Fullname };
}

When I read the 'Call' entity from the database, I sometimes include 'User' and sometimes I don't. When I do not include it, there is no User property definition in the Call object because it is lazy loading. Therefore, I get the following error in MapCallVm method.

The ObjectContext instance has been disposed and can no longer be used for operations that require a connection.

Is there a way to check this? I just want to assign UserFullname = call.User?.Fullname when there is a eager load. The only solution I can think of is controlling with try-catch. Is there a different solution?

1
  • you could disable lazy loading then you would know as it will throw an exception. Commented Feb 5, 2020 at 7:01

2 Answers 2

2

You can use DbReferenceEntry.IsLoaded Property.

Gets or sets a value indicating whether the entity has been loaded from the database.

if (_dbContext.Entry(Call).Reference(e => e.User).IsLoaded)

Updated

If you are getting value without dbContext, you should force the query to Eager loading instead.

Read the following post to have a better understanding.

Should we disable lazy loading of Entity Framework in web apps?

Sign up to request clarification or add additional context in comments.

5 Comments

I do not have access to the dbContext object in 'MapCallVm' method. I need to do this control without mixing the context class. Can't I control it directly over the 'Call' object? For this I wrote a code like this:
public CallVm MapCallVm(Call call) { string userFullName; try { userFullName = call.User?.Username; } catch { userFullName = null; } return call == null ? null : new CallVm { Id = HashidsHelper.Encrypt(call.Id), ContactId = HashidsHelper.Encrypt(call.ContactId), UserFullname = userFullName, IsAccess = call.IsAccess, Date = call.Date }; }
If you are getting value without dbContext, you should force the query to Eager loading instead. Anyway, your code is okie. But It's not a good way.
You should read this link to have a better understanding.
Phong is right - try to avoid passing database entities to your views
0

As @Phong's answer - avoid passing DbContext. Normally, your repository class should map DB entities to simple POCO/DTO objects.

I suggest to introduce mapper class. This will help you to unit test your logic

// Interface to inject to repository
public interface ICallMapper
{
    CallVm Map(Call call);
}

public class CallMapper : ICallMapper
{
    public CallVm Map(Call call)
    {
        return call == null ? null : new CallVm { Id = call.Id, UserFullname = call.User?.Username };
    }
}

Pass mapper to repository and ensure that your objects are not connected with DB anymore

public class CallRepository : ICallRepository
{
    private readonly ICallMapper _callMapper;

    public CallRepository(ICallMapper callMapper)
    {
        _callMapper = callMapper;
    }

    public IList<CallVm> GetList()
    {
        // Call DB and get entities
        var calls = GetCalls();

        // Map DB entities to plain model
        return calls.Select(_callMapper.Map).ToList();
    }
}

This lets you to get rid of your error. And makes your program more structable and testable.

Comments

Your Answer

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