asp.net .NET 7一对一关系-导航属性Null

ukxgm1gy  于 2023-04-22  发布在  .NET
关注(0)|答案(2)|浏览(148)

在我的.NET 7 Web API中,尽管dependent(Researcher)正确地包含了parents id属性,但parent(User)上的navigation属性仍然为null。我试图在AuthService中同时创建User和Researcher。如果您能帮助我如何让User包含对Researcher的引用,我将不胜感激。

**编辑:**经过进一步测试,我发现User.Researcher和Researcher.User都是null,我唯一的参考是Researcher.UserId是正确的。

The API response:

[
  {
    "userId": "d8725e37-b3b2-4851-8ae7-07afadb92adc",
    "email": "a@email.com",
    "passwordHash": "$2a$11$hfvUxb44m.b0/RYWBwyQvuaxF0mcRhmiGmqjldvOlDiAmnOiHF92i",
    "researcher": null
  },
  {
    "userId": "3eb55b33-8c41-405e-a19c-5390c472302e",
    "email": "b@email.com",
    "passwordHash": "$2a$11$uwvZErMttnzYRpwUO2LfOOvvjURZrSs5V68lp2L8.MtC6gubs7bkO",
    "researcher": null
  },
  {
    "userId": "b2fc3e76-02c5-48a4-b865-b686870e429a",
    "email": "c@email.com",
    "passwordHash": "$2a$11$hKDeNsfK5qnPf5uFujKgu.LB5d/FSDVFnj.vN49vAmd.zZn/ogaLW",
    "researcher": null
  }
]

The api schema:

[
  {
    "userId": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
    "email": "string",
    "passwordHash": "string",
    "researcher": {
      "researcherId": 0,
      "firstName": "string",
      "lastName": "string",
      "field": "string",
      "areaOfFocus": "string",
      "biography": "string",
      "user": "string",
      "joinEntities": [
        {
          "researcherArticleId": 0,
          "researcherId": 0,
          "researcher": "string",
          "articleId": 0,
          "article": {
            "articleId": 0,
            "title": "string",
            "url": "string",
            "articleData": "string",
            "joinEntities": [
              "string"
            ]
          }
        }
      ]
    }
  }
]

User.cs父项:

public class User
    {
        public Guid UserId { get; set; } = Guid.NewGuid();
        public string Email { get; set; } = string.Empty;
        public string PasswordHash { get; set; } = string.Empty;
        public Researcher? Researcher { get; set; }
    }

Researcher.cs相关:

public class Researcher
    {
        [Key]
        public int ResearcherId { get; set; }
        public string FirstName { get; set; } = string.Empty;
        public string LastName { get; set; } = string.Empty;
        public string Field { get; set; } = string.Empty;
        public string AreaOfFocus { get; set; } = string.Empty;
        public string Biography { get; set; } = string.Empty;
        [ForeignKey("UserId")]
        public User? User { get; set; }
        public List<ResearcherArticle>? JoinEntities { get; set; }

    }

AuthService.cs方法,用于在同一个调用中创建User和Researcher:

public async Task<ServiceResponse<UserRegisterResponseDto>> RegisterUser(UserRegisterRequestDto request)
        {
            List<string> userValidationMessages = ValidateUser(request);

            var serviceResponse = new ServiceResponse<UserRegisterResponseDto>() { };

            if (userValidationMessages.Count > 0)
            {
                serviceResponse.Success = false;
                serviceResponse.Messages = userValidationMessages;
                return serviceResponse;
            }

            string passwordHash
                = BCrypt.Net.BCrypt.HashPassword(request.Password);
            User userToReturn = new()
            {
                Email = request.Email,
                PasswordHash = passwordHash
            };

            Researcher reasercherToAdd = new()
            {
                User = userToReturn,
            };

            _db.Researchers.Add(reasercherToAdd);

            userToReturn.Researcher = reasercherToAdd;
            _db.Users.Add(userToReturn);
            bool userAdded = await _db.SaveChangesAsync() > 0;

            if (!userAdded)
            {
                serviceResponse.Success = false;
                serviceResponse.Messages = new List<string>() { userAdded.ToString() };
                return serviceResponse;
            }

            serviceResponse.Success = true;
            serviceResponse.Messages = new List<string>() { "Registration successful" };
       
            return serviceResponse;
        }
t3psigkw

t3psigkw1#

TLDR:切换到 * 一对多 * 即可。如果您必须询问有关 * 一对一 * 的问题,则可能是由于缺乏经验而在第一次使用时犯了错误。

我的直觉告诉我,你的case不是1:1关系,User将永远是父实体。所以只要把它设为1:* 就可以了。我猜你要么想避免每个User有多个Researcher,这可以通过 unique FK 来实现,要么只是想方便的双向导航属性,这可以通过多种方式来实现(例如,NotMapped ICollection.FirstOrDefault)。
所以正确的方法是摆脱一对一的关系,因为它在SQL级别本身就很糟糕(或者更确切地说,它并不存在)。ORM的使用往往会让它变得麻烦,因为使用它们比使用原始SQL时更糟糕。在EF中,它通常会以有趣的方式搞砸实体跟踪和自动关系。如果你想避免酗酒,就不要这样做。
你会遇到的额外问题是两个实体之间的循环引用。虽然你可以强制EF使用它,但你以后会遇到各种“有趣”的问题(例如,序列化导致堆栈溢出或OOM)。哦,从DB中删除它们是非常非常非常有趣的。
但是为了直接回答你的问题,在原始SQL中做这件事的方法和在任何ORM +/-一些自动化中是一样的。
1.创建两个实体。
1.插入到数据库(EF中的SaveChanges
1.获取 * 插入的ID*(默认情况下,EF会自动执行此操作)
1.更新外键(通过FK,而不是Navigation Property,否则EF会给予你带来麻烦)
因为EF在单次传递中完成所有操作,所以您只能自动获得两个ID中的一个。
要在单个操作中完成它,您需要在应用程序端手动生成PK,而不是依赖于SQL sequence(这就是auto increment),这通常是非常糟糕的想法,或者完全跳过ORM并在SQL过程中完成所有操作。
在这一点上,人们通常会意识到,1:1的关系首先是一个坏主意。
哦,请记住,您的设置似乎没有配置为懒惰加载。如果您试图滥用这个有问题的功能,这可能会咬到您的屁股。

2ledvvac

2ledvvac2#

1.在Program.cs中使用AddJsonOptions。这可以防止在第2点中导致的无限循环,其中Researcher的导航属性指向User,因此指向其自身:

builder.Services.AddControllers().AddJsonOptions(options =>
{
    options.JsonSerializerOptions.ReferenceHandler = ReferenceHandler.IgnoreCycles;
});

1.在API调用中使用.Include

_db.Users.Include("Researcher").ToListAsync();

1.不知道这是否是必需的,但我也添加了virtual关键字到我的模型中。下面是用户模型的示例:
User.cs

public class User
    {
        public Guid UserId { get; set; } = Guid.NewGuid();
        public string Email { get; set; } = string.Empty;
        public string PasswordHash { get; set; } = string.Empty;
        public virtual Researcher? Researcher { get; set; }
    }

相关问题