Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions ODataCodeGenTools.sln
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 16
VisualStudioVersion = 16.0.29613.14
# Visual Studio Version 18
VisualStudioVersion = 18.3.11312.210
MinimumVisualStudioVersion = 10.0.40219.1
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ODataConnectedService", "src\ODataConnectedService\ODataConnectedService.csproj", "{A8BC5B8E-9AB7-4257-B8F1-E7C62169F9B5}"
EndProject
Expand Down
7 changes: 7 additions & 0 deletions azure-pipelines.yml
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,12 @@ stages:
- job: Build
steps:

- task: UseDotNet@2
displayName: 'Use .NET SDK 8.0.x'
inputs:
packageType: 'sdk'
version: '8.0.x'

- task: NuGetToolInstaller@0
inputs:
versionSpec: '>=5.2.0'
Expand All @@ -57,6 +63,7 @@ stages:
solution: '$(sln)'
platform: '$(buildPlatform)'
configuration: '$(buildConfiguration)'
msbuildArgs: '/p:DeployExtension=false'

- task: DotNetCoreCLI@2
displayName: 'Pack Microsoft.OData.Cli package'
Expand Down
9 changes: 9 additions & 0 deletions src/Microsoft.OData.Cli/ODataCliFileHandler.cs
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,15 @@ public Task<bool> EmitContainerPropertyAttributeAsync()
return Task.FromResult(true);
}

/// <summary>
/// True if the native date and time types can be emitted; otherwise, false.
/// </summary>
/// <returns>A bool indicating whether to emit native date and time types or not</returns>
public Task<bool> EmitNativeDateTimeTypesAsync()
{
return Task.FromResult(this.project.CheckODataClientVersion());
}

/// <summary>
/// Sets the CSDL file as an embedded resource
/// </summary>
Expand Down
29 changes: 29 additions & 0 deletions src/Microsoft.OData.Cli/ProjectHelper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -143,5 +143,34 @@ internal static string[] GetProjectTargetFrameworks(this Project project)

return targetFrameworks;
}

/// <summary>
/// Checks if the Microsoft.OData.Client package version in the project is at least 9.0.0.
/// </summary>
/// <param name="project">An instance of the loaded <see cref="Project"/>.</param>
/// <returns>True if the Microsoft.OData.Client version is at least 9.0.0; otherwise, false.</returns>
internal static bool CheckODataClientVersion(this Project project)
{
if (project == null)
{
return false;
}

var version = project.GetItems("PackageReference")
.FirstOrDefault(pr => pr.EvaluatedInclude.Equals("Microsoft.OData.Client", StringComparison.OrdinalIgnoreCase))
?.GetMetadataValue("Version");

if (string.IsNullOrEmpty(version))
{
return false;
}

if (version.Contains('-'))
{
version = version.Substring(0, version.IndexOf("-"));
}

return Version.TryParse(version, out Version odataClientVersion) && odataClientVersion >= Version.Parse("9.0.0");
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -196,6 +196,9 @@ private async Task AddGeneratedCodeAsync(string metadata, string outputDirectory
await FileHandler.SetFileAsEmbeddedResourceAsync(csdlFileName);
t4CodeGenerator.EmitContainerPropertyAttribute = await FileHandler.EmitContainerPropertyAttributeAsync();

// Determine whether to emit native DateOnly and TimeOnly types
t4CodeGenerator.EmitNativeDateTimeTypes = await FileHandler.EmitNativeDateTimeTypesAsync();

t4CodeGenerator.MetadataFilePath = metadataFile;
t4CodeGenerator.MetadataFileRelativePath = csdlFileName;

Expand Down
9 changes: 8 additions & 1 deletion src/Microsoft.OData.CodeGen/FileHandling/IFileHandler.cs
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,14 @@ public interface IFileHandler
/// <summary>
/// Emits container property attribute
/// </summary>
/// <returns></returns>
/// <returns>>A task that represents the asynchronous operation. true if container property can be emitted; otherwise false</returns>
Task<bool> EmitContainerPropertyAttributeAsync();

/// <summary>
/// Emits dotnet native date and time types (DateOnly and TimeOnly) to the target environment asynchronously.
/// </summary>
/// <returns>A task that represents the asynchronous operation. true if the native
/// date and time types can be emitted; otherwise, false.</returns>
Task<bool> EmitNativeDateTimeTypesAsync();
}
}
34 changes: 28 additions & 6 deletions src/Microsoft.OData.CodeGen/Templates/ODataT4CodeGenerator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -209,7 +209,8 @@ public virtual async Task<string> TransformTextAsync()
ExcludedOperationImports = this.ExcludedOperationImports,
ExcludedBoundOperations = this.ExcludedBoundOperations,
ExcludedSchemaTypes = this.ExcludedSchemaTypes,
EmitContainerPropertyAttribute = this.EmitContainerPropertyAttribute
EmitContainerPropertyAttribute = this.EmitContainerPropertyAttribute,
EmitNativeDateTimeTypes = this.EmitNativeDateTimeTypes
};
}
else
Expand Down Expand Up @@ -250,7 +251,8 @@ public virtual async Task<string> TransformTextAsync()
ExcludedOperationImports = this.ExcludedOperationImports,
ExcludedBoundOperations = this.ExcludedBoundOperations,
ExcludedSchemaTypes = this.ExcludedSchemaTypes,
EmitContainerPropertyAttribute = this.EmitContainerPropertyAttribute
EmitContainerPropertyAttribute = this.EmitContainerPropertyAttribute,
EmitNativeDateTimeTypes = this.EmitNativeDateTimeTypes
};
}

Expand Down Expand Up @@ -568,6 +570,15 @@ public bool EmitContainerPropertyAttribute
internal set;
}

/// <summary>
/// true to emit .NET date/time types (DateOnly and TimeOnly) instead of Microsoft.OData.Edm.Date and Microsoft.OData.Edm.TimeOfDay, false otherwise
/// </summary>
public bool EmitNativeDateTimeTypes
{
get;
internal set;
}

/// <summary>
/// Generate code targeting a specific .Net Framework language.
/// </summary>
Expand Down Expand Up @@ -1333,6 +1344,17 @@ public bool EmitContainerPropertyAttribute
set;
}

/// <summary>
/// true to use native .NET date/time types (DateOnly and TimeOnly) instead of
/// Microsoft.OData.Edm.Date and Microsoft.OData.Edm.TimeOfDay, false otherwise.
/// Set to true when targeting .NET 10.0 or later.
/// </summary>
public bool EmitNativeDateTimeTypes
{
get;
set;
}

/// <summary>
/// true if this EntityContainer need to set the UrlConvention to KeyAsSegment, false otherwise.
/// </summary>
Expand Down Expand Up @@ -4298,10 +4320,10 @@ public ODataClientCSharpTemplate(CodeGenerationContext context)
internal override string GeometryMultiPolygonTypeName { get { return "global::Microsoft.Spatial.GeometryMultiPolygon"; } }
internal override string GeometryMultiLineStringTypeName { get { return "global::Microsoft.Spatial.GeometryMultiLineString"; } }
internal override string GeometryMultiPointTypeName { get { return "global::Microsoft.Spatial.GeometryMultiPoint"; } }
internal override string DateTypeName { get { return "global::Microsoft.OData.Edm.Date"; } }
internal override string DateTypeName { get { return this.context.EmitNativeDateTimeTypes ? "global::System.DateOnly" : "global::Microsoft.OData.Edm.Date"; } }
internal override string DateTimeOffsetTypeName { get { return "global::System.DateTimeOffset"; } }
internal override string DurationTypeName { get { return "global::System.TimeSpan"; } }
internal override string TimeOfDayTypeName { get { return "global::Microsoft.OData.Edm.TimeOfDay"; } }
internal override string TimeOfDayTypeName { get { return this.context.EmitNativeDateTimeTypes ? "global::System.TimeOnly" : "global::Microsoft.OData.Edm.TimeOfDay"; } }
internal override string XmlConvertClassName { get { return "global::System.Xml.XmlConvert"; } }
internal override string EnumTypeName { get { return "global::System.Enum"; } }
internal override string DictionaryInterfaceName { get { return "global::System.Collections.Generic.IDictionary<{0}, {1}>"; } }
Expand Down Expand Up @@ -6441,10 +6463,10 @@ public ODataClientVBTemplate(CodeGenerationContext context)
internal override string GeometryMultiPolygonTypeName { get { return "Global.Microsoft.Spatial.GeometryMultiPolygon"; } }
internal override string GeometryMultiLineStringTypeName { get { return "Global.Microsoft.Spatial.GeometryMultiLineString"; } }
internal override string GeometryMultiPointTypeName { get { return "Global.Microsoft.Spatial.GeometryMultiPoint"; } }
internal override string DateTypeName { get { return "Global.Microsoft.OData.Edm.Date"; } }
internal override string DateTypeName { get { return this.context.EmitNativeDateTimeTypes ? "Global.System.DateOnly" : "Global.Microsoft.OData.Edm.Date"; } }
internal override string DateTimeOffsetTypeName { get { return "Global.System.DateTimeOffset"; } }
internal override string DurationTypeName { get { return "Global.System.TimeSpan"; } }
internal override string TimeOfDayTypeName { get { return "Global.Microsoft.OData.Edm.TimeOfDay"; } }
internal override string TimeOfDayTypeName { get { return this.context.EmitNativeDateTimeTypes ? "Global.System.TimeOnly" : "Global.Microsoft.OData.Edm.TimeOfDay"; } }
internal override string XmlConvertClassName { get { return "Global.System.Xml.XmlConvert"; } }
internal override string EnumTypeName { get { return "Global.System.Enum"; } }
internal override string DictionaryInterfaceName { get { return "Global.System.Collections.Generic.IDictionary(Of {0}, {1})"; } }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,8 @@ public virtual async Task<string> TransformTextAsync()
ExcludedOperationImports = this.ExcludedOperationImports,
ExcludedBoundOperations = this.ExcludedBoundOperations,
ExcludedSchemaTypes = this.ExcludedSchemaTypes,
EmitContainerPropertyAttribute = this.EmitContainerPropertyAttribute
EmitContainerPropertyAttribute = this.EmitContainerPropertyAttribute,
EmitNativeDateTimeTypes = this.EmitNativeDateTimeTypes
};
}
else
Expand Down Expand Up @@ -107,7 +108,8 @@ public virtual async Task<string> TransformTextAsync()
ExcludedOperationImports = this.ExcludedOperationImports,
ExcludedBoundOperations = this.ExcludedBoundOperations,
ExcludedSchemaTypes = this.ExcludedSchemaTypes,
EmitContainerPropertyAttribute = this.EmitContainerPropertyAttribute
EmitContainerPropertyAttribute = this.EmitContainerPropertyAttribute,
EmitNativeDateTimeTypes = this.EmitNativeDateTimeTypes
};
}

Expand Down Expand Up @@ -425,6 +427,15 @@ public bool EmitContainerPropertyAttribute
internal set;
}

/// <summary>
/// true to emit .NET date/time types (DateOnly and TimeOnly) instead of Microsoft.OData.Edm.Date and Microsoft.OData.Edm.TimeOfDay, false otherwise
/// </summary>
public bool EmitNativeDateTimeTypes
{
get;
internal set;
}

/// <summary>
/// Generate code targeting a specific .Net Framework language.
/// </summary>
Expand Down Expand Up @@ -1190,6 +1201,17 @@ public class CodeGenerationContext
set;
}

/// <summary>
/// true to use native .NET date/time types (DateOnly and TimeOnly) instead of
/// Microsoft.OData.Edm.Date and Microsoft.OData.Edm.TimeOfDay, false otherwise.
/// Set to true when targeting .NET 10.0 or later.
/// </summary>
public bool EmitNativeDateTimeTypes
{
get;
set;
}

/// <summary>
/// true if this EntityContainer need to set the UrlConvention to KeyAsSegment, false otherwise.
/// </summary>
Expand Down
74 changes: 53 additions & 21 deletions src/ODataConnectedService.Shared/ConnectedServiceFileHandler.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,10 @@
using System.Threading.Tasks;
using EnvDTE;
using Microsoft.OData.CodeGen.FileHandling;
using Microsoft.OData.ConnectedService.Threading;
using Microsoft.VisualStudio.ConnectedServices;
using Microsoft.VisualStudio.Shell;
using VSLangProj;
using Microsoft.OData.ConnectedService.Threading;
using Task = System.Threading.Tasks.Task;

namespace Microsoft.OData.ConnectedService
Expand All @@ -25,6 +25,10 @@ public class ConnectedServiceFileHandler : IFileHandler
private ConnectedServiceHandlerContext Context;
private readonly IThreadHelper threadHelper;

// Cache the OData Client version to avoid multiple project references enumeration
private Version odataClientVersion = null;
private bool isOdataClientVersionCached = false;

public Project Project { get; private set; }

/// <summary>
Expand Down Expand Up @@ -70,7 +74,7 @@ await this.threadHelper.RunInUiThreadAsync(() =>
}
#pragma warning restore VSTHRD010 // This invokes the code in the required main thread.
return false;
});
}).ConfigureAwait(false);
}

/// <summary>
Expand All @@ -79,27 +83,55 @@ await this.threadHelper.RunInUiThreadAsync(() =>
/// </summary>
/// <returns>A value of either true or false</returns>
public Task<bool> EmitContainerPropertyAttributeAsync()
=> threadHelper.RunInUiThreadAsync(() =>
=> this.CheckODataClientVersionAsync(version => version > Version.Parse("7.6.4.0"));

/// <summary>
/// Determines asynchronously whether native date and time types are supported by the connected OData service.
/// </summary>
/// <returns>A task that represents the asynchronous operation. True if native date and time types are supported; otherwise, false.</returns>
public Task<bool> EmitNativeDateTimeTypesAsync()
=> this.CheckODataClientVersionAsync(version => version >= Version.Parse("9.0.0") || version >= Version.Parse("9.0.0.0"));

/// <summary>
/// Checks if the Microsoft.OData.Client reference meets a version condition.
/// </summary>
/// <param name="versionPredicate">A predicate to evaluate against the OData Client version.</param>
/// <returns>True if the reference exists and meets the version condition; otherwise false.</returns>
private Task<bool> CheckODataClientVersionAsync(Func<Version, bool> versionPredicate)
{
return this.threadHelper.RunInUiThreadAsync(() =>
{
if (this.isOdataClientVersionCached && this.odataClientVersion != null)
{
return versionPredicate(this.odataClientVersion);
}

{
#pragma warning disable VSTHRD010 // Invoke single-threaded types on Main thread
if (this.Project.Object is VSProject vsProject)
{
foreach (Reference reference in vsProject.References)
{
if (reference.SourceProject == null)
{
// Assembly reference (For project reference, SourceProject != null)
if (reference.Name.Equals("Microsoft.OData.Client", StringComparison.Ordinal))
{
return Version.Parse(reference.Version) > Version.Parse("7.6.4.0");
}
}
}
}
if (this.Project.Object is VSProject vsProject)
{
foreach (Reference reference in vsProject.References)
{
if (reference.SourceProject == null &&
reference.Name.Equals("Microsoft.OData.Client", StringComparison.Ordinal))
{
var currentVersion = reference.Version;
if (currentVersion.Contains("-"))
{
currentVersion = currentVersion.Substring(0, currentVersion.IndexOf('-'));
}

this.odataClientVersion = Version.Parse(currentVersion);
this.isOdataClientVersionCached = true;
return versionPredicate(this.odataClientVersion);
}
}
}
#pragma warning restore VSTHRD010 // Invoke single-threaded types on Main thread

return false;
});
this.odataClientVersion = null;
this.isOdataClientVersionCached = true;
return false;
});
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ public async Task CheckAndInstallNuGetPackageAsync(string packageSource, string
{
if (!PackageInstallerServices.IsPackageInstalled(this.Project, packageName))
{
PackageInstaller.InstallPackage(packageSource, this.Project, packageName, (string)null, false); ;
PackageInstaller.InstallPackage(packageSource, this.Project, packageName, (string)null, false);

await (this.MessageLogger?.WriteMessageAsync(LogMessageCategory.Information, $"Nuget Package \"{packageName}\" for OData client was added.")).ConfigureAwait(false);
}
Expand Down
Loading
Loading