Skip to content
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

Initial support for bulk operations and many other fixes / improvements #71

Merged
merged 46 commits into from
Mar 24, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
46 commits
Select commit Hold shift + click to select a range
1e8f69d
Merge pull request #8 from DynamicsValue/2x-dev
jordimontana82 Dec 2, 2021
221cdc4
Merge branch '2x-dev' into main
jordimontana82 Mar 21, 2022
b743d42
Merge pull request #14 from DynamicsValue/2x-dev
jordimontana82 May 29, 2022
e41b227
Merge pull request #21 from DynamicsValue/2x-dev
jordimontana82 Sep 7, 2022
eee8848
fixed DynamicsValue/fake-xrm-easy#64
BetimBeja Dec 23, 2022
414fcbd
fix for DynamicsValue/fake-xrm-easy#96
BetimBeja May 8, 2023
5b3e20f
Correctly Implemented Like Operator
BetimBeja May 28, 2023
331780c
chore fix linkedentity query
temmyraharjo Oct 25, 2023
62b9184
Validate EntityReferences with null logicalName on .Initialize()
BetimBeja Dec 3, 2023
a95dc78
adding initial support for bulk operations DynamicsValue/fake-xrm-eas…
jordimontana82 Mar 10, 2024
541102f
Limit bulk operations to v9 only DynamicsValue/fake-xrm-easy#122
jordimontana82 Mar 10, 2024
a81b180
Add remaining tests for create multiple DynamicsValue/fake-xrm-easy#122
jordimontana82 Mar 12, 2024
fc54ae5
Add conditional compilation for latest early bound generated types fr…
jordimontana82 Mar 12, 2024
b9e9309
More CreateMultiple tests, refactor alternate key unit tests
jordimontana82 Mar 14, 2024
8e056d6
Update previous alternate key tests to return new specific fault exce…
jordimontana82 Mar 14, 2024
564f6a9
Add support for UpdateMultipleRequest DynamicsValue/fake-xrm-easy#122…
jordimontana82 Mar 15, 2024
98ae63c
UpdateMultipleRequest only applies to v9
jordimontana82 Mar 15, 2024
ee6d829
Limit FileAttributeMetadata tests to v9 too
jordimontana82 Mar 15, 2024
4d351a6
Refactor common code in bulk operations
jordimontana82 Mar 15, 2024
b68a4ba
Adding UpsertMultipleRequest tests DynamicsValue/fake-xrm-easy#122 an…
jordimontana82 Mar 16, 2024
b7bfced
Adds remaining UpsertMultipleRequest tests DynamicsValue/fake-xrm-eas…
jordimontana82 Mar 16, 2024
bd38225
Limit UpsertMultipleRequests tests to v9
jordimontana82 Mar 16, 2024
9225f63
Resolve many code smells
jordimontana82 Mar 16, 2024
faba6b7
Fix other several code smells
jordimontana82 Mar 16, 2024
991ad16
Refactor xMultiple messages to use service.Create or service.Update m…
jordimontana82 Mar 16, 2024
6fffa32
Merge branch 'fix/broken-entityreference' of https://github.com/Betim…
jordimontana82 Mar 16, 2024
a0bd3e5
Resolves DynamicsValue/fake-xrm-easy#107, introduced user-defined exc…
jordimontana82 Mar 16, 2024
37e760a
Merge branch '2x-dev' of https://github.com/temmyraharjo/fake-xrm-eas…
jordimontana82 Mar 16, 2024
04ecb4d
Resolves DynamicsValue/fake-xrm-easy#63
jordimontana82 Mar 16, 2024
8b9eb86
Merge branch 'fix/like-operator' of https://github.com/BetimBeja/dyna…
jordimontana82 Mar 17, 2024
a675a48
Add further tests for DynamicsValue/fake-xrm-easy#139
jordimontana82 Mar 17, 2024
87471b3
Update formatting
jordimontana82 Mar 17, 2024
77d6cb9
Merge branch 'fix/values-in' of https://github.com/BetimBeja/dynamics…
jordimontana82 Mar 18, 2024
89ae113
Resolves DynamicsValue/fake-xrm-easy#96 and initial work on DynamicsV…
jordimontana82 Mar 23, 2024
4d0da5f
Limit the new tests to v9 only
jordimontana82 Mar 23, 2024
f775595
Cater for null values in OptionSetValueCollectionExtensions
jordimontana82 Mar 23, 2024
8cbfae9
remove unnecessary null checks
jordimontana82 Mar 23, 2024
a052d4b
Reduce cognitive complexity in MetadataGenerator
jordimontana82 Mar 23, 2024
627e16e
Replace string concatenation by StringBuilder DynamicsValue/fake-xrm-…
jordimontana82 Mar 23, 2024
ef3d714
Resolve various code smells
jordimontana82 Mar 23, 2024
5fcc630
Mark TypedCondictionExpression as internal, no need to expose it as a…
jordimontana82 Mar 23, 2024
1a9d452
Merge branch 'fix/2x/issue-64' of https://github.com/BetimBeja/dynami…
jordimontana82 Mar 24, 2024
02a2b4b
Resolves DynamicsValue/fake-xrm-easy#64, updat enamespace
jordimontana82 Mar 24, 2024
78ece40
Rename typos in method names, make all condition expression extension…
jordimontana82 Mar 24, 2024
7262257
Refactor TypeCastExpressions into separate files
jordimontana82 Mar 24, 2024
9080ad5
Increment abstractions package version to 2.5.0
jordimontana82 Mar 24, 2024
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
18 changes: 18 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,21 @@
## [2.5.0]

### Added

- Added FileAttributeMetadata support to MetadataGenerator
- Added support for bulk operations: CreateMultipleRequest, UpdateMultipleRequest, UpsertMultipleRequest - https://github.com/DynamicsValue/fake-xrm-easy/issues/122
- Added new exception to make the initialization of entity records with attributes with a null entity reference more obvious (thanks Betim) - https://github.com/DynamicsValue/fake-xrm-easy/issues/107
- Add support for OptionSetValueCollection attributes when they are generated as an IEnumerable<TEnum> (using EBG or pac modelbuilder) - https://github.com/DynamicsValue/fake-xrm-easy/issues/140
- Added extended wildcard support for the Like operator (thanks Betim) - https://github.com/DynamicsValue/fake-xrm-easy/issues/139

### Changed

- Resolves referencing EntityAlias or EntityName in conditions inside nested filters of a LinkedEntity (thanks Temmy) - https://github.com/DynamicsValue/fake-xrm-easy/issues/63
- Resolves Resolving entity references by Alternate Keys when EntityMetadata doesn't have any Keys. - https://github.com/DynamicsValue/fake-xrm-easy/issues/138
- Resolves an issue where a ConditionExpression with an In operator should to not take array of integers as an input, but instead separate values (thanks Ben and Betim) - https://github.com/DynamicsValue/fake-xrm-easy/issues/96
- Resolves filtering Money attributes by an integer value (thanks Ben and Betim) - https://github.com/DynamicsValue/fake-xrm-easy/issues/64


## [2.4.2]

### Added
Expand Down
2 changes: 1 addition & 1 deletion src/FakeXrmEasy.Core/Db/InMemoryDb.cs
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ namespace FakeXrmEasy.Core.Db
internal class InMemoryDb
{
/// <summary>
/// A collection of tables indexed by its logical name
/// A collection of tables indexed by their logical name
/// </summary>
protected internal Dictionary<string, InMemoryTable> _tables;

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
using System;
using Microsoft.Xrm.Sdk;

namespace FakeXrmEasy.Core.Exceptions
{
/// <summary>
/// Throws an exception when an entity record has an entity reference attribute with a null logical name
/// </summary>
public class NullLogicalNameEntityReferenceException: Exception
{
/// <summary>
/// Default constructor
/// </summary>
/// <param name="record">The entity record that has the invalid EntityReference attribute</param>
/// <param name="attributeLogicalName">The name of the attribute in the entity record with the invalid entity reference</param>
public NullLogicalNameEntityReferenceException(Entity record, string attributeLogicalName) :
base ($"The entity record with logical name '{record.LogicalName}' and Id '{record.Id}' has an entity reference attribute '{attributeLogicalName}' with a null logical name.")
{

}
}
}
23 changes: 22 additions & 1 deletion src/FakeXrmEasy.Core/Extensions/EntityExtensions.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
using Microsoft.Xrm.Sdk;
using Microsoft.Xrm.Sdk.Query;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using FakeXrmEasy.Abstractions;
Expand Down Expand Up @@ -212,7 +213,7 @@ public static Entity RemoveNullAttributes(Entity entity)
}

/// <summary>
///
/// Clones an attribute value to make sure the object reference in memory is different to the original attribute present in the In-Memory database
/// </summary>
/// <param name="attributeValue"></param>
/// <param name="context"></param>
Expand Down Expand Up @@ -311,6 +312,26 @@ internal static object CloneAttribute(object attributeValue, IXrmFakedContext co
var copy = new OptionSetValueCollection(original.ToArray());
return copy;
}
else if (typeof(IEnumerable).IsAssignableFrom(type))
{
if (type.IsGenericType)
{
var genericTypeArguments = type.GenericTypeArguments;
if (genericTypeArguments.Length == 1 && genericTypeArguments[0].IsEnum)
{
//MultiOption set value
return (attributeValue as IEnumerable).Copy();
}
}
else if (type.IsArray)
{
var elementType = type.GetElementType();
if (elementType.IsEnum)
{
return attributeValue.Copy();
}
}
}
#endif
else if (type == typeof(int) || type == typeof(Int64))
return attributeValue; //Not a reference type
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
#if FAKE_XRM_EASY_9

using System.Collections;
using System.Collections.Generic;
using System.Linq;
using Microsoft.Xrm.Sdk;
Expand All @@ -18,17 +19,21 @@ public static class OptionSetValueCollectionExtensions
/// <param name="input"></param>
/// <param name="isOptionSetValueCollectionAccepted"></param>
/// <returns></returns>
public static HashSet<int> ConvertToHashSetOfInt(object input, bool isOptionSetValueCollectionAccepted)
public static HashSet<int> ConvertToHashSetOfInt(this object input, bool isOptionSetValueCollectionAccepted)
{
if (input == null) return null;

var set = new HashSet<int>();

var faultReason = $"The formatter threw an exception while trying to deserialize the message: There was an error while trying to deserialize parameter" +
$" http://schemas.microsoft.com/xrm/2011/Contracts/Services:query. The InnerException message was 'Error in line 1 position 8295. Element " +
$"'http://schemas.microsoft.com/2003/10/Serialization/Arrays:anyType' contains data from a type that maps to the name " +
$"'http://schemas.microsoft.com/xrm/2011/Contracts:{input?.GetType()}'. The deserializer has no knowledge of any type that maps to this name. " +
$"'http://schemas.microsoft.com/xrm/2011/Contracts:{input.GetType()}'. The deserializer has no knowledge of any type that maps to this name. " +
$"Consider changing the implementation of the ResolveName method on your DataContractResolver to return a non-null value for name " +
$"'{input?.GetType()}' and namespace 'http://schemas.microsoft.com/xrm/2011/Contracts'.'. Please see InnerException for more details.";
$"'{input.GetType()}' and namespace 'http://schemas.microsoft.com/xrm/2011/Contracts'.'. Please see InnerException for more details.";

var type = input.GetType();

if (input is int)
{
set.Add((int)input);
Expand Down Expand Up @@ -74,6 +79,21 @@ public static HashSet<int> ConvertToHashSetOfInt(object input, bool isOptionSetV
{
set.UnionWith((input as OptionSetValueCollection).Select(osv => osv.Value));
}
else if (typeof(IEnumerable).IsAssignableFrom(type))
{
if (type.IsGenericType)
{
var genericTypeArguments = type.GenericTypeArguments;
if (genericTypeArguments.Length == 1 && genericTypeArguments[0].IsEnum)
{
var enumerator = (input as IEnumerable).GetEnumerator();
while (enumerator.MoveNext())
{
set.Add((int)enumerator.Current);
}
}
}
}
else
{
throw FakeOrganizationServiceFaultFactory.New(faultReason);
Expand Down
33 changes: 31 additions & 2 deletions src/FakeXrmEasy.Core/Extensions/TypeExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,17 @@ public static bool IsOptionSet(this Type t)
|| nullableType != null && nullableType.IsEnum;
}

/// <summary>
///
/// </summary>
/// <param name="t"></param>
/// <returns></returns>
public static bool IsMoney(this Type t)
{
var nullableType = Nullable.GetUnderlyingType(t);
return t == typeof(Money) || nullableType != null && nullableType == typeof(Money);
}

#if FAKE_XRM_EASY_9

/// <summary>
Expand All @@ -32,8 +43,26 @@ public static bool IsOptionSet(this Type t)
/// <returns></returns>
public static bool IsOptionSetValueCollection(this Type t)
{
var nullableType = Nullable.GetUnderlyingType(t);
return t == typeof(OptionSetValueCollection);
var isActualOptionSetValueCollection = t == typeof(OptionSetValueCollection);
var typeIsConvertibleToOptionSetValueCollection = false;
if (t.IsGenericType)
{
var genericTypeArguments = t.GenericTypeArguments;
if (genericTypeArguments.Length == 1 && genericTypeArguments[0].IsEnum)
{
typeIsConvertibleToOptionSetValueCollection = true;
}
}
else if (t.IsArray)
{
var elementType = t.GetElementType();
if (elementType.IsEnum)
{
typeIsConvertibleToOptionSetValueCollection = true;
}
}

return isActualOptionSetValueCollection || typeIsConvertibleToOptionSetValueCollection;
}
#endif

Expand Down
25 changes: 1 addition & 24 deletions src/FakeXrmEasy.Core/Extensions/XmlExtensionsForFetchXml.cs
Original file line number Diff line number Diff line change
Expand Up @@ -550,32 +550,9 @@ public static ConditionExpression ToConditionExpression(this XElement elem, IXrm
break;
case "like":
op = ConditionOperator.Like;

if (value != null)
{
if (value.StartsWith("%") && !value.EndsWith("%"))
op = ConditionOperator.EndsWith;
else if (!value.StartsWith("%") && value.EndsWith("%"))
op = ConditionOperator.BeginsWith;
else if (value.StartsWith("%") && value.EndsWith("%"))
op = ConditionOperator.Contains;

value = value.Replace("%", "");
}
break;
case "not-like":
op = ConditionOperator.NotLike;
if (value != null)
{
if (value.StartsWith("%") && !value.EndsWith("%"))
op = ConditionOperator.DoesNotEndWith;
else if (!value.StartsWith("%") && value.EndsWith("%"))
op = ConditionOperator.DoesNotBeginWith;
else if (value.StartsWith("%") && value.EndsWith("%"))
op = ConditionOperator.DoesNotContain;

value = value.Replace("%", "");
}
op = ConditionOperator.NotLike;
break;
case "gt":
op = ConditionOperator.GreaterThan;
Expand Down
18 changes: 9 additions & 9 deletions src/FakeXrmEasy.Core/FakeXrmEasy.Core.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
<TargetFrameworks Condition="'$(Configuration)'=='FAKE_XRM_EASY_2013'">net452</TargetFrameworks>
<TargetFrameworks Condition="'$(Configuration)'=='FAKE_XRM_EASY'">net452</TargetFrameworks>
<PackageId>FakeXrmEasy.Core</PackageId>
<VersionPrefix>2.4.2</VersionPrefix>
<VersionPrefix>2.5.0</VersionPrefix>
<Authors>Jordi Montaña</Authors>
<Company>Dynamics Value</Company>
<Title>FakeXrmEasy Core</Title>
Expand Down Expand Up @@ -57,8 +57,8 @@
</PropertyGroup>

<ItemGroup Condition="'$(TargetFramework)' == 'net462' And '$(Configuration)'=='FAKE_XRM_EASY_9'">
<PackageReference Include="Microsoft.CrmSdk.CoreAssemblies" Version="9.0.2.27" />
<PackageReference Include="Microsoft.CrmSdk.XrmTooling.CoreAssembly" Version="9.0.0.5" />
<PackageReference Include="Microsoft.CrmSdk.CoreAssemblies" Version="9.0.2.52" />
<PackageReference Include="Microsoft.CrmSdk.XrmTooling.CoreAssembly" Version="9.1.1.45" />
</ItemGroup>

<ItemGroup Condition="'$(TargetFramework)' == 'net462' And '$(Configuration)'=='FAKE_XRM_EASY_365'">
Expand Down Expand Up @@ -102,22 +102,22 @@
</ItemGroup>

<ItemGroup Condition="'$(Configuration)'=='FAKE_XRM_EASY'">
<PackageReference Include="FakeXrmEasy.Abstractions.v2011" Version="[2.4.0-*,3.0)" />
<PackageReference Include="FakeXrmEasy.Abstractions.v2011" Version="[2.5.0-*,3.0)" />
</ItemGroup>
<ItemGroup Condition="'$(Configuration)'=='FAKE_XRM_EASY_2013'">
<PackageReference Include="FakeXrmEasy.Abstractions.v2013" Version="[2.4.0-*,3.0)" />
<PackageReference Include="FakeXrmEasy.Abstractions.v2013" Version="[2.5.0-*,3.0)" />
</ItemGroup>
<ItemGroup Condition="'$(Configuration)'=='FAKE_XRM_EASY_2015'">
<PackageReference Include="FakeXrmEasy.Abstractions.v2015" Version="[2.4.0-*,3.0)" />
<PackageReference Include="FakeXrmEasy.Abstractions.v2015" Version="[2.5.0-*,3.0)" />
</ItemGroup>
<ItemGroup Condition="'$(Configuration)'=='FAKE_XRM_EASY_2016'">
<PackageReference Include="FakeXrmEasy.Abstractions.v2016" Version="[2.4.0-*,3.0)" />
<PackageReference Include="FakeXrmEasy.Abstractions.v2016" Version="[2.5.0-*,3.0)" />
</ItemGroup>
<ItemGroup Condition="'$(Configuration)'=='FAKE_XRM_EASY_365'">
<PackageReference Include="FakeXrmEasy.Abstractions.v365" Version="[2.4.0-*,3.0)" />
<PackageReference Include="FakeXrmEasy.Abstractions.v365" Version="[2.5.0-*,3.0)" />
</ItemGroup>
<ItemGroup Condition="'$(Configuration)'=='FAKE_XRM_EASY_9'">
<PackageReference Include="FakeXrmEasy.Abstractions.v9" Version="[2.4.0-*,3.0)" />
<PackageReference Include="FakeXrmEasy.Abstractions.v9" Version="[2.5.0-*,3.0)" />
</ItemGroup>

<Target Name="PreparePackageReleaseNotesFromFile" BeforeTargets="GenerateNuspec">
Expand Down
74 changes: 41 additions & 33 deletions src/FakeXrmEasy.Core/Metadata/MetadataGenerator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -192,7 +192,7 @@ private static T GetCustomAttribute<T>(MemberInfo member) where T : Attribute
return (T)Attribute.GetCustomAttribute(member, typeof(T));
}

private static AttributeMetadata CreateAttributeMetadata(Type propertyType)
internal static AttributeMetadata CreateAttributeMetadata(Type propertyType)
{
if (typeof(string) == propertyType)
{
Expand All @@ -212,7 +212,46 @@ private static AttributeMetadata CreateAttributeMetadata(Type propertyType)
}
else if (propertyType.IsGenericType)
{
Type genericType = propertyType.GetGenericArguments().FirstOrDefault();
return CreateAttributeMetadataFromGenericType(propertyType);
}
else if (typeof(BooleanManagedProperty) == propertyType)
{
var booleanManaged = new BooleanAttributeMetadata();
booleanManaged.SetSealedPropertyValue("AttributeType", AttributeTypeCode.ManagedProperty);
return booleanManaged;
}
#if !FAKE_XRM_EASY && !FAKE_XRM_EASY_2013
else if (typeof(Guid) == propertyType)
{
return new UniqueIdentifierAttributeMetadata();
}
#endif
#if !FAKE_XRM_EASY
else if (typeof(byte[]) == propertyType)
{

return new ImageAttributeMetadata();
}
#endif
#if FAKE_XRM_EASY_9
else if (typeof(OptionSetValueCollection).IsAssignableFrom(propertyType))
{
return new MultiSelectPicklistAttributeMetadata();
}
else if (typeof(object) == propertyType)
{
return new FileAttributeMetadata();
}
#endif
else
{
throw new Exception($"Type {propertyType.Name} has not been mapped to an AttributeMetadata.");
}
}

internal static AttributeMetadata CreateAttributeMetadataFromGenericType(Type propertyType)
{
Type genericType = propertyType.GetGenericArguments().FirstOrDefault();
if (propertyType.GetGenericTypeDefinition() == typeof(Nullable<>))
{
if (typeof(int) == genericType)
Expand Down Expand Up @@ -262,38 +301,7 @@ private static AttributeMetadata CreateAttributeMetadata(Type propertyType)
{
throw new Exception($"Type {propertyType.Name}{genericType?.Name} has not been mapped to an AttributeMetadata.");
}
}
else if (typeof(BooleanManagedProperty) == propertyType)
{
var booleanManaged = new BooleanAttributeMetadata();
booleanManaged.SetSealedPropertyValue("AttributeType", AttributeTypeCode.ManagedProperty);
return booleanManaged;
}
#if !FAKE_XRM_EASY && !FAKE_XRM_EASY_2013
else if (typeof(Guid) == propertyType)
{
return new UniqueIdentifierAttributeMetadata();
}
#endif
#if !FAKE_XRM_EASY
else if (typeof(byte[]) == propertyType)
{

return new ImageAttributeMetadata();
}
#endif
#if FAKE_XRM_EASY_9
else if (typeof(OptionSetValueCollection).IsAssignableFrom(propertyType))
{
return new MultiSelectPicklistAttributeMetadata();
}
#endif
else
{
throw new Exception($"Type {propertyType.Name} has not been mapped to an AttributeMetadata.");
}
}

private static OneToManyRelationshipMetadata CreateOneToManyRelationshipMetadata(Type referencingEntity,
PropertyInfo referencingAttribute,
Type referencedEntity,
Expand Down
Loading
Loading