英文:
I can't get Tweet Replies from database on my .NET 7.0 Web API
问题
我正在制作一个Twitter克隆API。当我尝试通过ID获取带有回复的推文时,即使有回复,它也返回null。
Tweet.cs
using TwitterCloneApp.Core.Interfaces;
namespace TwitterCloneApp.Core.Models
{
public class Tweet : IBaseEntity, IDeletable, IUpdatedAt, ICreatedAt
{
public int Id { get; set; }
public int UserId { get; set; }
public string Content { get; set; }
public User User { get; set; }
public bool isMainTweet { get; set; }
public bool IsDeleted { get; set; } = false;
public DateTime? DeletedAt { get; set; }
public DateTime? UpdatedAt { get; set; }
public DateTime? CreatedAt { get; set; }
public ICollection<Tag>? Tags { get; set; } // Many-to-Many ilişkisi için koleksiyon
public ICollection<Tweet>? Replies { get; set; }
public ICollection<Like>? Likes { get; set; }
}
}
TwReply.Cs(用于关系)
namespace TwitterCloneApp.Core.Models
{
public class TwReply
{
public int TweetId { get; set; }
public int ReplyId { get; set; }
public Tweet Tweet { get; set; }
public Tweet Reply { get; set; }
}
}
TweetDto.cs
using TwitterCloneApp.DTO.Tag;
using TwitterCloneApp.DTO.User;
namespace TwitterCloneApp.DTO.Tweet
{
public class TweetDto
{
public int Id { get; set; }
public UserResponseDto User { get; set; }
public string? Content { get; set; }
public DateTime? CreatedAt { get; set; }
public int LikeCount { get; set; }
public List<TagResponseDto>? Tags { get; set; }
public List<ReplyResponseDto>? Replies { get; set; }
}
}
ReplyResponseDto.cs
using TwitterCloneApp.DTO.User;
namespace TwitterCloneApp.DTO.Tweet
{
public class ReplyResponseDto
{
public UserResponseDto User { get; set; }
public string Content { get; set; }
public DateTime? CreatedAt { get; set; }
public int LikeCount { get; set; }
}
}
UserResponseDto.cs
namespace TwitterCloneApp.DTO.User
{
public class UserResponseDto
{
public string UserName { get; set; }
public string DisplayName { get; set; }
public string? ProfileImg { get; set; }
}
}
TweetController部分
[HttpGet("[action]")]
public async Task<IActionResult> GetTweetByIdAsync(int id)
{
var tweet = await _tweetService.GetTweetByIdAsync(id);
return Ok(tweet);
}
TweetService部分
public async Task<TweetDto> GetTweetByIdAsync(int tweetId)
{
return await _tweetRepository.GetTweetWithTagsByIdAsync(tweetId);
}
TweetRepository部分
public async Task<TweetDto> GetTweetWithTagsByIdAsync(int tweetId)
{
var tweet = await _tweet.Include(t => t.Tags)
.Include(t => t.Replies).ThenInclude(reply => reply.User)
.Include(t => t.User)
.Include(t => t.Likes)
.FirstOrDefaultAsync(t => t.Id == tweetId);
if (tweet == null)
{
return null; // 或者执行必要的操作
}
var tweetDto = new TweetDto
{
Id = tweet.Id,
Content = tweet.Content,
CreatedAt = tweet.CreatedAt,
LikeCount = tweet.Likes?.Count ?? 0,
Tags = tweet.Tags?.Select(t => new TagResponseDto
{
Name = t.Name
}).ToList(),
User = new UserResponseDto
{
UserName = tweet.User.UserName,
DisplayName = tweet.User.DisplayName,
ProfileImg = tweet.User.ProfileImg
},
Replies = tweet.Replies?.Select(reply => new ReplyResponseDto
{
User = new UserResponseDto
{
UserName = reply.User.UserName,
DisplayName = reply.User.DisplayName,
ProfileImg = reply.User.ProfileImg
},
Content = reply.Content,
CreatedAt = reply.CreatedAt,
LikeCount = reply.Likes?.Count ?? 0
}).ToList()
};
return tweetDto;
}
AppDbContext.cs
using Microsoft.EntityFrameworkCore;
using System.Reflection;
using TwitterCloneApp.Core.Models;
namespace TwitterCloneApp.Repository
{
public class AppDbContext : DbContext
{
public AppDbContext(DbContextOptions<AppDbContext> options) : base(options)
{
}
public DbSet<User> Users { get; set; }
public DbSet<Tweet> Tweets { get; set; }
public DbSet<Tag> Tags { get; set; }
public DbSet<Like> Likes { get; set; }
public DbSet<TwReply> TwReplies { get; set; }
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.ApplyConfigurationsFromAssembly(Assembly.GetExecutingAssembly());
var relationships = modelBuilder.Model.GetEntityTypes()
.SelectMany(e => e.GetForeignKeys());
foreach (var relationship in relationships)
{
relationship.DeleteBehavior = DeleteBehavior.Restrict;
}
base.OnModelCreating(modelBuilder);
}
}
}
推文ID为1有2个回复(3和10),但输出为:
{
"id": 1,
"user": {
"userName": "asdasdasd",
"displayName": "asdasdasd",
"profileImg": "string"
},
"content": "First tweet",
"createdAt": null,
"likeCount": 3,
"tags": [
{
"name": "#testingSeed1"
},
{
"name": "#testingSeed2"
},
{
"name": "#testing"
}
],
"replies": []
}
我尝试在获取推文时获取回复,但程序不会获取回复。我使用的是Entity Framework Core。
英文:
I am making a Twitter Clone API. When I try to get a Tweet by Id with its replies, it returns null even if there is a reply.
Tweet.cs
using TwitterCloneApp.Core.Interfaces;
namespace TwitterCloneApp.Core.Models
{
public class Tweet : IBaseEntity, IDeletable, IUpdatedAt, ICreatedAt
{
public int Id { get; set; }
public int UserId { get; set; }
public string Content { get; set; }
public User User { get; set; }
public bool isMainTweet { get; set; }
public bool IsDeleted { get; set; } = false;
public DateTime? DeletedAt { get; set; }
public DateTime? UpdatedAt { get; set; }
public DateTime? CreatedAt { get; set; }
public ICollection<Tag>? Tags { get; set; } // Many-to-Many ilişkisi için koleksiyon
public ICollection<Tweet>? Replies { get; set; }
public ICollection<Like>? Likes { get; set; }
}
}
TwReply.Cs (for relation)
namespace TwitterCloneApp.Core.Models
{
public class TwReply
{
public int TweetId { get; set; }
public int ReplyId { get; set; }
public Tweet Tweet { get; set; }
public Tweet Reply { get; set; }
}
}
TweetDto.cs
using TwitterCloneApp.DTO.Tag;
using TwitterCloneApp.DTO.User;
namespace TwitterCloneApp.DTO.Tweet
{
public class TweetDto
{
public int Id { get; set; }
public UserResponseDto User { get; set; }
public string? Content { get; set; }
public DateTime? CreatedAt { get; set; }
public int LikeCount { get; set; }
public List<TagResponseDto>? Tags { get; set; }
public List<ReplyResponseDto>? Replies { get; set; }
}
}
ReplyResponseDto.cs
using TwitterCloneApp.DTO.User;
namespace TwitterCloneApp.DTO.Tweet
{
public class ReplyResponseDto
{
public UserResponseDto User { get; set; }
public string Content { get; set; }
public DateTime? CreatedAt { get; set; }
public int LikeCount { get; set; }
}
}
UserResponseDto.cs
namespace TwitterCloneApp.DTO.User
{
public class UserResponseDto
{
public string UserName { get; set; }
public string DisplayName { get; set; }
public string? ProfileImg { get; set; }
}
}
TweetController part
[HttpGet("[action]")]
public async Task<IActionResult> GetTweetByIdAsync(int id)
{
var tweet = await _tweetService.GetTweetByIdAsync(id);
return Ok(tweet);
}
TweetService part
public async Task<TweetDto> GetTweetByIdAsync(int tweetId)
{
return await _tweetRepository.GetTweetWithTagsByIdAsync(tweetId);
}
TweetRepository part
public async Task<TweetDto> GetTweetWithTagsByIdAsync(int tweetId)
{
var tweet = await _tweet.Include(t => t.Tags)
.Include(t => t.Replies).ThenInclude(reply => reply.User)
.Include(t => t.User)
.Include(t => t.Likes)
.FirstOrDefaultAsync(t => t.Id == tweetId);
if (tweet == null)
{
return null; // veya gerekli işlemleri yapın
}
var tweetDto = new TweetDto
{
Id = tweet.Id,
Content = tweet.Content,
CreatedAt = tweet.CreatedAt,
LikeCount = tweet.Likes?.Count ?? 0,
Tags = tweet.Tags?.Select(t => new TagResponseDto
{
Name = t.Name
}).ToList(),
User = new UserResponseDto
{
UserName = tweet.User.UserName,
DisplayName = tweet.User.DisplayName,
ProfileImg = tweet.User.ProfileImg
},
Replies = tweet.Replies?.Select(reply => new ReplyResponseDto
{
User = new UserResponseDto
{
UserName = reply.User.UserName,
DisplayName = reply.User.DisplayName,
ProfileImg = reply.User.ProfileImg
},
Content = reply.Content,
CreatedAt = reply.CreatedAt,
LikeCount = reply.Likes?.Count ?? 0
}).ToList()
};
return tweetDto;
}
AppDbContext.cs
using Microsoft.EntityFrameworkCore;
using System.Reflection;
using TwitterCloneApp.Core.Models;
namespace TwitterCloneApp.Repository
{
public class AppDbContext : DbContext
{
public AppDbContext(DbContextOptions<AppDbContext> options) : base(options)
{
}
public DbSet<User> Users { get; set; }
public DbSet<Tweet> Tweets { get; set; }
public DbSet<Tag> Tags { get; set; }
public DbSet<Like> Likes { get; set; }
public DbSet<TwReply> TwReplies { get; set; }
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.ApplyConfigurationsFromAssembly(Assembly.GetExecutingAssembly());
var relationships = modelBuilder.Model.GetEntityTypes()
.SelectMany(e => e.GetForeignKeys());
foreach (var relationship in relationships)
{
relationship.DeleteBehavior = DeleteBehavior.Restrict;
}
base.OnModelCreating(modelBuilder);
}
}
}
There are 2 replies (3 and 10) for Tweet Id:1
but output is:
{
"id": 1,
"user": {
"userName": "asdasdasd",
"displayName": "asdasdasd",
"profileImg": "string"
},
"content": "First tweet",
"createdAt": null,
"likeCount": 3,
"tags": [
{
"name": "#testingSeed1"
},
{
"name": "#testingSeed2"
},
{
"name": "#testing"
}
],
"replies": []
}
I tried to get replies while getting a Tweet by Id but the program doesn't get Replies. I checked how does it work with breakpoints and it seems problem is in TweetRepository
part. It returns nothing for Replies when it gets entity. I use Entity Framework Core BTW.
答案1
得分: 1
实体Tweet
(简化版)如下所示:
public class Tweet
{
public int Id { get; set; }
public string Content { get; set; } = "";
public ICollection<Tweet>? Replies { get; set; }
}
然后,EF Core 将把这个模型映射到以下数据库模式:
CREATE TABLE [Tweets] (
[Id] int NOT NULL,
[Content] nvarchar(max) NOT NULL,
[TweetId] int NULL,
CONSTRAINT [PK_Tweets] PRIMARY KEY ([Id]),
CONSTRAINT [FK_Tweets_Tweets_TweetId] FOREIGN KEY ([TweetId]) REFERENCES [Tweets] ([Id])
);
当没有指定数据库模式时,EF Core 使用约定来确定数据库模式。对于指向同一实体的集合导航属性,约定是一对多关系(一个推文可以有多个回复,但只能回复一个推文)。就像模型是这样的:
public class Tweet
{
public int Id { get; set; }
public string Content { get; set; } = "";
public ICollection<Tweet>? Replies { get; set; }
public Tweet ReplyTo { get; set; }
}
您可以参考文档 EF Core:关系发现的约定 了解更多关于关系的约定信息。
在您的情况下,这是一个多对多关系(一个推文可以有多个回复,也可以回复多个推文),通过中间实体 TwReply
。
按照约定,模型将如下所示:
public class Tweet
{
public int Id { get; set; }
public string Content { get; set; } = "";
public ICollection<TwReply>? Replies { get; set; }
}
public class TwReply
{
public int Id { get; set; }
public Tweet Tweet { get; set; }
public Tweet Reply { get; set; }
}
如果您想保留您的模型,您需要手动指定关系,如下所示:
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
...
modelBuilder.Entity<Tweet>()
.HasMany(t => t.Replies)
.WithMany()
.UsingEntity<TwReply>(
i => i.HasOne(tr => tr.Reply).WithMany(),
i => i.HasOne(tr => tr.Tweet).WithMany()
);
}
英文:
The entity Tweet
(simplified) is :
public class Tweet
{
public int Id { get; set; }
public string Content { get; set; } = "";
public ICollection<Tweet>? Replies { get; set; }
}
Then EF Core will tanlaste this model to this DB Schema :
CREATE TABLE [Tweets] (
[Id] int NOT NULL,
[Content] nvarchar(max) NOT NULL,
[TweetId] int NULL,
CONSTRAINT [PK_Tweets] PRIMARY KEY ([Id]),
CONSTRAINT [FK_Tweets_Tweets_TweetId] FOREIGN KEY ([TweetId]) REFERENCES [Tweets] ([Id])
);
When the DB schema isn't specified, EF Core use convention to determine the DB schema. And the convention for a collection navigation property to the same entity is 1 to n (a tweet can have manyreplies, but can reply to only one tweet). It's like the model was :
public class Tweet
{
public int Id { get; set; }
public string Content { get; set; } = "";
public ICollection<Tweet>? Replies { get; set; }
public Tweet ReplyTo { get; set; }
}
You can refer to the documentatino EF Core : Conventions for relationship discovery for more information about the convention about relationship.
In you case, it's a relation n to n (a tweet can have many reply and can reply to many tweet) by the intermediate entity TwReply.
By convention, the model will be :
public class Tweet
{
public int Id { get; set; }
public string Content { get; set; } = "";
public ICollection<TwReply>? Replies { get; set; }
}
public class TwReply
{
public int Id { get; set; }
public Tweet Tweet { get; set; }
public Tweet Reply { get; set; }
}
If you want keep your model, you need to specify manually the relation like :
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
...
modelBuilder.Entity<Tweet>()
.HasMany(t => t.Replies)
.WithMany()
.UsingEntity<TwReply>(
i => i.HasOne(tr => tr.Reply).WithMany(),
i => i.HasOne(tr => tr.Tweet).WithMany()
);
}
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论