简体   繁体   中英

How to get related data from one to one relationship built by EF Core?

I have model class inheriting from IdentityUser , which has 1 to 1 relationship

public class CustomUser: IdentityUser
{
    public ExtendedUserData ExtendedData { get; set; }
}

And I have an ExtendedData model class:

public class ExtendedUserData
{
    public Guid Id { get; set; }

    public string LastName { get; set; }
    public string FirstName { get; set; }
    public string Contract { get; set; }
    public string ExtendedDataOfCustomUserId { get; set; }
    public CustomUser CustomUser { get; set; }
}

(made this with this EF core tutorial )

But, if I use this method to get data

public ExtendedUserData GetExtendedUserDataById(Guid id)
{
    return context.ExtendedUserDatas
                  .FirstOrDefault(x => x.ExtendedDataOfCustomUserId == id.ToString());
}

I try make like this

ExtendedUserData userData = new ExtendedUserData();
userData = GetExtendedUserDataById(id);
....
userData.CustomUser.Id;

OR

userData.CustomUser.UserName;

I get an error. like a NullRefExc

How can I get related data from ExtendedUserData (or mb CustomUser ) using 1 to 1 relationship?

I think about this, but besides this I didn't come up with anything else

public ExtendedUserData GetExtendedUserDataById(Guid id)
{
    ExtendedUserData ud = context.ExtendedUserDatas
                                 .FirstOrDefault(x => x.ExtendedDataOfCustomUserId == id.ToString());
    ud.CustomUser = context.CustomUsers
                           .FirstOrDefault(x => x.Id == id.ToString());
    return ud;
}

Maybe this can be done more correctly or shorter?

Thanks for your answers!

If you already have the CustomUser loaded and tracked by EF, this should work. If you only have the ID, you have to take care of related data yourself. Few ways:

Use eager loading

public ExtendedUserData GetExtendedUserDataById(Guid id)
{
    return context.ExtendedUserDatas
                  .Include(x => x.CustomUser)
                  .FirstOrDefault(x => x.ExtendedDataOfCustomUserId == id.ToString())
                  ;
}

good when you know that every consumer will want CustomUser loaded. In general code, it's difficult to tell what you need to Include in advance (CustomUser can also have related entities you may want to load, and so on, leading to long chain of .Include(...).ThenInclude(...) ).

Use explicit loading

public ExtendedUserData GetExtendedUserDataById(Guid id)
{
    var user = context.ExtendedUserDatas
                  .FirstOrDefault(x => x.ExtendedDataOfCustomUserId == id.ToString())
                   ;
    if (user != null)
    {
        context.Entry(user).Reference(x => x.CustomUser).Load();
    }
    return user;
}

This doesn't make much sense as written. But since anyone can do it, this way you can pass responsibility to load related data to consumer who knows better what it needs. Kinda pain to write when the relation chain gets longer and includes collections.

There is also lazy loading that hides all this complexity, but you have to take some steps to activate it, and it's easy to kill your performance with it if you forget it's there. More information about all three: https://docs.microsoft.com/en-us/ef/core/querying/related-data#lazy-loading

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