diff --git a/Tests/DefaultModelProviderFactoryTests.cs b/Tests/DefaultModelProviderFactoryTests.cs index f1756d212..feffcc546 100644 --- a/Tests/DefaultModelProviderFactoryTests.cs +++ b/Tests/DefaultModelProviderFactoryTests.cs @@ -48,16 +48,11 @@ public DefaultModelProviderFactoryTests(ITestOutputHelper testOutputHelper) XbimServices xbimServices; - [Fact(Skip = "Need to re-consider the requirement - should it be explicit")] - public void ShouldProvideHeuristicModelProvider_When_XBIM_Esent_Loaded() + [Fact] + public void ShouldProvideHeuristicModelProvider_In_IfcStore() { - xbimServices.ConfigureServices(s => s.AddXbimToolkit()); - var serviceProvider = xbimServices.ServiceProvider; - - IModelProviderFactory factory = serviceProvider.GetRequiredService(); - - var modelProvider = factory.CreateProvider(); + var modelProvider = IfcStore.ModelProviderFactory.CreateProvider(); Assert.IsType(modelProvider); } diff --git a/Tests/DependencyInjectionTests.cs b/Tests/DependencyInjectionTests.cs index 7958effd3..792c29cf7 100644 --- a/Tests/DependencyInjectionTests.cs +++ b/Tests/DependencyInjectionTests.cs @@ -91,19 +91,6 @@ public void ThirdParty_modelProviders_resolve() provider.Should().BeOfType(); } - [Fact(Skip ="Brittle due to use of singleton state")] - public void IfcStore_resolves_defined_provider() - { - XbimServices.Current.Rebuild(); - - SuT.ConfigureServices(s => s.AddXbimToolkit(opt => opt.UseEsentModel())); - - var provider = IfcStore.ModelProviderFactory.CreateProvider(); - XbimServices.Current.Rebuild(); - - provider.Should().BeOfType(); - } - [Fact] public void Logging_can_be_added() diff --git a/Xbim.IO.Esent/Esent/EsentModel.cs b/Xbim.IO.Esent/Esent/EsentModel.cs index ae3940e57..b08aa400a 100644 --- a/Xbim.IO.Esent/Esent/EsentModel.cs +++ b/Xbim.IO.Esent/Esent/EsentModel.cs @@ -13,13 +13,15 @@ using Xbim.Common.Configuration; using Xbim.Common.Metadata; using Xbim.Common.Step21; +using System.Runtime.InteropServices; namespace Xbim.IO.Esent { /// /// IModel implementation for Esent DB based model support /// - + /// Esent is a data storage system deployed automatically on Microsoft Windows machines. + /// Consequently this IModel is only supported on Windows Operating Systems public class EsentModel : IModel, IFederatedModel, IDisposable { #region Fields @@ -99,6 +101,11 @@ private EsentModel(ILoggerFactory loggerFactory) { _loggerFactory = loggerFactory ?? XbimServices.Current.GetLoggerFactory(); Logger = _loggerFactory.CreateLogger(); + if(!RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) + { + Logger.LogCritical("Esent is Windows only and not supported on {OS}", RuntimeInformation.OSDescription); + throw new NotSupportedException("Esent is not supported on this operating system"); + } } protected void Init(IEntityFactory factory) diff --git a/Xbim.IO.Esent/EsentModelProvider.cs b/Xbim.IO.Esent/EsentModelProvider.cs index bc4514c37..a148c02d3 100644 --- a/Xbim.IO.Esent/EsentModelProvider.cs +++ b/Xbim.IO.Esent/EsentModelProvider.cs @@ -7,6 +7,7 @@ using Xbim.Common.Exceptions; using Xbim.Common.Configuration; using Xbim.Common.Step21; +using System.Runtime.InteropServices; namespace Xbim.IO.Esent { @@ -23,6 +24,10 @@ public EsentModelProvider() : this(default) public EsentModelProvider(ILoggerFactory loggerFactory) { _loggerFactory = loggerFactory ?? XbimServices.Current.GetLoggerFactory(); + if (!IsEsentSupported()) + { + _logger.LogWarning("EsentModel is only compatible with Windows platforms. Please use another ModelProvider."); + } } public override StoreCapabilities Capabilities => new StoreCapabilities(isTransient: false, supportsTransactions: true); @@ -231,5 +236,10 @@ private EsentModel CreateEsentModel(XbimSchemaVersion schema, int codePageOverri } public string DatabaseFileName { get; set; } + + private static bool IsEsentSupported() + { + return RuntimeInformation.IsOSPlatform(OSPlatform.Windows); + } } } diff --git a/Xbim.IO.Esent/HeuristicModelProvider.cs b/Xbim.IO.Esent/HeuristicModelProvider.cs index a6f6f9267..93bbfcae1 100644 --- a/Xbim.IO.Esent/HeuristicModelProvider.cs +++ b/Xbim.IO.Esent/HeuristicModelProvider.cs @@ -1,5 +1,4 @@ -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Logging; using System; using System.Collections.Generic; using System.IO; @@ -10,12 +9,11 @@ using Xbim.IO; using Xbim.IO.Esent; using Xbim.IO.Memory; +using System.Runtime.InteropServices; namespace Xbim.Ifc { - // IMPORTANT: if we ever rename this provider we need to update the DefaultModelProviderFactory, since - // this is the default provider when the assembly is loaded, and it's discovered by Name through reflection /// /// The encapsulates the underlying implementations we use @@ -24,10 +22,13 @@ namespace Xbim.Ifc /// This store permits a to be used where it's appropriate, while switching to an /// when persistance is required, or a source model is above a size threshold. /// The store also permits a to persisted. + /// + /// Since EsentModel only supports the Windows platform this provider is not suitable for use on non-Windows Operating Systems /// public class HeuristicModelProvider : BaseModelProvider { private readonly ILoggerFactory _loggerFactory; + private readonly ILogger _logger; public HeuristicModelProvider() : this(default) { @@ -37,6 +38,12 @@ public HeuristicModelProvider() : this(default) public HeuristicModelProvider(ILoggerFactory loggerFactory) { _loggerFactory = loggerFactory ?? XbimServices.Current.GetLoggerFactory(); + _logger = _loggerFactory.CreateLogger(); + + if(!IsEsentSupported()) + { + _logger.LogWarning("EsentModel is only compatible with Windows operating systems. Please use another ModelProvider."); + } } /// @@ -52,8 +59,6 @@ public HeuristicModelProvider(ILoggerFactory loggerFactory) public override StoreCapabilities Capabilities => new StoreCapabilities(isTransient: false, supportsTransactions: true); - - /// /// Closes a Model store, releasing any resources /// @@ -168,6 +173,13 @@ public override XbimSchemaVersion GetXbimSchemaVersion(string modelPath) public override IModel Open(Stream stream, StorageType dataType, XbimSchemaVersion schema, XbimModelType modelType, XbimDBAccess accessMode = XbimDBAccess.Read, ReportProgressDelegate progDelegate = null, int codePageOverride = -1) { + + if(modelType == XbimModelType.EsentModel && ! IsEsentSupported()) + { + modelType = XbimModelType.MemoryModel; + _logger.LogWarning("EsentModel not support on this platform. Falling back to memory model where possible"); + } + //any Esent model needs to run from the file so we need to create a temporal one var xbimFilePath = Path.GetTempFileName(); xbimFilePath = Path.ChangeExtension(xbimFilePath, ".xbim"); @@ -175,6 +187,11 @@ public override IModel Open(Stream stream, StorageType dataType, XbimSchemaVersi switch (dataType) { case StorageType.Xbim: + if(modelType == XbimModelType.MemoryModel) + { + throw new NotSupportedException($"Cannot open xbim file with a Memory Model"); + } + //xBIM file has to be opened from the file so we need to create temporary file if it is not a local file stream var localFile = false; var fileStream = stream as FileStream; @@ -281,23 +298,27 @@ public override IModel Open(string path, XbimSchemaVersion schemaVersion, double if (storageType == StorageType.Xbim) //open the XbimFile { + if(!IsEsentSupported()) + { + throw new NotSupportedException($"Cannot open xbim file on this platform"); + } var model = CreateEsentModel(schemaVersion, codePageOverride); model.Open(path, accessMode, progDelegate); return model; } else //it will be an IFC file if we are at this point { - var fInfo = new FileInfo(path); - double ifcMaxLength = (ifcDatabaseSizeThreshHold ?? DefaultIfcDatabaseSizeThreshHoldMb) * 1024 * 1024; + var file = new FileInfo(path); + double ifcMaxFileLength = (ifcDatabaseSizeThreshHold ?? DefaultIfcDatabaseSizeThreshHoldMb) * 1024 * 1024; // we need to make an Esent database, if ifcMaxLength<0 we use in memory - if (ifcMaxLength >= 0 && fInfo.Length > ifcMaxLength) + if (ExceedsThreshold(file, ifcMaxFileLength) && IsEsentSupported()) { var tmpFileName = Path.GetTempFileName(); var model = CreateEsentModel(schemaVersion, codePageOverride); // We delete the XBIM on close as the consumer is not controlling the generation of the XBIM file if (model.CreateFrom(path, tmpFileName, progDelegate, keepOpen: true, deleteOnClose: true)) - return model; - + return model; + throw new FileLoadException(path + " file was not a valid IFC format"); } else //we can use a memory model @@ -321,6 +342,16 @@ public override IModel Open(string path, XbimSchemaVersion schemaVersion, double } } + private static bool ExceedsThreshold(FileInfo file, double maxLength) + { + return maxLength >= 0 && file.Length > maxLength; + } + + private static bool IsEsentSupported() + { + return RuntimeInformation.IsOSPlatform(OSPlatform.Windows); + } + /// /// Persists the model to a permanent store /// diff --git a/Xbim.Ifc/IfcStore.cs b/Xbim.Ifc/IfcStore.cs index 9e909c195..babdfdd44 100644 --- a/Xbim.Ifc/IfcStore.cs +++ b/Xbim.Ifc/IfcStore.cs @@ -5,6 +5,7 @@ using System.IO; using System.Linq; using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; using Xbim.Common; using Xbim.Common.Configuration; using Xbim.Common.Exceptions; @@ -72,7 +73,14 @@ static IfcStore() { if(!XbimServices.Current.IsBuilt) { - XbimServices.Current.ConfigureServices(s => s.AddXbimToolkit()); + if(RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) + { + XbimServices.Current.ConfigureServices(s => s.AddXbimToolkit(opt => opt.UseHeuristicModel())); + } + else + { + XbimServices.Current.ConfigureServices(s => s.AddXbimToolkit(opt => opt.UseMemoryModel())); + } } ModelProviderFactory = XbimServices.Current.ServiceProvider.GetRequiredService(); } diff --git a/Xbim.Ifc/Xbim.Ifc.csproj b/Xbim.Ifc/Xbim.Ifc.csproj index 2c9df7999..f224acb28 100644 --- a/Xbim.Ifc/Xbim.Ifc.csproj +++ b/Xbim.Ifc/Xbim.Ifc.csproj @@ -18,6 +18,7 @@ +