Skip to content
New issue

Have a question about this project? # for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “#”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? # to your account

Replace obsolete AssociationAttribute with new EntityAssociationAttribute on client #509

Merged
Merged
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
6 changes: 3 additions & 3 deletions src/OpenRiaServices.Client/Framework/ChangeSetBuilder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -368,7 +368,7 @@ protected override void VisitEntityCollection(IEntityCollection entityCollection
/// </summary>
/// <param name="association">The association to check.</param>
/// <returns>The resulting collection of entries.</returns>
private IEnumerable<ChangeSetEntry> FindOriginalChildren(AssociationAttribute association)
private IEnumerable<ChangeSetEntry> FindOriginalChildren(EntityAssociationAttribute association)
{
foreach (ChangeSetEntry entry in this._changeSetEntries.Where(p => p.Entity.EntityState == EntityState.Deleted))
{
Expand Down Expand Up @@ -467,7 +467,7 @@ protected override void VisitEntityCollection(IEntityCollection entityCollection
// look for any invalid updates made to composed children
if (entityCollection.HasValues && member.IsComposition)
{
AssociationAttribute assoc = member.AssociationAttribute;
EntityAssociationAttribute assoc = member.AssociationAttribute;
foreach (Entity childEntity in entityCollection.Entities)
{
CheckInvalidChildUpdates(childEntity, assoc);
Expand Down Expand Up @@ -498,7 +498,7 @@ protected override void VisitEntityRef(IEntityRef entityRef, Entity parent, Meta
/// </summary>
/// <param name="entity">The child entity to check.</param>
/// <param name="compositionAttribute">The composition attribute.</param>
private static void CheckInvalidChildUpdates(Entity entity, AssociationAttribute compositionAttribute)
private static void CheckInvalidChildUpdates(Entity entity, EntityAssociationAttribute compositionAttribute)
{
if (compositionAttribute == null)
{
Expand Down
6 changes: 3 additions & 3 deletions src/OpenRiaServices.Client/Framework/Entity.cs
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ public abstract partial class Entity : IEditableObject, INotifyPropertyChanged,

private bool _trackChanges;
private Entity _parent;
private AssociationAttribute _parentAssociation;
private EntityAssociationAttribute _parentAssociation;
private bool _hasChildChanges;
private Dictionary<string, ComplexObject> _trackedInstances;
private MetaType _metaType;
Expand Down Expand Up @@ -86,7 +86,7 @@ internal Entity Parent
/// <summary>
/// Gets the parent association for this entity.
/// </summary>
internal AssociationAttribute ParentAssociation
internal EntityAssociationAttribute ParentAssociation
{
get
{
Expand Down Expand Up @@ -121,7 +121,7 @@ internal MetaType MetaType
/// </remarks>
/// <param name="parent">The parent.</param>
/// <param name="association">The parent association.</param>
internal void SetParent(Entity parent, AssociationAttribute association)
internal void SetParent(Entity parent, EntityAssociationAttribute association)
{
if (this._parent != parent)
{
Expand Down
69 changes: 69 additions & 0 deletions src/OpenRiaServices.Client/Framework/EntityAssociationAttribute.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
using System;
using System.Collections.Generic;

#pragma warning disable CS3015 // Type has no accessible constructors which use only CLS-compliant types

namespace OpenRiaServices
{
/// <summary>
/// Used to mark an Entity member as an association
/// </summary>
[AttributeUsage(AttributeTargets.Property, AllowMultiple = false, Inherited = true)]

public sealed class EntityAssociationAttribute : Attribute
#pragma warning restore CS3015 // Type has no accessible constructors which use only CLS-compliant types
{
/// <summary>
/// Full form of constructor
/// </summary>
/// <param name="name">The name of the association. For bi-directional associations,
/// the name must be the same on both sides of the association</param>
/// <param name="thisKey">List of the property names of the key values on this side of the association</param>
/// <param name="otherKey">List of the property names of the key values on the other side of the association</param>
public EntityAssociationAttribute(string name, string[] thisKey, string[] otherKey)
{
Name = name ?? throw new ArgumentNullException(nameof(name));
ThisKeyMembers = thisKey ?? throw new ArgumentNullException(nameof(thisKey));
OtherKeyMembers = otherKey ?? throw new ArgumentNullException(nameof(otherKey));

if (name .Length == 0)
throw new ArgumentException("Name cannot be empty", nameof(name));
if (thisKey.Length == 0)
throw new ArgumentException("ThisKey cannot be empty", nameof(thisKey));
if (otherKey.Length == 0)
throw new ArgumentException("OtherKey cannot be empty", nameof(otherKey));
}

/// <summary>
/// Gets the name of the association. For bi-directional associations, the name must
/// be the same on both sides of the association
/// </summary>
public string Name { get; }

/// <summary>
/// Gets or sets a value indicating whether this association member represents
/// the foreign key side of an association
/// </summary>
public bool IsForeignKey { get; set; }

/// <summary>
/// Gets the collection of individual key members specified in the ThisKey string.
/// </summary>
public IReadOnlyCollection<string> ThisKeyMembers { get; }

/// <summary>
/// Gets the collection of individual key members specified in the OtherKey string.
/// </summary>
public IReadOnlyCollection<string> OtherKeyMembers { get; }

/// <summary>
/// Gets or sets the key value on this side of the association
/// </summary>
public string ThisKey => string.Join(",", ThisKeyMembers);

/// <summary>
/// <see langword="string"/> representation of the key value on the other side of the association
/// </summary>
public string OtherKey => string.Join(",", OtherKeyMembers);
}
}
6 changes: 3 additions & 3 deletions src/OpenRiaServices.Client/Framework/EntityCollection.cs
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ public sealed class EntityCollection<TEntity> : IEntityCollection, IEntityCollec
private bool _entitiesLoaded;
private bool _entitiesAdded;

private AssociationAttribute AssocAttribute => _metaMember.AssociationAttribute;
private EntityAssociationAttribute AssocAttribute => _metaMember.AssociationAttribute;
private bool IsComposition => _metaMember.IsComposition;

/// <summary>
Expand Down Expand Up @@ -74,7 +74,7 @@ public EntityCollection(Entity parent, string memberName, Func<TEntity, bool> en
{
throw new ArgumentException(string.Format(CultureInfo.CurrentCulture, Resource.Property_Does_Not_Exist, parent.GetType(), memberName), nameof(memberName));
}
if (this._metaMember.AssociationAttribute == null)
if (!this._metaMember.IsAssociationMember)
{
throw new ArgumentException(string.Format(CultureInfo.CurrentCulture, Resource.MemberMustBeAssociation, memberName), nameof(memberName));
}
Expand Down Expand Up @@ -770,7 +770,7 @@ private void ResetLoadedEntities()
#endregion

#region IEntityCollection Members
AssociationAttribute IEntityCollection.Association
EntityAssociationAttribute IEntityCollection.Association
{
get
{
Expand Down
8 changes: 4 additions & 4 deletions src/OpenRiaServices.Client/Framework/EntityRef.cs
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ public sealed class EntityRef<TEntity> : IEntityRef where TEntity : Entity

private string MemberName => _metaMember.Name;
private bool IsComposition => _metaMember.IsComposition;
private AssociationAttribute AssocAttribute => _metaMember.AssociationAttribute;
private EntityAssociationAttribute AssocAttribute => _metaMember.AssociationAttribute;

/// <summary>
/// Initializes a new instance of the EntityRef class
Expand Down Expand Up @@ -58,7 +58,7 @@ public EntityRef(Entity parent, string memberName, Func<TEntity, bool> entityPre
{
throw new ArgumentException(string.Format(CultureInfo.CurrentCulture, Resource.Property_Does_Not_Exist, parent.GetType(), memberName), nameof(memberName));
}
if (this._metaMember.AssociationAttribute == null)
if (!this._metaMember.IsAssociationMember)
{
throw new ArgumentException(string.Format(CultureInfo.CurrentCulture, Resource.MemberMustBeAssociation, memberName), nameof(memberName));
}
Expand Down Expand Up @@ -393,7 +393,7 @@ private void ParentEntityPropertyChanged(object sender, PropertyChangedEventArgs
}

#region IEntityRef Members
AssociationAttribute IEntityRef.Association
EntityAssociationAttribute IEntityRef.Association
{
get
{
Expand Down Expand Up @@ -441,7 +441,7 @@ internal interface IEntityRef
/// <summary>
/// Gets the AssociationAttribute for this reference.
/// </summary>
AssociationAttribute Association
EntityAssociationAttribute Association
{
get;
}
Expand Down
6 changes: 3 additions & 3 deletions src/OpenRiaServices.Client/Framework/EntitySet.cs
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ namespace OpenRiaServices.Client
/// </summary>
public abstract class EntitySet : IEnumerable, ICollection, INotifyCollectionChanged, IRevertibleChangeTracking, INotifyPropertyChanged
{
private readonly Dictionary<AssociationAttribute, Action<Entity>> _associationUpdateCallbackMap = new Dictionary<AssociationAttribute, Action<Entity>>();
private readonly Dictionary<EntityAssociationAttribute, Action<Entity>> _associationUpdateCallbackMap = new();
private readonly Type _entityType;
private EntityContainer _entityContainer;
private EntitySetOperations _supportedOperations;
Expand Down Expand Up @@ -305,7 +305,7 @@ internal void UpdateRelatedAssociations(Entity entity, string propertyName)
/// <param name="association">AssociationAttribute indicating the association to monitor</param>
/// <param name="callback">The callback to call</param>
/// <param name="register">True if the callback is being registered, false if it is being unregistered</param>
internal void RegisterAssociationCallback(AssociationAttribute association, Action<Entity> callback, bool register)
internal void RegisterAssociationCallback(EntityAssociationAttribute association, Action<Entity> callback, bool register)
{
this._associationUpdateCallbackMap.TryGetValue(association, out Action<Entity> del);
if (register)
Expand Down Expand Up @@ -1426,7 +1426,7 @@ public int Add(object value)
int countBefore = this.Source.Count;
this.Source.Add(entity);

return this.Source.Count == countBefore + 1
return this.Source.Count == countBefore + 1
? countBefore
: ((List<T>)this.Source.List).IndexOf(entity, countBefore);
}
Expand Down
2 changes: 1 addition & 1 deletion src/OpenRiaServices.Client/Framework/IEntityCollection.cs
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ internal interface IEntityCollection
/// <summary>
/// Gets the AssociationAttribute for this collection.
/// </summary>
AssociationAttribute Association
EntityAssociationAttribute Association
{
get;
}
Expand Down
18 changes: 16 additions & 2 deletions src/OpenRiaServices.Client/Framework/Internal/MetaMember.cs
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,21 @@ internal MetaMember(MetaType metaType, PropertyInfo property, bool isRoundtripEn
IsKeyMember = TypeUtility.IsAttributeDefined(property, typeof(KeyAttribute), false);
IsComposition = TypeUtility.IsAttributeDefined(property, typeof(CompositionAttribute), false);

AssociationAttribute = (AssociationAttribute)property.GetCustomAttributes(typeof(AssociationAttribute), false).SingleOrDefault();
if (property.GetCustomAttribute<EntityAssociationAttribute>(false) is { } association)
{
this.AssociationAttribute = association;
}
#pragma warning disable CS0618 // Type or member is obsolete
// TODO: Remove fallback when code generation has used the never attribute for a little while
else if (property.GetCustomAttribute<AssociationAttribute>(false) is { } associationAttribute)
{
this.AssociationAttribute = new EntityAssociationAttribute(associationAttribute.Name, associationAttribute.ThisKeyMembers.ToArray(), associationAttribute.OtherKeyMembers.ToArray())
{
IsForeignKey = associationAttribute.IsForeignKey,
};
}
#pragma warning restore CS0618 // Type or member is obsolete

EditableAttribute = (EditableAttribute)property.GetCustomAttributes(typeof(EditableAttribute), false).SingleOrDefault();

IsRoundtripMember = CheckIfRoundtripMember(this, isRoundtripEntity);
Expand Down Expand Up @@ -88,7 +102,7 @@ internal MetaMember(MetaType metaType, PropertyInfo property, bool isRoundtripEn
/// Gets any <see cref="AssociationAttribute"/> applied to the property, or <c>null</c>
/// if no attribute is specified for the property
/// </summary>
public AssociationAttribute AssociationAttribute { get; }
public EntityAssociationAttribute AssociationAttribute { get; }

/// <summary>
/// Gets a value indicating whether this member is one of the supported values to send between
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,6 @@
<PropertyGroup>
<TargetFrameworks>net472;netstandard2.0;net6.0-windows</TargetFrameworks>

<!-- Disable obsolete warning (primarily AssociationAttribute) -->
<NoWarn Condition=" '$(TargetFramework)' != 'net472' ">$(NoWarn);CS0618</NoWarn>
<DefineConstants Condition=" '$(TargetFramework)' == 'net472' or '$(TargetFramework)' == 'net6.0-windows' ">$(DefineConstants);HAS_COLLECTIONVIEW</DefineConstants>
<UseWPF Condition=" '$(TargetFramework)' == 'net6.0-windows' ">true</UseWPF>
</PropertyGroup>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,18 +13,12 @@ public void TestDefaultDataAnnotationAttributeCtors()
{
Type[] _knownAttributeTypes = {
typeof(KeyAttribute),
typeof(AssociationAttribute),
typeof(ConcurrencyCheckAttribute),
typeof(TimestampAttribute)
};

foreach (Type t in _knownAttributeTypes)
{
if (t == typeof(AssociationAttribute)) {
// no default constructor defined
continue;
}

Attribute attr = null;
string message = string.Empty;
try
Expand All @@ -38,22 +32,5 @@ public void TestDefaultDataAnnotationAttributeCtors()
Assert.IsNotNull(attr, "Default ctor failed for attribute type " + t.GetType().Name + message);
}
}

[TestMethod]
public void TestAssociationAttribute()
{
AssociationAttribute attr = new AssociationAttribute("name", "thisKey", "otherKey");
attr.IsForeignKey = false;

Assert.AreEqual("name", attr.Name);
Assert.AreEqual("thisKey", attr.ThisKey);
Assert.AreEqual("otherKey", attr.OtherKey);
Assert.AreEqual(false, attr.IsForeignKey);

// Verify can reverse polarity of foreign key
attr.IsForeignKey = true;
Assert.AreEqual(true, attr.IsForeignKey);

}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,6 @@
<TargetFrameworks>net472;netstandard2.0;net6.0</TargetFrameworks>
<DefineConstants>$(DefineConstants);SERVERFX</DefineConstants>
</PropertyGroup>
<PropertyGroup Condition=" '$(TargetFramework)' != 'net472'">
<!-- Disable obsolete warning (primarily AssociationAttribute) -->
<NoWarn>$(NoWarn);CS0618</NoWarn>
</PropertyGroup>

<ItemGroup Condition="'$(TargetFramework)' == 'net472'">
<Reference Include="mscorlib" />
Expand All @@ -30,6 +26,7 @@
<Compile Include="..\..\OpenRiaServices.Client\Framework\BinaryTypeUtility.cs" Link="Data\BinaryTypeUtility.cs" />
<Compile Include="..\..\OpenRiaServices.Client\Framework\DomainException.cs" Link="Data\DomainException.cs" />
<Compile Include="..\..\OpenRiaServices.Client\Framework\DomainIdentifierAttribute.cs" Link="Data\DomainIdentifierAttribute.cs" />
<Compile Include="..\..\OpenRiaServices.Client\Framework\EntityAssociationAttribute.cs" Link="Data\EntityAssociationAttribute.cs" />
<Compile Include="..\..\OpenRiaServices.Client\Framework\ExternalReferenceAttribute.cs" Link="Data\ExternalAttribute.cs" />
<Compile Include="..\..\OpenRiaServices.Client\Framework\Polyfills.cs" Link="Data\Polyfills.cs" />
<Compile Include="..\..\OpenRiaServices.Client\Framework\RoundtripOriginalAttribute.cs" Link="Data\RoundtripOriginalAttribute.cs" />
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Diagnostics;
using System.Globalization;
using System.Linq;
using System.Runtime.Serialization;
using System.Text;
using OpenRiaServices.Server;

namespace OpenRiaServices.Tools.TextTemplate
Expand Down Expand Up @@ -57,6 +59,9 @@ internal class AttributeGeneratorHelper
{ typeof(EntityActionAttribute), null },
{ typeof(RequiresAuthenticationAttribute), null },
{ typeof(RequiresRoleAttribute), null },
// Translate all AssociationAttribute to EntityAssociationAttribute on the client
{ typeof(AssociationAttribute), new EntityAssociationAttributeBuilder() },
//{ typeof(EntityAssociationAttribute), new EntityAssociationAttributeBuilder() },
};

public static AttributeDeclaration GetAttributeDeclaration(Attribute attribute, ClientCodeGenerator textTemplateClientCodeGenerator, bool forcePropagation)
Expand Down Expand Up @@ -298,6 +303,25 @@ internal static string ConvertValueToCode(object value, bool isCSharp)
return CodeGenUtilities.GetTypeName(value.GetType()) + "." + value.ToString();
}

if (value is Array array)
{
Debug.Assert(isCSharp);

StringBuilder stringBuilder = new StringBuilder(200);

stringBuilder.Append($"new ");
stringBuilder.Append(CodeGenUtilities.GetTypeName(value.GetType().GetElementType()));
stringBuilder.Append("[] {");
for (int i=0; i < array.Length; i++)
{
if (i > 0)
stringBuilder.Append(", ");
stringBuilder.Append(ConvertValueToCode(array.GetValue(i), isCSharp));
}
stringBuilder.Append('}');
return stringBuilder.ToString();
}

return value.ToString();
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
<Compile Include="..\..\OpenRiaServices.Tools\Framework\MetadataPipeline\DisplayCustomAttributeBuilder.cs" Link="CSharpGenerators\AttributeGenerationHelpers\DisplayCustomAttributeBuilder.cs" />
<Compile Include="..\..\OpenRiaServices.Tools\Framework\MetadataPipeline\DomainIdentifierAttributeBuilder.cs" Link="CSharpGenerators\AttributeGenerationHelpers\DomainIdentifierAttributeBuilder.cs" />
<Compile Include="..\..\OpenRiaServices.Tools\Framework\MetadataPipeline\EditableAttributeBuilder.cs" Link="CSharpGenerators\AttributeGenerationHelpers\EditableAttributeBuilder.cs" />
<Compile Include="..\..\OpenRiaServices.Tools\Framework\MetadataPipeline\EntityAssociationAttributeBuilder.cs" Link="CSharpGenerators\AttributeGenerationHelpers\EntityAssociationAttributeBuilder.cs" />
<Compile Include="..\..\OpenRiaServices.Tools\Framework\MetadataPipeline\ICustomAttributeBuilder.cs" Link="CSharpGenerators\AttributeGenerationHelpers\ICustomAttributeBuilder.cs" />
<Compile Include="..\..\OpenRiaServices.Tools\Framework\MetadataPipeline\RangeCustomAttributeBuilder.cs" Link="CSharpGenerators\AttributeGenerationHelpers\RangeCustomAttributeBuilder.cs" />
<Compile Include="..\..\OpenRiaServices.Tools\Framework\MetadataPipeline\StandardCustomAttributeBuilder.cs" Link="CSharpGenerators\AttributeGenerationHelpers\StandardCustomAttributeBuilder.cs" />
Expand Down
Loading
Loading