diff --git a/src/OrchardCore/OrchardCore.ContentManagement.Abstractions/ContentExtensions.cs b/src/OrchardCore/OrchardCore.ContentManagement.Abstractions/ContentExtensions.cs index b10447ef7e3..15a6dc8c9b7 100644 --- a/src/OrchardCore/OrchardCore.ContentManagement.Abstractions/ContentExtensions.cs +++ b/src/OrchardCore/OrchardCore.ContentManagement.Abstractions/ContentExtensions.cs @@ -63,12 +63,14 @@ public static bool Has(this ContentElement contentElement) where TElem /// The content element instance or. null if it doesn't exist. public static ContentElement Get(this ContentElement contentElement, Type contentElementType, string name) { - if (contentElement.Elements.TryGetValue(name, out var element)) + if (contentElement.Elements.TryGetValue(name, out var element) && + element.GetType().IsAssignableTo(contentElementType)) { return element; } var elementData = contentElement.Data[name] as JsonObject; + if (elementData is null) { return null; diff --git a/test/OrchardCore.Tests/ContentManagement/ContentElementTests.cs b/test/OrchardCore.Tests/ContentManagement/ContentElementTests.cs new file mode 100644 index 00000000000..fc6d5683e6d --- /dev/null +++ b/test/OrchardCore.Tests/ContentManagement/ContentElementTests.cs @@ -0,0 +1,69 @@ +using System.Text.Json; +using OrchardCore.ContentManagement; +using OrchardCore.Title.Models; + +namespace OrchardCore.Tests.ContentManagement; +public class ContentElementTests +{ + [Fact] + public void Get_WhenCastingBaseTypeThenConcreteType_ReturnNewInstance() + { + var contentItem = new ContentItem(); + var titlePart = new TitlePart + { + Title = "test" + }; + + contentItem.Weld(titlePart); + + var json = JConvert.SerializeObject(contentItem); + + var contentItem2 = JConvert.DeserializeObject(json); + + // act + // The order arrangement of the next two calls are important. + // First cast to ContentPart. + var contentPart = contentItem2.Get(nameof(TitlePart)); + + // Second, cast to TitlePart. + var actualPart = contentItem2.Get(nameof(TitlePart)); + + // assert + Assert.NotNull(contentPart); + Assert.NotNull(actualPart); + + // actualPart should deserialized again, so it must not be same as contentPart. + Assert.NotSame(contentPart, actualPart); + } + + [Fact] + public void Get_WhenCastingConcreteTypeThenBaseType_ReturnNewInstance() + { + var contentItem = new ContentItem(); + var titlePart = new TitlePart + { + Title = "test" + }; + + contentItem.Weld(titlePart); + + var json = JConvert.SerializeObject(contentItem); + + var contentItem2 = JConvert.DeserializeObject(json); + + // act + // The order arrangement of the next two calls are important. + // First cast to TitlePart. + var actualPart = contentItem2.Get(nameof(TitlePart)); + + // Second, cast to ContentPart. + var contentPart = contentItem2.Get(nameof(TitlePart)); + + // assert + Assert.NotNull(contentPart); + Assert.NotNull(actualPart); + + // contentPart should be returned from cache, so it must be same as actualPart. + Assert.Same(contentPart, actualPart); + } +}