-
Notifications
You must be signed in to change notification settings - Fork 44
Add versioning support in durabletask-dotnet(phase1) #295
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from 14 commits
380c622
1976de6
4e1f8cb
f6162ff
d564c79
d9298d0
60c1341
fc44a4a
5b1b2b2
cb86ecd
83bc227
f1619b1
0045ced
2172e96
5c3ad58
7912d46
d015d3e
87b15e1
6ecfde1
220fb2d
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -8,127 +8,169 @@ | |
/// </summary> | ||
public readonly struct TaskName : IEquatable<TaskName> | ||
{ | ||
// TODO: Add detailed remarks that describe the role of TaskName | ||
// TODO: Add detailed remarks that describe the role of TaskName | ||
|
||
/// <summary> | ||
/// Initializes a new instance of the <see cref="TaskName"/> struct. | ||
/// </summary> | ||
/// <param name="name">The name of the task. Providing <c>null</c> will yield the default struct.</param> | ||
public TaskName(string name) | ||
/// <summary> | ||
/// Initializes a new instance of the <see cref="TaskName"/> struct. | ||
/// </summary> | ||
/// <param name="name">The name of the task. Providing <c>null</c> will yield the default struct.</param> | ||
public TaskName(string name) | ||
{ | ||
if (name is null) | ||
{ | ||
if (name is null) | ||
{ | ||
// Force the default struct when null is passed in. | ||
this.Name = null!; | ||
this.Version = null!; | ||
} | ||
else | ||
{ | ||
this.Name = name; | ||
this.Version = string.Empty; // expose setting Version only when we actually consume it. | ||
} | ||
// Force the default struct when null is passed in. | ||
this.Name = null!; | ||
this.Version = null!; | ||
} | ||
|
||
/// <summary> | ||
/// Gets the name of the task without the version. | ||
/// </summary> | ||
/// <value> | ||
/// The name of the activity task without the version. | ||
/// </value> | ||
public string Name { get; } | ||
|
||
/// <summary> | ||
/// Gets the version of the task. | ||
/// </summary> | ||
/// <remarks> | ||
/// Task versions is currently locked to <see cref="string.Empty" /> as it is not yet integrated into task | ||
/// identification. This is being left here as we intend to support it soon. | ||
/// </remarks> | ||
public string Version { get; } | ||
|
||
/// <summary> | ||
/// Implicitly converts a <see cref="TaskName"/> into a <see cref="string"/> of the <see cref="Name"/> property value. | ||
/// </summary> | ||
/// <param name="value">The <see cref="TaskName"/> to be converted into a string.</param> | ||
public static implicit operator string(TaskName value) => value.Name; | ||
|
||
/// <summary> | ||
/// Implicitly converts a <see cref="string"/> into a <see cref="TaskName"/> value. | ||
/// </summary> | ||
/// <param name="value">The string to convert into a <see cref="TaskName"/>.</param> | ||
public static implicit operator TaskName(string value) => string.IsNullOrEmpty(value) ? default : new(value); | ||
|
||
/// <summary> | ||
/// Compares two <see cref="TaskName"/> objects for equality. | ||
/// </summary> | ||
/// <param name="a">The first <see cref="TaskName"/> to compare.</param> | ||
/// <param name="b">The second <see cref="TaskName"/> to compare.</param> | ||
/// <returns><c>true</c> if the two <see cref="TaskName"/> objects are equal; otherwise <c>false</c>.</returns> | ||
public static bool operator ==(TaskName a, TaskName b) | ||
else | ||
{ | ||
return a.Equals(b); | ||
this.Name = name; | ||
this.Version = string.Empty; // expose setting Version only when we actually consume it. | ||
} | ||
} | ||
|
||
/// <summary> | ||
/// Compares two <see cref="TaskName"/> objects for inequality. | ||
/// </summary> | ||
/// <param name="a">The first <see cref="TaskName"/> to compare.</param> | ||
/// <param name="b">The second <see cref="TaskName"/> to compare.</param> | ||
/// <returns><c>true</c> if the two <see cref="TaskName"/> objects are not equal; otherwise <c>false</c>.</returns> | ||
public static bool operator !=(TaskName a, TaskName b) | ||
/// <summary> | ||
/// Initializes a new instance of the <see cref="TaskName"/> struct. | ||
/// </summary> | ||
/// <param name="name">The name of the task. Providing <c>null</c> will yield the default struct.</param> | ||
/// <param name="version">The version of the task.</param> | ||
cgillum marked this conversation as resolved.
Show resolved
Hide resolved
|
||
public TaskName(string name, string version) | ||
{ | ||
if (name is null) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is a good opportunity for pattern matching. Can replace this if/else statements with: (this.Name, this.Version) = (name, version) switch
{
(null, null) => (null!, null!), // Force the default struct when null is passed in.
(null, string _) => throw new ArgumentException("name must not be null when version is non-null"),
(string _, null) => (name, string.Empty),
(string _, string _) => (name, version),
}; Also while we are at it, can replace the other ctor with: (this.Name, this.Version) = name switch
{
null => (null!, null!), // Force the default struct when null is passed in.
string _ => (name, string.Empty),
} |
||
{ | ||
return !a.Equals(b); | ||
if (version is null) | ||
{ | ||
// Force the default struct when null is passed in. | ||
this.Name = null!; | ||
this.Version = null!; | ||
} | ||
else | ||
{ | ||
throw new ArgumentException("name must not be null when version is non-null"); | ||
} | ||
} | ||
|
||
/// <summary> | ||
/// Gets a value indicating whether to <see cref="TaskName"/> objects | ||
/// are equal using value semantics. | ||
/// </summary> | ||
/// <param name="other">The other object to compare to.</param> | ||
/// <returns><c>true</c> if the two objects are equal using value semantics; otherwise <c>false</c>.</returns> | ||
public bool Equals(TaskName other) | ||
else | ||
{ | ||
return string.Equals(this.Name, other.Name, StringComparison.OrdinalIgnoreCase); | ||
if (version is null) | ||
{ | ||
this.Name = name; | ||
this.Version = string.Empty; // fallback to the contructor without version parameter | ||
skyao marked this conversation as resolved.
Show resolved
Hide resolved
|
||
} | ||
else | ||
{ | ||
this.Name = name; | ||
this.Version = version; | ||
} | ||
} | ||
} | ||
|
||
/// <summary> | ||
/// Gets a value indicating whether to <see cref="TaskName"/> objects | ||
/// are equal using value semantics. | ||
/// </summary> | ||
/// <param name="obj">The other object to compare to.</param> | ||
/// <returns><c>true</c> if the two objects are equal using value semantics; otherwise <c>false</c>.</returns> | ||
public override bool Equals(object? obj) | ||
{ | ||
if (obj is not TaskName other) | ||
{ | ||
return false; | ||
} | ||
/// <summary> | ||
/// Gets the name of the task without the version. | ||
/// </summary> | ||
/// <value> | ||
/// The name of the activity task without the version. | ||
/// </value> | ||
public string Name { get; } | ||
|
||
/// <summary> | ||
/// Gets the version of the task. | ||
/// </summary> | ||
/// <remarks> | ||
/// Task versions is currently locked to <see cref="string.Empty" /> as it is not yet integrated into task | ||
/// identification. This is being left here as we intend to support it soon. | ||
skyao marked this conversation as resolved.
Show resolved
Hide resolved
|
||
/// </remarks> | ||
public string Version { get; } | ||
|
||
/// <summary> | ||
/// Implicitly converts a <see cref="TaskName"/> into a <see cref="string"/> of the <see cref="Name"/> property value. | ||
/// </summary> | ||
/// <param name="value">The <see cref="TaskName"/> to be converted into a string.</param> | ||
public static implicit operator string(TaskName value) => value.ToString(); | ||
|
||
/// <summary> | ||
/// Implicitly converts a <see cref="string"/> into a <see cref="TaskName"/> value. | ||
/// </summary> | ||
/// <param name="value">The string to convert into a <see cref="TaskName"/>.</param> | ||
public static implicit operator TaskName(string value) => string.IsNullOrEmpty(value) ? default : new(value); | ||
|
||
/// <summary> | ||
/// Compares two <see cref="TaskName"/> objects for equality. | ||
/// </summary> | ||
/// <param name="a">The first <see cref="TaskName"/> to compare.</param> | ||
/// <param name="b">The second <see cref="TaskName"/> to compare.</param> | ||
/// <returns><c>true</c> if the two <see cref="TaskName"/> objects are equal; otherwise <c>false</c>.</returns> | ||
public static bool operator ==(TaskName a, TaskName b) | ||
{ | ||
return a.Equals(b); | ||
} | ||
|
||
return this.Equals(other); | ||
/// <summary> | ||
/// Compares two <see cref="TaskName"/> objects for inequality. | ||
/// </summary> | ||
/// <param name="a">The first <see cref="TaskName"/> to compare.</param> | ||
/// <param name="b">The second <see cref="TaskName"/> to compare.</param> | ||
/// <returns><c>true</c> if the two <see cref="TaskName"/> objects are not equal; otherwise <c>false</c>.</returns> | ||
public static bool operator !=(TaskName a, TaskName b) | ||
{ | ||
return !a.Equals(b); | ||
} | ||
|
||
/// <summary> | ||
/// Gets a value indicating whether to <see cref="TaskName"/> objects | ||
/// are equal using value semantics. | ||
/// </summary> | ||
/// <param name="other">The other object to compare to.</param> | ||
/// <returns><c>true</c> if the two objects are equal using value semantics; otherwise <c>false</c>.</returns> | ||
public bool Equals(TaskName other) | ||
{ | ||
return string.Equals(this.Name, other.Name, StringComparison.OrdinalIgnoreCase) | ||
&& string.Equals(this.Version, other.Version, StringComparison.OrdinalIgnoreCase); | ||
} | ||
|
||
/// <summary> | ||
/// Gets a value indicating whether to <see cref="TaskName"/> objects | ||
/// are equal using value semantics. | ||
/// </summary> | ||
/// <param name="obj">The other object to compare to.</param> | ||
/// <returns><c>true</c> if the two objects are equal using value semantics; otherwise <c>false</c>.</returns> | ||
public override bool Equals(object? obj) | ||
{ | ||
if (obj is not TaskName other) | ||
{ | ||
return false; | ||
} | ||
|
||
/// <summary> | ||
/// Calculates a hash code value for the current <see cref="TaskName"/> instance. | ||
/// </summary> | ||
/// <returns>A 32-bit hash code value.</returns> | ||
public override int GetHashCode() | ||
return this.Equals(other); | ||
} | ||
|
||
/// <summary> | ||
/// Calculates a hash code value for the current <see cref="TaskName"/> instance. | ||
/// </summary> | ||
/// <returns>A 32-bit hash code value.</returns> | ||
public override int GetHashCode() | ||
{ | ||
if (string.IsNullOrEmpty(this.Version)) | ||
{ | ||
return StringComparer.OrdinalIgnoreCase.GetHashCode(this.Name); | ||
return StringComparer.OrdinalIgnoreCase.GetHashCode(this.Name); | ||
} | ||
|
||
/// <summary> | ||
/// Gets the string value of the current <see cref="TaskName"/> instance. | ||
/// </summary> | ||
/// <returns>The name and optional version of the current <see cref="TaskName"/> instance.</returns> | ||
public override string ToString() | ||
return StringComparer.OrdinalIgnoreCase.GetHashCode(this.Name) * 31 | ||
Check warning on line 157 in src/Abstractions/TaskName.cs
|
||
skyao marked this conversation as resolved.
Show resolved
Hide resolved
|
||
+ StringComparer.OrdinalIgnoreCase.GetHashCode(this.Version); | ||
} | ||
|
||
/// <summary> | ||
/// Gets the string value of the current <see cref="TaskName"/> instance. | ||
/// </summary> | ||
/// <returns>The name and optional version of the current <see cref="TaskName"/> instance.</returns> | ||
public override string ToString() | ||
{ | ||
if (string.IsNullOrEmpty(this.Version)) | ||
{ | ||
return this.Name; | ||
} | ||
else | ||
{ | ||
if (string.IsNullOrEmpty(this.Version)) | ||
{ | ||
return this.Name; | ||
} | ||
else | ||
{ | ||
return this.Name + ":" + this.Version; | ||
} | ||
return this.Name + ":" + this.Version; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We should consider adding FromString semantics. But I wonder if that will be a breaking change? We have not restricted what characters can be used in a task name before this. This change is going to introduce a confusing behavior: TaskName name1 = new("MyTask", "2"); // Name = "MyTask", Version = "2'
TaskName name2 = name1.ToString(); // implicit operator convers. Name = "MyTask:2", Version = "" There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Should the implicit operator be updated to parse out the version? I'm not super concerned about the breaking change since 99+% of .NET users should be relying on the function name, which already can't have special characters. However, we can take a look at Kusto data to confirm. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yes the implicit operator would be changed to call There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We should have a discussion on what separator to use. I like However, we already use There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think using There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @cgillum yeah I am trying to think through how that behaves. One thing is that it is a separate type for entities. We have a few options:
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Want to clarify that I am not opposed to a different separator char for version. We haven't officially GA'd distributed tracing v2, so we could update that spec with the new separator. But what one exactly? Some that I have ruled out myself in the past:
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Can we merge this PR first and then open a new issue to trace and update the separator after we have a final decision? Because today is my last day, I'm afraid that I can't continue to update the code in this PR because of permissions. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. |
||
} | ||
} | ||
} |
Uh oh!
There was an error while loading. Please reload this page.