Updates to null reference types (#12317)

* Amended IFileSystem OpenFile to return Stream.Null rather than a nullable stream.

* Updated IUmbracoMapper to map enumerables without null elements given non-null inputs.

* Amended EntityRepositoryBase to not return null collections.

* Made IPublishedElement.Properties a non-nullable collection.
This commit is contained in:
Andy Butland
2022-04-29 08:19:34 +02:00
committed by GitHub
parent 5e2f26ac9b
commit b187c89113
25 changed files with 99 additions and 101 deletions

View File

@@ -1,4 +1,4 @@
using System;
using System;
using System.Collections.Generic;
using Umbraco.Cms.Core.Models.Entities;
@@ -66,7 +66,7 @@ namespace Umbraco.Cms.Core.Cache
/// <param name="performGetAll">The repository PerformGetAll method.</param>
/// <returns>If <paramref name="ids"/> is empty, all entities, else the entities with the specified identifiers.</returns>
/// <remarks>Get all the entities. Either from the cache or the repository depending on the implementation.</remarks>
TEntity[]? GetAll(TId[]? ids, Func<TId[]?, IEnumerable<TEntity>?> performGetAll);
TEntity[] GetAll(TId[]? ids, Func<TId[]?, IEnumerable<TEntity>> performGetAll);
/// <summary>
/// Clears the entire cache.

View File

@@ -62,13 +62,9 @@ namespace Umbraco.Extensions
public static void CopyFile(this IFileSystem fs, string path, string newPath)
{
using (Stream? stream = fs.OpenFile(path))
using (Stream stream = fs.OpenFile(path))
{
if (stream is not null)
{
fs.AddFile(newPath, stream);
}
fs.AddFile(newPath, stream);
}
}

View File

@@ -1,4 +1,4 @@
using System;
using System;
using System.Collections.Generic;
using System.IO;
@@ -82,7 +82,7 @@ namespace Umbraco.Cms.Core.IO
/// <returns>
/// <see cref="Stream"/>.
/// </returns>
Stream? OpenFile(string path);
Stream OpenFile(string path);
/// <summary>
/// Deletes the specified file.

View File

@@ -120,14 +120,14 @@ namespace Umbraco.Cms.Core.IO
#region Associated Media Files
/// <summary>
/// Returns a stream (file) for a content item or null if there is no file.
/// Returns a stream (file) for a content item (or a null stream if there is no file).
/// </summary>
/// <param name="content"></param>
/// <param name="mediaFilePath">The file path if a file was found</param>
/// <param name="propertyTypeAlias"></param>
/// <param name="variationContextAccessor"></param>
/// <returns></returns>
public Stream? GetFile(
public Stream GetFile(
IContentBase content,
out string? mediaFilePath,
string propertyTypeAlias = Constants.Conventions.Media.File,
@@ -142,17 +142,10 @@ namespace Umbraco.Cms.Core.IO
if (!content.TryGetMediaPath(propertyTypeAlias, _mediaUrlGenerators!, out mediaFilePath, culture, segment))
{
return null;
return Stream.Null;
}
Stream? stream = FileSystem.OpenFile(mediaFilePath!);
if (stream != null)
{
return stream;
}
mediaFilePath = null;
return null;
return FileSystem.OpenFile(mediaFilePath!);
}
/// <summary>

View File

@@ -1,4 +1,4 @@
using System;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
@@ -37,12 +37,9 @@ namespace Umbraco.Cms.Core.IO
}
else
{
using (Stream? stream = _sfs.OpenFile(kvp.Key))
using (Stream stream = _sfs.OpenFile(kvp.Key))
{
if (stream is not null)
{
_fs.AddFile(kvp.Key, stream, true);
}
_fs.AddFile(kvp.Key, stream, true);
}
}
}
@@ -227,11 +224,13 @@ namespace Umbraco.Cms.Core.IO
.Distinct();
}
public Stream? OpenFile(string path)
public Stream OpenFile(string path)
{
ShadowNode? sf;
if (Nodes.TryGetValue(NormPath(path), out sf))
return sf.IsDir || sf.IsDelete ? null : _sfs.OpenFile(path);
if (Nodes.TryGetValue(NormPath(path), out ShadowNode? sf))
{
return sf.IsDir || sf.IsDelete ? Stream.Null : _sfs.OpenFile(path);
}
return _fs.OpenFile(path);
}

View File

@@ -175,7 +175,7 @@ namespace Umbraco.Cms.Core.IO
return FileSystem.GetFiles(path, filter);
}
public Stream? OpenFile(string path)
public Stream OpenFile(string path)
{
return FileSystem.OpenFile(path);
}

View File

@@ -37,7 +37,7 @@ namespace Umbraco.Cms.Core.IO
if (_viewFileSystem.FileExists(path))
{
using (var tr = new StreamReader(_viewFileSystem.OpenFile(path)!))
using (var tr = new StreamReader(_viewFileSystem.OpenFile(path)))
{
viewContent = tr.ReadToEnd();
tr.Close();
@@ -58,7 +58,7 @@ namespace Umbraco.Cms.Core.IO
}
else
{
using (var tr = new StreamReader(_viewFileSystem.OpenFile(path)!))
using (var tr = new StreamReader(_viewFileSystem.OpenFile(path)))
{
viewContent = tr.ReadToEnd();
tr.Close();

View File

@@ -1,4 +1,4 @@
using System;
using System;
using System.Collections.Generic;
namespace Umbraco.Cms.Core.Mapping
@@ -131,7 +131,7 @@ namespace Umbraco.Cms.Core.Mapping
/// <typeparam name="TTargetElement">The type of the target objects.</typeparam>
/// <param name="source">The source objects.</param>
/// <returns>A list containing the target objects.</returns>
List<TTargetElement?> MapEnumerable<TSourceElement, TTargetElement>(IEnumerable<TSourceElement> source);
List<TTargetElement> MapEnumerable<TSourceElement, TTargetElement>(IEnumerable<TSourceElement> source);
/// <summary>
/// Maps an enumerable of source objects to a new list of target objects.
@@ -141,7 +141,7 @@ namespace Umbraco.Cms.Core.Mapping
/// <param name="source">The source objects.</param>
/// <param name="f">A mapper context preparation method.</param>
/// <returns>A list containing the target objects.</returns>
List<TTargetElement?> MapEnumerable<TSourceElement, TTargetElement>(IEnumerable<TSourceElement> source, Action<MapperContext> f);
List<TTargetElement> MapEnumerable<TSourceElement, TTargetElement>(IEnumerable<TSourceElement> source, Action<MapperContext> f);
/// <summary>
/// Maps an enumerable of source objects to a new list of target objects.
@@ -151,6 +151,6 @@ namespace Umbraco.Cms.Core.Mapping
/// <param name="source">The source objects.</param>
/// <param name="context">A mapper context.</param>
/// <returns>A list containing the target objects.</returns>
List<TTargetElement?> MapEnumerable<TSourceElement, TTargetElement>(IEnumerable<TSourceElement> source, MapperContext context);
List<TTargetElement> MapEnumerable<TSourceElement, TTargetElement>(IEnumerable<TSourceElement> source, MapperContext context);
}
}

View File

@@ -1,4 +1,4 @@
using System.Collections.Generic;
using System.Collections.Generic;
using System.Linq;
namespace Umbraco.Cms.Core.Mapping
@@ -119,7 +119,7 @@ namespace Umbraco.Cms.Core.Mapping
/// <typeparam name="TTargetElement">The type of the target objects.</typeparam>
/// <param name="source">The source objects.</param>
/// <returns>A list containing the target objects.</returns>
public List<TTargetElement?> MapEnumerable<TSourceElement, TTargetElement>(IEnumerable<TSourceElement> source)
public List<TTargetElement> MapEnumerable<TSourceElement, TTargetElement>(IEnumerable<TSourceElement> source)
{
return source.Select(Map<TSourceElement, TTargetElement>).ToList();
}

View File

@@ -73,7 +73,7 @@ namespace Umbraco.Cms.Core.Media
// if anything goes wrong, just reset the properties
try
{
using (var filestream = _mediaFileManager.FileSystem.OpenFile(filepath))
using (Stream filestream = _mediaFileManager.FileSystem.OpenFile(filepath))
{
SetProperties(content, autoFillConfig, filepath, filestream, culture, segment);
}

View File

@@ -1,4 +1,4 @@
using System;
using System;
using System.Collections.Generic;
namespace Umbraco.Cms.Core.Models.PublishedContent
@@ -33,7 +33,7 @@ namespace Umbraco.Cms.Core.Models.PublishedContent
/// </summary>
/// <remarks>Contains one <c>IPublishedProperty</c> for each property defined for the content type, including
/// inherited properties. Some properties may have no value.</remarks>
IEnumerable<IPublishedProperty>? Properties { get; }
IEnumerable<IPublishedProperty> Properties { get; }
/// <summary>
/// Gets a property identified by its alias.

View File

@@ -1,4 +1,4 @@
using System;
using System;
using System.Collections.Generic;
using System.Diagnostics;
@@ -122,7 +122,7 @@ namespace Umbraco.Cms.Core.Models.PublishedContent
#region Properties
/// <inheritdoc cref="IPublishedElement.Properties"/>
public virtual IEnumerable<IPublishedProperty>? Properties => _content.Properties;
public virtual IEnumerable<IPublishedProperty> Properties => _content.Properties;
/// <inheritdoc cref="IPublishedElement.GetProperty(string)"/>
public virtual IPublishedProperty? GetProperty(string alias)

View File

@@ -1,4 +1,4 @@
using System;
using System;
using System.Collections.Generic;
namespace Umbraco.Cms.Core.Models.PublishedContent
@@ -37,7 +37,7 @@ namespace Umbraco.Cms.Core.Models.PublishedContent
public Guid Key => _content.Key;
/// <inheritdoc />
public IEnumerable<IPublishedProperty>? Properties => _content.Properties;
public IEnumerable<IPublishedProperty> Properties => _content.Properties;
/// <inheritdoc />
public IPublishedProperty? GetProperty(string alias) => _content.GetProperty(alias);

View File

@@ -456,22 +456,20 @@ namespace Umbraco.Cms.Core.Packaging
throw new InvalidOperationException("No file found with path " + file);
}
using (Stream? stream = fileSystem?.OpenFile(file))
using (Stream stream = fileSystem!.OpenFile(file))
{
if (stream is not null)
using (var reader = new StreamReader(stream))
{
using (var reader = new StreamReader(stream))
{
var fileContents = reader.ReadToEnd();
scriptsXml.Add(
new XElement(
elementName,
new XAttribute("path", file),
new XCData(fileContents)));
}
var fileContents = reader.ReadToEnd();
scriptsXml.Add(
new XElement(
elementName,
new XAttribute("path", file),
new XCData(fileContents)));
}
}
}
root.Add(scriptsXml);
}

View File

@@ -4,7 +4,7 @@ namespace Umbraco.Cms.Core.Persistence.Repositories
{
public interface IFileRepository
{
Stream? GetFileContentStream(string filepath);
Stream GetFileContentStream(string filepath);
void SetFileContent(string filepath, Stream content);

View File

@@ -9,6 +9,6 @@ namespace Umbraco.Cms.Core.Persistence.Repositories
{
IMacro? GetByAlias(string alias);
IEnumerable<IMacro>? GetAllByAlias(string[] aliases);
IEnumerable<IMacro> GetAllByAlias(string[] aliases);
}
}

View File

@@ -72,7 +72,7 @@ namespace Umbraco.Cms.Core.PublishedCache.Internal
public IPublishedContentType ContentType { get; set; }
public IEnumerable<IPublishedProperty>? Properties { get; set; }
public IEnumerable<IPublishedProperty> Properties { get; set; }
public IPublishedProperty? GetProperty(string alias) => Properties?.FirstOrDefault(p => p.Alias.InvariantEquals(alias));

View File

@@ -1,4 +1,4 @@
using System;
using System;
using System.Collections.Generic;
using System.Linq;
using Umbraco.Cms.Core.Models.PublishedContent;
@@ -36,9 +36,10 @@ namespace Umbraco.Cms.Core.PublishedCache
.Select(propertyType =>
{
values.TryGetValue(propertyType.Alias, out var value);
return (IPublishedProperty) new PublishedElementPropertyBase(propertyType, this, previewing, referenceCacheLevel, value, publishedSnapshotAccessor);
return (IPublishedProperty)new PublishedElementPropertyBase(propertyType, this, previewing, referenceCacheLevel, value, publishedSnapshotAccessor);
})
.ToArray();
.ToArray()
?? new IPublishedProperty[0];
}
// initializes a new instance of the PublishedElement class
@@ -72,9 +73,9 @@ namespace Umbraco.Cms.Core.PublishedCache
#region Properties
private readonly IPublishedProperty[]? _propertiesArray;
private readonly IPublishedProperty[] _propertiesArray;
public IEnumerable<IPublishedProperty>? Properties => _propertiesArray;
public IEnumerable<IPublishedProperty> Properties => _propertiesArray;
public IPublishedProperty? GetProperty(string alias)
{

View File

@@ -1176,10 +1176,12 @@ namespace Umbraco.Cms.Core.Services
#region File Management
public Stream? GetMediaFileContentStream(string filepath)
public Stream GetMediaFileContentStream(string filepath)
{
if (_mediaFileManager.FileSystem.FileExists(filepath) == false)
return null;
{
return Stream.Null;
}
try
{
@@ -1187,7 +1189,7 @@ namespace Umbraco.Cms.Core.Services
}
catch
{
return null; // deal with race conds
return Stream.Null; // deal with race conds
}
}

View File

@@ -442,9 +442,13 @@ namespace Umbraco.Cms.Core.Mapping
/// <typeparam name="TTargetElement">The type of the target objects.</typeparam>
/// <param name="source">The source objects.</param>
/// <returns>A list containing the target objects.</returns>
public List<TTargetElement?> MapEnumerable<TSourceElement, TTargetElement>(IEnumerable<TSourceElement> source)
public List<TTargetElement> MapEnumerable<TSourceElement, TTargetElement>(IEnumerable<TSourceElement> source)
{
return source.Select(Map<TSourceElement, TTargetElement>).ToList();
return source
.Select(Map<TSourceElement, TTargetElement>)
.Where(x => x is not null)
.Select(x => x!)
.ToList();
}
/// <summary>
@@ -455,11 +459,15 @@ namespace Umbraco.Cms.Core.Mapping
/// <param name="source">The source objects.</param>
/// <param name="f">A mapper context preparation method.</param>
/// <returns>A list containing the target objects.</returns>
public List<TTargetElement?> MapEnumerable<TSourceElement, TTargetElement>(IEnumerable<TSourceElement> source, Action<MapperContext> f)
public List<TTargetElement> MapEnumerable<TSourceElement, TTargetElement>(IEnumerable<TSourceElement> source, Action<MapperContext> f)
{
var context = new MapperContext(this);
f(context);
return source.Select(x => Map<TSourceElement, TTargetElement>(x, context)).ToList();
return source
.Select(x => Map<TSourceElement, TTargetElement>(x, context))
.Where(x => x is not null)
.Select(x => x!)
.ToList();
}
/// <summary>
@@ -470,9 +478,13 @@ namespace Umbraco.Cms.Core.Mapping
/// <param name="source">The source objects.</param>
/// <param name="context">A mapper context.</param>
/// <returns>A list containing the target objects.</returns>
public List<TTargetElement?> MapEnumerable<TSourceElement, TTargetElement>(IEnumerable<TSourceElement> source, MapperContext context)
public List<TTargetElement> MapEnumerable<TSourceElement, TTargetElement>(IEnumerable<TSourceElement> source, MapperContext context)
{
return source.Select(x => Map<TSourceElement, TTargetElement>(x, context)).ToList();
return source
.Select(x => Map<TSourceElement, TTargetElement>(x, context))
.Where(x => x is not null)
.Select(x => x!)
.ToList();
}
#endregion

View File

@@ -5,7 +5,6 @@ using Microsoft.Extensions.Logging;
using NPoco;
using Umbraco.Cms.Core;
using Umbraco.Cms.Core.Cache;
using Umbraco.Cms.Core.Models;
using Umbraco.Cms.Core.Models.Entities;
using Umbraco.Cms.Core.Persistence;
using Umbraco.Cms.Core.Persistence.Querying;
@@ -176,7 +175,7 @@ namespace Umbraco.Cms.Infrastructure.Persistence.Repositories.Implement
/// <summary>
/// Gets a list of entities by the passed in query
/// </summary>
public IEnumerable<TEntity>? Get(IQuery<TEntity> query)
public IEnumerable<TEntity> Get(IQuery<TEntity> query)
{
// ensure we don't include any null refs in the returned collection!
@@ -210,9 +209,9 @@ namespace Umbraco.Cms.Infrastructure.Persistence.Repositories.Implement
protected abstract TEntity? PerformGet(TId? id);
protected abstract IEnumerable<TEntity>? PerformGetAll(params TId[]? ids);
protected abstract IEnumerable<TEntity> PerformGetAll(params TId[]? ids);
protected abstract IEnumerable<TEntity>? PerformGetByQuery(IQuery<TEntity> query);
protected abstract IEnumerable<TEntity> PerformGetByQuery(IQuery<TEntity> query);
protected abstract void PersistNewItem(TEntity item);

View File

@@ -202,20 +202,20 @@ namespace Umbraco.Cms.Infrastructure.Persistence.Repositories.Implement
return null;
}
public Stream? GetFileContentStream(string filepath)
public Stream GetFileContentStream(string filepath)
{
if (FileSystem?.FileExists(filepath) == false)
{
return null;
return Stream.Null;
}
try
{
return FileSystem?.OpenFile(filepath);
return FileSystem?.OpenFile(filepath) ?? Stream.Null;
}
catch
{
return null; // deal with race conds
return Stream.Null; // deal with race conds
}
}

View File

@@ -75,7 +75,7 @@ namespace Umbraco.Cms.Infrastructure.Persistence.Repositories.Implement
return _macroByAliasCachePolicy.Get(alias, PerformGetByAlias, PerformGetAllByAlias);
}
public IEnumerable<IMacro>? GetAllByAlias(string[] aliases)
public IEnumerable<IMacro> GetAllByAlias(string[] aliases)
{
if (aliases.Any() is false)
{
@@ -91,7 +91,7 @@ namespace Umbraco.Cms.Infrastructure.Persistence.Repositories.Implement
return PerformGetByQuery(query)?.FirstOrDefault();
}
private IEnumerable<IMacro>? PerformGetAllByAlias(params string[]? aliases)
private IEnumerable<IMacro> PerformGetAllByAlias(params string[]? aliases)
{
if (aliases is null || aliases.Any() is false)
{

View File

@@ -1,4 +1,4 @@
using System.Collections.Generic;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
@@ -101,17 +101,20 @@ namespace Umbraco.Cms.Infrastructure.Persistence.Repositories.Implement
}
}
public Stream? GetFileContentStream(string filepath)
public Stream GetFileContentStream(string filepath)
{
if (FileSystem?.FileExists(filepath) == false) return null;
if (FileSystem?.FileExists(filepath) == false)
{
return Stream.Null;
}
try
{
return FileSystem?.OpenFile(filepath);
return FileSystem?.OpenFile(filepath) ?? Stream.Null;
}
catch
{
return null; // deal with race conds
return Stream.Null; // deal with race conds
}
}

View File

@@ -441,22 +441,17 @@ namespace Umbraco.Cms.Infrastructure.Persistence.Repositories.Implement
return null;
}
using Stream? stream = fs.OpenFile(filename);
if (stream is not null)
{
using var reader = new StreamReader(stream, Encoding.UTF8, true);
return reader.ReadToEnd();
}
return null;
using Stream stream = fs.OpenFile(filename);
using var reader = new StreamReader(stream, Encoding.UTF8, true);
return reader.ReadToEnd();
}
public Stream? GetFileContentStream(string filepath)
public Stream GetFileContentStream(string filepath)
{
IFileSystem? fileSystem = GetFileSystem(filepath);
if (fileSystem?.FileExists(filepath) == false)
{
return null;
return Stream.Null;
}
try
@@ -465,7 +460,7 @@ namespace Umbraco.Cms.Infrastructure.Persistence.Repositories.Implement
}
catch
{
return null; // deal with race conds
return Stream.Null; // deal with race conds
}
}