Я использую архитектуру DDD, и следующий код изменяет корневой элемент агрегата (по сути, добавляет комментарий, который является объектом в корень агрегата).
public sealed class AddCommentToPostCommandHandlers : ICommandHandler
{
private readonly IPostCommandRepository _postRepository;
private readonly ILogger _logger;
public AddCommentToPostCommandHandlers(IPostCommandRepository postRepository, ILogger logger)
{
_postRepository = postRepository;
_logger = logger;
}
public async Task Handle(AddCommentToPostCommand request, CancellationToken cancellationToken)
{
var post = await _postRepository.GetGraphByAsync(request.PostId, cancellationToken);
if (post is not null)
{
post.AddComment(request.DisplayName, request.Email, request.CommentText);
if (post.Result.IsSuccess)
{
_postRepository.UpdateBy(post);
await _postRepository.CommitAsync(cancellationToken);
return post.Id;
}
return post.Result;
}
return Result.Fail(ErrorMessages.NotFound(request.ToString()));
}
}
Этот код отлично работал с EF Core 8, но при обновлении до EF Core 9 я получаю следующую ошибку:
Microsoft.EntityFrameworkCore.DbUpdateConcurrencyException: ожидалось, что
операция с базой данных повлияет на 1 строку(и), но на самом деле
затронула 0 строк; данные могли быть изменены или удалены после загрузки
объектов.
Эта ошибка также возникает при редактировании комментария, тогда как в предыдущей версии ( EF Core 8) эта ошибка не произошла.
Агрегированный корневой код:
namespace ContentService.Core.Domain.Aggregates.Posts;
public class Post : AggregateRoot
{
public Title Title { get; private set; }
public Description Description { get; private set; }
public Text Text { get; private set; }
private readonly List _categoryIds;
public virtual IReadOnlyList CategoryIds => _categoryIds;
#region بارگذاری تنبل در سطح دامنه
private List _comments;
public virtual IReadOnlyList Comments
{
get
{
if (_comments == null)
{
LoadComments();
}
return _comments.AsReadOnly();
}
}
private void LoadComments()
{
// Load comments from the data source here.
// This is just a placeholder. You will need to replace this with your actual data loading logic.
_comments = new List();
}
#endregion End بارگذاری تنبل در سطح دامنه
public Post()
{
_categoryIds = new List();
}
private Post(string? title, string? description, string? text) : this()
{
var titleResult = Title.Create(title);
Result.WithErrors(titleResult.Errors);
var descriptionResult = Description.Create(description);
Result.WithErrors(descriptionResult.Errors);
var contentResult = Text.Create(text);
Result.WithErrors(contentResult.Errors);
if (Result.IsSuccess)
{
Title = titleResult.Value;
Description = descriptionResult.Value;
Text = contentResult.Value;
}
}
public Post Create(string? title, string? description, string? text)
{
var checkValidations = new Post(title, description, text);
Result.WithErrors(checkValidations.Result.Errors);
if (Result.IsFailed)
return this;
if (Result.IsSuccess)
{
this.Text = checkValidations.Text;
this.Title = checkValidations.Title;
this.Description = checkValidations.Description;
RaiseDomainEvent(new PostCreatedEvent(Id, this.Title.Value, this.Description.Value, this.Text.Value));
}
return this;
}
public Post UpdatePost(string? title, string? description, string? text)
{
var checkValidations = new Post(title, description, text);
Result.WithErrors(checkValidations.Result.Errors);
if (Result.IsFailed)
return this;
if (Result.IsSuccess)
{
this.Title = checkValidations.Title;
this.Description = checkValidations.Description;
this.Text = checkValidations.Text;
RaiseDomainEvent(new PostUpdatedEvent(Id, Title.Value!, Description.Value!, Text.Value!));
Result.WithSuccess(SuccessMessages.SuccessUpdate(DataDictionary.Post));
}
return this;
}
public Post RemovePost(Guid? id)
{
var guidResult = GuidId.Create(id);
if (guidResult.IsFailed)
{
Result.WithErrors(guidResult.Errors);
return this;
}
// Note: if have IsDeleted property (soft delete) we can change to true here
RaiseDomainEvent(new PostRemovedEvent(id));
Result.WithSuccess(SuccessMessages.SuccessDelete(DataDictionary.Post));
return this;
}
#region Category
public Post AddCategory(Guid? categoryId)
{
var guidResult = GuidId.Create(categoryId);
if (guidResult.IsFailed)
{
Result.WithErrors(guidResult.Errors);
return this;
}
if (!_categoryIds.Contains(guidResult.Value)) //جلوگیری از تکراری بودن دسته بندی
{
_categoryIds.Add(guidResult.Value);
RaiseDomainEvent(new PostCategoryAddedEvent(Id, (Guid)categoryId!));
}
return this;
}
public Post ChangeCategory(Guid? oldCategoryId, Guid? newCategoryId)
{
var oldGuidResult = GuidId.Create(oldCategoryId);
var newGuidResult = GuidId.Create(newCategoryId);
if (oldGuidResult.IsFailed)
{
Result.WithErrors(oldGuidResult.Errors);
return this;
}
if (newGuidResult.IsFailed)
{
Result.WithErrors(newGuidResult.Errors);
return this;
}
if (_categoryIds.Contains(oldGuidResult.Value))
{
var indexOldCategory = _categoryIds.IndexOf(oldGuidResult.Value);
if (!_categoryIds.Contains(newGuidResult.Value))
{
_categoryIds.RemoveAt(indexOldCategory);
_categoryIds.Insert(indexOldCategory, newGuidResult.Value);
}
else
{
_categoryIds.RemoveAt(indexOldCategory);
}
RaiseDomainEvent(new CategoryPostChangedEvent(Id, (Guid)oldCategoryId!, (Guid)newCategoryId!));
}
else
{
Result.WithError(ErrorMessages.NotFound(DataDictionary.Category));
}
return this;
}
public Post RemoveCategory(Guid? categoryId)
{
var guidResult = GuidId.Create(categoryId);
if (guidResult.IsFailed)
{
Result.WithErrors(guidResult.Errors);
return this;
}
if (_categoryIds.Contains(guidResult.Value))
{
_categoryIds.Remove(guidResult.Value);
RaiseDomainEvent(new CategoryPostRemovedEvent(Id, (Guid)categoryId!));
}
return this;
}
#endregion End Category
#region Comments
public Post AddComment(string? name, string? email, string? text)
{
var commentResult = Comment.Create(this, name, email, text);
Result.WithErrors(commentResult.Errors);
if (Result.IsFailed)
{
return this;
}
var hasAny = Comments
.Any(c => c.Name == commentResult.Value.Name
&& c.Email == commentResult.Value.Email
&& c.CommentText == commentResult.Value.CommentText);
if (hasAny)
{
var errorMessage = ValidationMessages.Repetitive(DataDictionary.Comment);
Result.WithError(errorMessage);
return this;
}
_comments.Add(commentResult.Value);
RaiseDomainEvent(new CommentAddedEvent(this.Id, commentResult.Value.Id, commentResult.Value.Name.Value, commentResult.Value.Email.Value, commentResult.Value.CommentText.Value));
return this;
}
public Post ChangeCommentText(string? name, string? email, string? text, string? newText)
{
var commentOldResult = Comment.Create(this, name, email, text);
var commentNewResult = Comment.Create(this, name, email, newText);
Result.WithErrors(commentOldResult.Errors);
Result.WithErrors(commentNewResult.Errors);
var emailGuardResult = Guard.CheckIf(commentNewResult.Value.Email, DataDictionary.Email)
.Equal(commentOldResult.Value.Email);
Result.WithErrors(emailGuardResult.Errors);
var nameGuardResult = Guard.CheckIf(commentNewResult.Value.Name, DataDictionary.Name)
.Equal(commentOldResult.Value.Name);
Result.WithErrors(nameGuardResult.Errors);
var commentTextGuardResult = Guard.CheckIf(commentNewResult.Value.CommentText, DataDictionary.CommentText)
.NotEqual(commentOldResult.Value.CommentText);
Result.WithErrors(commentTextGuardResult.Errors);
if (Result.IsFailed)
{
return this;
}
LoadComments();
var hasAny = Comments
.Any(c => c.Name == commentNewResult.Value.Name
&& c.Email == commentNewResult.Value.Email
&& c.CommentText == commentNewResult.Value.CommentText);
if (hasAny)
{
var errorMessage = ValidationMessages.Repetitive(DataDictionary.Comment);
Result.WithError(errorMessage);
return this;
}
//var commentIndex = _comments
// .FindIndex(c => c.Name == commentOldResult.Value.Name
// && c.Email == commentOldResult.Value.Email
// && c.CommentText == commentOldResult.Value.CommentText);
var commentIndex = Comments
.Select((c, i) => new { Comment = c, Index = i })
.FirstOrDefault(x => x.Comment.Name == commentOldResult.Value.Name
&& x.Comment.Email == commentOldResult.Value.Email
&& x.Comment.CommentText == commentOldResult.Value.CommentText)?.Index;
if (commentIndex >= 0)
{
_comments.RemoveAt((int)commentIndex);
_comments.Insert((int)commentIndex, commentNewResult.Value);
RaiseDomainEvent(new CommentEditedEvent(this.Id, commentNewResult.Value.Id, commentNewResult.Value.Name.Value, commentNewResult.Value.Email.Value, commentNewResult.Value.CommentText.Value));
}
return this;
}
public Post RemoveComment(string? name, string? email, string? text)
{
var commentResult = Comment.Create(this, name, email, text);
Result.WithErrors(commentResult.Errors);
if (Result.IsFailed)
{
return this;
}
var commentFounded = Comments
.FirstOrDefault(c => c.Name?.Value?.ToLower() == commentResult.Value.Name?.Value?.ToLower()
&& c.Email?.Value?.ToLower() == commentResult.Value?.Email?.Value?.ToLower()
&& c.CommentText.Value?.ToLower() == commentResult?.Value?.CommentText.Value?.ToLower());
if (commentFounded is null)
{
var errorMessage = ErrorMessages.NotFound(DataDictionary.Comment);
Result.WithError(errorMessage);
return this;
}
_comments.Remove(commentFounded);
Result.WithSuccess(SuccessMessages.SuccessDelete(DataDictionary.Comment));
RaiseDomainEvent(new CommentRemovedEvent(Id, name, email, text));
return this;
}
#endregion
}
and comment entity is:
namespace ContentService.Core.Domain.Aggregates.Posts.Entities;
public class Comment : Entity
{
public DisplayName Name { get; private set; }
public Email Email { get; private set; }
public CommentText CommentText { get; private set; }
public Guid PostId { get; private set; }
private Comment()
{
}
private Comment(Guid postId, DisplayName name, Email email, CommentText text) : this()
{
PostId = postId;
Name = name;
Email = email;
CommentText = text;
}
public static Result Create(Guid? postId, string? name, string? email, string? text)
{
Result result = new();
if (!postId.HasValue || postId == Guid.Empty)
{
var errorMessage = ValidationMessages.Required(DataDictionary.Post);
result.WithError(errorMessage);
}
var displayNameResult = DisplayName.Create(name);
result.WithErrors(displayNameResult.Errors);
var emailResult = Email.Create(email);
result.WithErrors(emailResult.Errors);
var textResult = CommentText.Create(text);
result.WithErrors(textResult.Errors);
if (result.IsFailed)
{
return result;
}
var returnValue = new Comment((Guid)postId!, displayNameResult.Value, emailResult.Value, textResult.Value);
result.WithValue(returnValue);
return result;
}
}
и конфигурация ef:
internal sealed class PostConfiguration : IEntityTypeConfiguration
{
public void Configure(EntityTypeBuilder builder)
{
builder.Property(p => p.CategoryIds)
.HasConversion(
v => string.Join(',', v.Select(c => c.Value)),
v => v.Split(',', StringSplitOptions.RemoveEmptyEntries).Select(c => GuidId.Create(c).Value).ToList()
);
builder.Property(p => p.Title)
.IsRequired(true)
.HasMaxLength(Title.Maximum)
.HasConversion(p => p.Value, p => Title.Create(p).Value);
builder.Property(p => p.Description)
.IsRequired(true)
.HasMaxLength(Description.Maximum)
.HasConversion(d => d.Value, d => Description.Create(d).Value);
builder.Property(p => p.Text)
.IsRequired(true)
.HasConversion(t => t.Value, t => Text.Create(t).Value);
builder.OwnsMany(c => c.Comments, cc =>
{
cc.ToTable("Comments");
cc.Property(c => c.Email)
.IsRequired(true)
.HasConversion(e => e.Value, e => Email.Create(e).Value);
cc.Property(c => c.Name)
.IsRequired(true)
.HasMaxLength(DisplayName.Maximum)
.HasConversion(e => e.Value, e => DisplayName.Create(e).Value);
cc.Property(c => c.CommentText)
.IsRequired(true)
.HasMaxLength(CommentText.Maximum)
.HasConversion(e => e.Value, e => CommentText.Create(e).Value);
});
}
}
Подробнее здесь: https://stackoverflow.com/questions/792 ... t-actually
EF Core 9: ожидалось, что операция базы данных повлияет на 1 строку (строки), но фактически затронула 0 строк. ⇐ C#
Место общения программистов C#
1732451933
Anonymous
Я использую архитектуру DDD, и следующий код изменяет корневой элемент агрегата (по сути, добавляет комментарий, который является объектом в корень агрегата).
public sealed class AddCommentToPostCommandHandlers : ICommandHandler
{
private readonly IPostCommandRepository _postRepository;
private readonly ILogger _logger;
public AddCommentToPostCommandHandlers(IPostCommandRepository postRepository, ILogger logger)
{
_postRepository = postRepository;
_logger = logger;
}
public async Task Handle(AddCommentToPostCommand request, CancellationToken cancellationToken)
{
var post = await _postRepository.GetGraphByAsync(request.PostId, cancellationToken);
if (post is not null)
{
post.AddComment(request.DisplayName, request.Email, request.CommentText);
if (post.Result.IsSuccess)
{
_postRepository.UpdateBy(post);
await _postRepository.CommitAsync(cancellationToken);
return post.Id;
}
return post.Result;
}
return Result.Fail(ErrorMessages.NotFound(request.ToString()));
}
}
Этот код отлично работал с EF Core 8, но при обновлении до EF Core 9 я получаю следующую ошибку:
Microsoft.EntityFrameworkCore.DbUpdateConcurrencyException: ожидалось, что
операция с базой данных повлияет на 1 строку(и), но на самом деле
затронула 0 строк; данные могли быть изменены или удалены после загрузки
объектов.
Эта ошибка также возникает при редактировании комментария, тогда как в предыдущей версии ( EF Core 8) эта ошибка не произошла.
Агрегированный корневой код:
namespace ContentService.Core.Domain.Aggregates.Posts;
public class Post : AggregateRoot
{
public Title Title { get; private set; }
public Description Description { get; private set; }
public Text Text { get; private set; }
private readonly List _categoryIds;
public virtual IReadOnlyList CategoryIds => _categoryIds;
#region بارگذاری تنبل در سطح دامنه
private List _comments;
public virtual IReadOnlyList Comments
{
get
{
if (_comments == null)
{
LoadComments();
}
return _comments.AsReadOnly();
}
}
private void LoadComments()
{
// Load comments from the data source here.
// This is just a placeholder. You will need to replace this with your actual data loading logic.
_comments = new List();
}
#endregion End بارگذاری تنبل در سطح دامنه
public Post()
{
_categoryIds = new List();
}
private Post(string? title, string? description, string? text) : this()
{
var titleResult = Title.Create(title);
Result.WithErrors(titleResult.Errors);
var descriptionResult = Description.Create(description);
Result.WithErrors(descriptionResult.Errors);
var contentResult = Text.Create(text);
Result.WithErrors(contentResult.Errors);
if (Result.IsSuccess)
{
Title = titleResult.Value;
Description = descriptionResult.Value;
Text = contentResult.Value;
}
}
public Post Create(string? title, string? description, string? text)
{
var checkValidations = new Post(title, description, text);
Result.WithErrors(checkValidations.Result.Errors);
if (Result.IsFailed)
return this;
if (Result.IsSuccess)
{
this.Text = checkValidations.Text;
this.Title = checkValidations.Title;
this.Description = checkValidations.Description;
RaiseDomainEvent(new PostCreatedEvent(Id, this.Title.Value, this.Description.Value, this.Text.Value));
}
return this;
}
public Post UpdatePost(string? title, string? description, string? text)
{
var checkValidations = new Post(title, description, text);
Result.WithErrors(checkValidations.Result.Errors);
if (Result.IsFailed)
return this;
if (Result.IsSuccess)
{
this.Title = checkValidations.Title;
this.Description = checkValidations.Description;
this.Text = checkValidations.Text;
RaiseDomainEvent(new PostUpdatedEvent(Id, Title.Value!, Description.Value!, Text.Value!));
Result.WithSuccess(SuccessMessages.SuccessUpdate(DataDictionary.Post));
}
return this;
}
public Post RemovePost(Guid? id)
{
var guidResult = GuidId.Create(id);
if (guidResult.IsFailed)
{
Result.WithErrors(guidResult.Errors);
return this;
}
// Note: if have IsDeleted property (soft delete) we can change to true here
RaiseDomainEvent(new PostRemovedEvent(id));
Result.WithSuccess(SuccessMessages.SuccessDelete(DataDictionary.Post));
return this;
}
#region Category
public Post AddCategory(Guid? categoryId)
{
var guidResult = GuidId.Create(categoryId);
if (guidResult.IsFailed)
{
Result.WithErrors(guidResult.Errors);
return this;
}
if (!_categoryIds.Contains(guidResult.Value)) //جلوگیری از تکراری بودن دسته بندی
{
_categoryIds.Add(guidResult.Value);
RaiseDomainEvent(new PostCategoryAddedEvent(Id, (Guid)categoryId!));
}
return this;
}
public Post ChangeCategory(Guid? oldCategoryId, Guid? newCategoryId)
{
var oldGuidResult = GuidId.Create(oldCategoryId);
var newGuidResult = GuidId.Create(newCategoryId);
if (oldGuidResult.IsFailed)
{
Result.WithErrors(oldGuidResult.Errors);
return this;
}
if (newGuidResult.IsFailed)
{
Result.WithErrors(newGuidResult.Errors);
return this;
}
if (_categoryIds.Contains(oldGuidResult.Value))
{
var indexOldCategory = _categoryIds.IndexOf(oldGuidResult.Value);
if (!_categoryIds.Contains(newGuidResult.Value))
{
_categoryIds.RemoveAt(indexOldCategory);
_categoryIds.Insert(indexOldCategory, newGuidResult.Value);
}
else
{
_categoryIds.RemoveAt(indexOldCategory);
}
RaiseDomainEvent(new CategoryPostChangedEvent(Id, (Guid)oldCategoryId!, (Guid)newCategoryId!));
}
else
{
Result.WithError(ErrorMessages.NotFound(DataDictionary.Category));
}
return this;
}
public Post RemoveCategory(Guid? categoryId)
{
var guidResult = GuidId.Create(categoryId);
if (guidResult.IsFailed)
{
Result.WithErrors(guidResult.Errors);
return this;
}
if (_categoryIds.Contains(guidResult.Value))
{
_categoryIds.Remove(guidResult.Value);
RaiseDomainEvent(new CategoryPostRemovedEvent(Id, (Guid)categoryId!));
}
return this;
}
#endregion End Category
#region Comments
public Post AddComment(string? name, string? email, string? text)
{
var commentResult = Comment.Create(this, name, email, text);
Result.WithErrors(commentResult.Errors);
if (Result.IsFailed)
{
return this;
}
var hasAny = Comments
.Any(c => c.Name == commentResult.Value.Name
&& c.Email == commentResult.Value.Email
&& c.CommentText == commentResult.Value.CommentText);
if (hasAny)
{
var errorMessage = ValidationMessages.Repetitive(DataDictionary.Comment);
Result.WithError(errorMessage);
return this;
}
_comments.Add(commentResult.Value);
RaiseDomainEvent(new CommentAddedEvent(this.Id, commentResult.Value.Id, commentResult.Value.Name.Value, commentResult.Value.Email.Value, commentResult.Value.CommentText.Value));
return this;
}
public Post ChangeCommentText(string? name, string? email, string? text, string? newText)
{
var commentOldResult = Comment.Create(this, name, email, text);
var commentNewResult = Comment.Create(this, name, email, newText);
Result.WithErrors(commentOldResult.Errors);
Result.WithErrors(commentNewResult.Errors);
var emailGuardResult = Guard.CheckIf(commentNewResult.Value.Email, DataDictionary.Email)
.Equal(commentOldResult.Value.Email);
Result.WithErrors(emailGuardResult.Errors);
var nameGuardResult = Guard.CheckIf(commentNewResult.Value.Name, DataDictionary.Name)
.Equal(commentOldResult.Value.Name);
Result.WithErrors(nameGuardResult.Errors);
var commentTextGuardResult = Guard.CheckIf(commentNewResult.Value.CommentText, DataDictionary.CommentText)
.NotEqual(commentOldResult.Value.CommentText);
Result.WithErrors(commentTextGuardResult.Errors);
if (Result.IsFailed)
{
return this;
}
LoadComments();
var hasAny = Comments
.Any(c => c.Name == commentNewResult.Value.Name
&& c.Email == commentNewResult.Value.Email
&& c.CommentText == commentNewResult.Value.CommentText);
if (hasAny)
{
var errorMessage = ValidationMessages.Repetitive(DataDictionary.Comment);
Result.WithError(errorMessage);
return this;
}
//var commentIndex = _comments
// .FindIndex(c => c.Name == commentOldResult.Value.Name
// && c.Email == commentOldResult.Value.Email
// && c.CommentText == commentOldResult.Value.CommentText);
var commentIndex = Comments
.Select((c, i) => new { Comment = c, Index = i })
.FirstOrDefault(x => x.Comment.Name == commentOldResult.Value.Name
&& x.Comment.Email == commentOldResult.Value.Email
&& x.Comment.CommentText == commentOldResult.Value.CommentText)?.Index;
if (commentIndex >= 0)
{
_comments.RemoveAt((int)commentIndex);
_comments.Insert((int)commentIndex, commentNewResult.Value);
RaiseDomainEvent(new CommentEditedEvent(this.Id, commentNewResult.Value.Id, commentNewResult.Value.Name.Value, commentNewResult.Value.Email.Value, commentNewResult.Value.CommentText.Value));
}
return this;
}
public Post RemoveComment(string? name, string? email, string? text)
{
var commentResult = Comment.Create(this, name, email, text);
Result.WithErrors(commentResult.Errors);
if (Result.IsFailed)
{
return this;
}
var commentFounded = Comments
.FirstOrDefault(c => c.Name?.Value?.ToLower() == commentResult.Value.Name?.Value?.ToLower()
&& c.Email?.Value?.ToLower() == commentResult.Value?.Email?.Value?.ToLower()
&& c.CommentText.Value?.ToLower() == commentResult?.Value?.CommentText.Value?.ToLower());
if (commentFounded is null)
{
var errorMessage = ErrorMessages.NotFound(DataDictionary.Comment);
Result.WithError(errorMessage);
return this;
}
_comments.Remove(commentFounded);
Result.WithSuccess(SuccessMessages.SuccessDelete(DataDictionary.Comment));
RaiseDomainEvent(new CommentRemovedEvent(Id, name, email, text));
return this;
}
#endregion
}
and comment entity is:
namespace ContentService.Core.Domain.Aggregates.Posts.Entities;
public class Comment : Entity
{
public DisplayName Name { get; private set; }
public Email Email { get; private set; }
public CommentText CommentText { get; private set; }
public Guid PostId { get; private set; }
private Comment()
{
}
private Comment(Guid postId, DisplayName name, Email email, CommentText text) : this()
{
PostId = postId;
Name = name;
Email = email;
CommentText = text;
}
public static Result Create(Guid? postId, string? name, string? email, string? text)
{
Result result = new();
if (!postId.HasValue || postId == Guid.Empty)
{
var errorMessage = ValidationMessages.Required(DataDictionary.Post);
result.WithError(errorMessage);
}
var displayNameResult = DisplayName.Create(name);
result.WithErrors(displayNameResult.Errors);
var emailResult = Email.Create(email);
result.WithErrors(emailResult.Errors);
var textResult = CommentText.Create(text);
result.WithErrors(textResult.Errors);
if (result.IsFailed)
{
return result;
}
var returnValue = new Comment((Guid)postId!, displayNameResult.Value, emailResult.Value, textResult.Value);
result.WithValue(returnValue);
return result;
}
}
и конфигурация ef:
internal sealed class PostConfiguration : IEntityTypeConfiguration
{
public void Configure(EntityTypeBuilder builder)
{
builder.Property(p => p.CategoryIds)
.HasConversion(
v => string.Join(',', v.Select(c => c.Value)),
v => v.Split(',', StringSplitOptions.RemoveEmptyEntries).Select(c => GuidId.Create(c).Value).ToList()
);
builder.Property(p => p.Title)
.IsRequired(true)
.HasMaxLength(Title.Maximum)
.HasConversion(p => p.Value, p => Title.Create(p).Value);
builder.Property(p => p.Description)
.IsRequired(true)
.HasMaxLength(Description.Maximum)
.HasConversion(d => d.Value, d => Description.Create(d).Value);
builder.Property(p => p.Text)
.IsRequired(true)
.HasConversion(t => t.Value, t => Text.Create(t).Value);
builder.OwnsMany(c => c.Comments, cc =>
{
cc.ToTable("Comments");
cc.Property(c => c.Email)
.IsRequired(true)
.HasConversion(e => e.Value, e => Email.Create(e).Value);
cc.Property(c => c.Name)
.IsRequired(true)
.HasMaxLength(DisplayName.Maximum)
.HasConversion(e => e.Value, e => DisplayName.Create(e).Value);
cc.Property(c => c.CommentText)
.IsRequired(true)
.HasMaxLength(CommentText.Maximum)
.HasConversion(e => e.Value, e => CommentText.Create(e).Value);
});
}
}
Подробнее здесь: [url]https://stackoverflow.com/questions/79219671/ef-core-9-the-database-operation-was-expected-to-affect-1-rows-but-actually[/url]
Ответить
1 сообщение
• Страница 1 из 1
Перейти
- Кемерово-IT
- ↳ Javascript
- ↳ C#
- ↳ JAVA
- ↳ Elasticsearch aggregation
- ↳ Python
- ↳ Php
- ↳ Android
- ↳ Html
- ↳ Jquery
- ↳ C++
- ↳ IOS
- ↳ CSS
- ↳ Excel
- ↳ Linux
- ↳ Apache
- ↳ MySql
- Детский мир
- Для души
- ↳ Музыкальные инструменты даром
- ↳ Печатная продукция даром
- Внешняя красота и здоровье
- ↳ Одежда и обувь для взрослых даром
- ↳ Товары для здоровья
- ↳ Физкультура и спорт
- Техника - даром!
- ↳ Автомобилистам
- ↳ Компьютерная техника
- ↳ Плиты: газовые и электрические
- ↳ Холодильники
- ↳ Стиральные машины
- ↳ Телевизоры
- ↳ Телефоны, смартфоны, плашеты
- ↳ Швейные машинки
- ↳ Прочая электроника и техника
- ↳ Фототехника
- Ремонт и интерьер
- ↳ Стройматериалы, инструмент
- ↳ Мебель и предметы интерьера даром
- ↳ Cантехника
- Другие темы
- ↳ Разное даром
- ↳ Давай меняться!
- ↳ Отдам\возьму за копеечку
- ↳ Работа и подработка в Кемерове
- ↳ Давай с тобой поговорим...
Мобильная версия