From eee88482d42c5a582f1cc6ac9f16bad25a1ab40b Mon Sep 17 00:00:00 2001
From: Betim Beja <11160171+BetimBeja@users.noreply.github.com>
Date: Fri, 23 Dec 2022 15:04:57 +0100
Subject: [PATCH 01/37] fixed DynamicsValue/fake-xrm-easy#64
---
.../Extensions/TypeExtensions.cs | 11 ++++
.../Query/TypeCastExpressions.cs | 8 +++
.../FakeXrmEasy.Core.Tests/Issues/Issue64.cs | 54 +++++++++++++++++++
3 files changed, 73 insertions(+)
create mode 100644 tests/FakeXrmEasy.Core.Tests/Issues/Issue64.cs
diff --git a/src/FakeXrmEasy.Core/Extensions/TypeExtensions.cs b/src/FakeXrmEasy.Core/Extensions/TypeExtensions.cs
index fccd9ae3..10158e89 100644
--- a/src/FakeXrmEasy.Core/Extensions/TypeExtensions.cs
+++ b/src/FakeXrmEasy.Core/Extensions/TypeExtensions.cs
@@ -23,6 +23,17 @@ public static bool IsOptionSet(this Type t)
|| nullableType != null && nullableType.IsEnum;
}
+ ///
+ ///
+ ///
+ ///
+ ///
+ 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
public static bool IsOptionSetValueCollection(this Type t)
{
diff --git a/src/FakeXrmEasy.Core/Query/TypeCastExpressions.cs b/src/FakeXrmEasy.Core/Query/TypeCastExpressions.cs
index 60471560..b966a7b4 100644
--- a/src/FakeXrmEasy.Core/Query/TypeCastExpressions.cs
+++ b/src/FakeXrmEasy.Core/Query/TypeCastExpressions.cs
@@ -294,6 +294,14 @@ internal static Expression GetAppropiateTypedValueAndType(object value, Type att
return Expression.Constant(value, typeof(string)).ToCaseInsensitiveExpression();
}
}
+ else if (value is int)
+ {
+ if (attributeType.IsMoney())
+ {
+ return Expression.Constant(((int)value)*1m, typeof(decimal));
+ }
+ return Expression.Constant(value);
+ }
else if (value is EntityReference)
{
var cast = (value as EntityReference).Id;
diff --git a/tests/FakeXrmEasy.Core.Tests/Issues/Issue64.cs b/tests/FakeXrmEasy.Core.Tests/Issues/Issue64.cs
new file mode 100644
index 00000000..d37c6d1f
--- /dev/null
+++ b/tests/FakeXrmEasy.Core.Tests/Issues/Issue64.cs
@@ -0,0 +1,54 @@
+using Crm;
+using Microsoft.Xrm.Sdk;
+using Microsoft.Xrm.Sdk.Query;
+using System;
+using System.Collections.Generic;
+using Xunit;
+
+namespace FakeXrmEasy.Tests.Issues
+{
+ // https://github.com/DynamicsValue/fake-xrm-easy/issues/64
+
+ public class Issue64 : FakeXrmEasyTestsBase
+ {
+ SalesOrderDetail salesOrderDetail;
+ // setup
+ public Issue64()
+ {
+ _context.EnableProxyTypes(typeof(Account).Assembly);
+
+ salesOrderDetail = new SalesOrderDetail()
+ {
+ Id = Guid.NewGuid(),
+ PricePerUnit = new Money(1.0m)
+ };
+
+ _context.Initialize(new List { salesOrderDetail });
+ }
+
+ // This test currently fails
+ [Fact]
+ public void When_Querying_A_Money_Attribute_Using_An_Integer_Value_It_Should_Not_Fail()
+ {
+ var qe = new QueryExpression(SalesOrderDetail.EntityLogicalName);
+ qe.Criteria.AddCondition("priceperunit", ConditionOperator.Equal, 1);
+
+ var entities = _service.RetrieveMultiple(qe).Entities;
+
+ Assert.Equal(entities[0].Id, salesOrderDetail.Id);
+ }
+ // This test currently passes
+ [Fact]
+ public void When_Querying_A_Money_Attribute_Using_A_Money_Value_It_Should_Not_Fail()
+ {
+ var qe = new QueryExpression(SalesOrderDetail.EntityLogicalName);
+ qe.Criteria.AddCondition("priceperunit", ConditionOperator.Equal, new Money(1.0m));
+
+ var entities = _service.RetrieveMultiple(qe).Entities;
+
+ Assert.Single(entities);
+ Assert.Equal(entities[0].Id, salesOrderDetail.Id);
+ }
+
+ }
+}
\ No newline at end of file
From 414fcbd8ac247f245d462bfcc64f42fa0271c171 Mon Sep 17 00:00:00 2001
From: Betim Beja <11160171+BetimBeja@users.noreply.github.com>
Date: Mon, 8 May 2023 12:12:38 +0200
Subject: [PATCH 02/37] fix for DynamicsValue/fake-xrm-easy#96
---
.../Query/ConditionExpressionExtensions.In.cs | 12 ++++----
.../Query/ConditionExpressionExtensions.cs | 16 ++++++++---
.../MultiSelectOptionSetTests.cs | 28 ++++---------------
.../Issues/Issue0096.cs | 20 +++++++++++++
.../FakeXrmEasy.Core.Tests/Issues/Issue180.cs | 4 +--
5 files changed, 45 insertions(+), 35 deletions(-)
create mode 100644 tests/FakeXrmEasy.Core.Tests/Issues/Issue0096.cs
diff --git a/src/FakeXrmEasy.Core/Query/ConditionExpressionExtensions.In.cs b/src/FakeXrmEasy.Core/Query/ConditionExpressionExtensions.In.cs
index efd42786..39936b74 100644
--- a/src/FakeXrmEasy.Core/Query/ConditionExpressionExtensions.In.cs
+++ b/src/FakeXrmEasy.Core/Query/ConditionExpressionExtensions.In.cs
@@ -31,12 +31,12 @@ internal static Expression ToInExpression(this TypedConditionExpression tc, Expr
{
if (value is Array)
{
- foreach (var a in ((Array)value))
- {
- expOrValues = Expression.Or(expOrValues, Expression.Equal(
- tc.AttributeType.GetAppropiateCastExpressionBasedOnType(getAttributeValueExpr, a),
- TypeCastExpressions.GetAppropiateTypedValueAndType(a, tc.AttributeType)));
- }
+ //foreach (var a in ((Array)value))
+ //{
+ // expOrValues = Expression.Or(expOrValues, Expression.Equal(
+ // tc.AttributeType.GetAppropiateCastExpressionBasedOnType(getAttributeValueExpr, a),
+ // TypeCastExpressions.GetAppropiateTypedValueAndType(a, tc.AttributeType)));
+ //}
}
else
{
diff --git a/src/FakeXrmEasy.Core/Query/ConditionExpressionExtensions.cs b/src/FakeXrmEasy.Core/Query/ConditionExpressionExtensions.cs
index 7623ff7e..ce598cd8 100644
--- a/src/FakeXrmEasy.Core/Query/ConditionExpressionExtensions.cs
+++ b/src/FakeXrmEasy.Core/Query/ConditionExpressionExtensions.cs
@@ -207,10 +207,12 @@ internal static Expression ToExpression(this TypedConditionExpression c, QueryEx
break;
case ConditionOperator.In:
+ ValidateInConditionValues(c, entity.Name ?? qe.EntityName);
operatorExpression = c.ToInExpression(getNonBasicValueExpr, containsAttributeExpression);
break;
case ConditionOperator.NotIn:
+ ValidateInConditionValues(c, entity.Name ?? qe.EntityName);
operatorExpression = Expression.Not(c.ToInExpression(getNonBasicValueExpr, containsAttributeExpression));
break;
@@ -312,9 +314,15 @@ internal static Expression ToExpression(this TypedConditionExpression c, QueryEx
}
-
-
-
-
+ private static void ValidateInConditionValues(TypedConditionExpression c, string name)
+ {
+ foreach (object value in c.CondExpression.Values)
+ {
+ if (value is Array)
+ {
+ throw new Exception($"Condition for attribute '{name}.numberofemployees': expected argument(s) of a different type but received '{value.GetType()}'.");
+ }
+ }
+ }
}
}
diff --git a/tests/FakeXrmEasy.Core.Tests/FakeContextTests/TranslateQueryExpressionTests/OperatorTests/MultiSelectOptionSet/MultiSelectOptionSetTests.cs b/tests/FakeXrmEasy.Core.Tests/FakeContextTests/TranslateQueryExpressionTests/OperatorTests/MultiSelectOptionSet/MultiSelectOptionSetTests.cs
index 88db0e87..fa4bb9ae 100644
--- a/tests/FakeXrmEasy.Core.Tests/FakeContextTests/TranslateQueryExpressionTests/OperatorTests/MultiSelectOptionSet/MultiSelectOptionSetTests.cs
+++ b/tests/FakeXrmEasy.Core.Tests/FakeContextTests/TranslateQueryExpressionTests/OperatorTests/MultiSelectOptionSet/MultiSelectOptionSetTests.cs
@@ -243,9 +243,6 @@ public void When_executing_a_query_expression_in_operator_throws_exception_for_o
[Fact]
public void When_executing_a_query_expression_in_operator_returns_exact_matches_for_single_int_array_right_hand_side()
{
-
-
-
_service.Create(new Contact { FirstName = "1,2", new_MultiSelectAttribute = new OptionSetValueCollection() { new OptionSetValue(1), new OptionSetValue(2) } });
_service.Create(new Contact { FirstName = "2", new_MultiSelectAttribute = new OptionSetValueCollection() { new OptionSetValue(2) } });
_service.Create(new Contact { FirstName = "2,3", new_MultiSelectAttribute = new OptionSetValueCollection() { new OptionSetValue(2), new OptionSetValue(3) } });
@@ -254,7 +251,7 @@ public void When_executing_a_query_expression_in_operator_returns_exact_matches_
var qe = new QueryExpression("contact");
qe.ColumnSet = new ColumnSet(new[] { "firstname" });
- qe.Criteria.AddCondition("new_multiselectattribute", ConditionOperator.In, new[] { 2 });
+ qe.Criteria.AddCondition("new_multiselectattribute", ConditionOperator.In, new object[] { 2 });
var entities = _service.RetrieveMultiple(qe).Entities;
@@ -265,9 +262,6 @@ public void When_executing_a_query_expression_in_operator_returns_exact_matches_
[Fact]
public void When_executing_a_query_expression_in_operator_returns_exact_matches_for_int_array_right_hand_side()
{
-
-
-
_service.Create(new Contact { FirstName = "1,2", new_MultiSelectAttribute = new OptionSetValueCollection() { new OptionSetValue(1), new OptionSetValue(2) } });
_service.Create(new Contact { FirstName = "2", new_MultiSelectAttribute = new OptionSetValueCollection() { new OptionSetValue(2) } });
_service.Create(new Contact { FirstName = "2,3", new_MultiSelectAttribute = new OptionSetValueCollection() { new OptionSetValue(2), new OptionSetValue(3) } });
@@ -276,7 +270,7 @@ public void When_executing_a_query_expression_in_operator_returns_exact_matches_
var qe = new QueryExpression("contact");
qe.ColumnSet = new ColumnSet(new[] { "firstname" });
- qe.Criteria.AddCondition("new_multiselectattribute", ConditionOperator.In, new[] { 2, 3 });
+ qe.Criteria.AddCondition("new_multiselectattribute", ConditionOperator.In, new object[] { 2, 3 });
var entities = _service.RetrieveMultiple(qe).Entities;
@@ -287,9 +281,6 @@ public void When_executing_a_query_expression_in_operator_returns_exact_matches_
[Fact]
public void When_executing_a_query_expression_in_operator_returns_exact_matches_for_out_of_order_int_array_right_hand_side()
{
-
-
-
_service.Create(new Contact { FirstName = "1,2", new_MultiSelectAttribute = new OptionSetValueCollection() { new OptionSetValue(1), new OptionSetValue(2) } });
_service.Create(new Contact { FirstName = "2", new_MultiSelectAttribute = new OptionSetValueCollection() { new OptionSetValue(2) } });
_service.Create(new Contact { FirstName = "2,3", new_MultiSelectAttribute = new OptionSetValueCollection() { new OptionSetValue(2), new OptionSetValue(3) } });
@@ -298,7 +289,7 @@ public void When_executing_a_query_expression_in_operator_returns_exact_matches_
var qe = new QueryExpression("contact");
qe.ColumnSet = new ColumnSet(new[] { "firstname" });
- qe.Criteria.AddCondition("new_multiselectattribute", ConditionOperator.In, new[] { 3, 1, 2 });
+ qe.Criteria.AddCondition("new_multiselectattribute", ConditionOperator.In, new object[] { 3, 1, 2 });
var entities = _service.RetrieveMultiple(qe).Entities;
@@ -309,9 +300,6 @@ public void When_executing_a_query_expression_in_operator_returns_exact_matches_
[Fact]
public void When_executing_a_query_expression_in_operator_returns_exact_matches_for_out_of_order_int_params_right_hand_side()
{
-
-
-
_service.Create(new Contact { FirstName = "1,2", new_MultiSelectAttribute = new OptionSetValueCollection() { new OptionSetValue(1), new OptionSetValue(2) } });
_service.Create(new Contact { FirstName = "2", new_MultiSelectAttribute = new OptionSetValueCollection() { new OptionSetValue(2) } });
_service.Create(new Contact { FirstName = "2,3", new_MultiSelectAttribute = new OptionSetValueCollection() { new OptionSetValue(2), new OptionSetValue(3) } });
@@ -331,9 +319,6 @@ public void When_executing_a_query_expression_in_operator_returns_exact_matches_
[Fact]
public void When_executing_a_query_expression_in_operator_returns_exact_matches_for_string_array_right_hand_side()
{
-
-
-
_service.Create(new Contact { FirstName = "1,2", new_MultiSelectAttribute = new OptionSetValueCollection() { new OptionSetValue(1), new OptionSetValue(2) } });
_service.Create(new Contact { FirstName = "2", new_MultiSelectAttribute = new OptionSetValueCollection() { new OptionSetValue(2) } });
_service.Create(new Contact { FirstName = "2,3", new_MultiSelectAttribute = new OptionSetValueCollection() { new OptionSetValue(2), new OptionSetValue(3) } });
@@ -396,10 +381,7 @@ public void When_executing_a_query_expression_in_operator_returns_exact_matches_
[Fact]
public void When_executing_a_query_expression_notin_operator_excludes_exact_matches_for_int_array_right_hand_side()
- {
-
-
-
+ {
_service.Create(new Contact { FirstName = "1,2", new_MultiSelectAttribute = new OptionSetValueCollection() { new OptionSetValue(1), new OptionSetValue(2) } });
_service.Create(new Contact { FirstName = "2", new_MultiSelectAttribute = new OptionSetValueCollection() { new OptionSetValue(2) } });
_service.Create(new Contact { FirstName = "2,3", new_MultiSelectAttribute = new OptionSetValueCollection() { new OptionSetValue(2), new OptionSetValue(3) } });
@@ -408,7 +390,7 @@ public void When_executing_a_query_expression_notin_operator_excludes_exact_matc
var qe = new QueryExpression("contact");
qe.ColumnSet = new ColumnSet(new[] { "firstname" });
- qe.Criteria.AddCondition("new_multiselectattribute", ConditionOperator.NotIn, new[] { 2, 3 });
+ qe.Criteria.AddCondition("new_multiselectattribute", ConditionOperator.NotIn, new object[] { 2, 3 });
var entities = _service.RetrieveMultiple(qe).Entities;
diff --git a/tests/FakeXrmEasy.Core.Tests/Issues/Issue0096.cs b/tests/FakeXrmEasy.Core.Tests/Issues/Issue0096.cs
new file mode 100644
index 00000000..4f4ced0d
--- /dev/null
+++ b/tests/FakeXrmEasy.Core.Tests/Issues/Issue0096.cs
@@ -0,0 +1,20 @@
+using Microsoft.Xrm.Sdk.Query;
+using Xunit;
+
+namespace FakeXrmEasy.Tests.Issues
+{
+ public class Issue0096 : FakeXrmEasyTestsBase
+ {
+ [Fact]
+ public void Reproduce_issue_96()
+ {
+ var query = new QueryExpression("account");
+ query.TopCount = 2;
+ query.Criteria.AddCondition("numberofemployees", ConditionOperator.In, new int[] { 0, 1 });
+
+ var ex = Record.Exception(() => _service.RetrieveMultiple(query));
+ Assert.NotNull(ex);
+ Assert.Equal("Condition for attribute 'account.numberofemployees': expected argument(s) of a different type but received 'System.Int32[]'.", ex.Message);
+ }
+ }
+}
diff --git a/tests/FakeXrmEasy.Core.Tests/Issues/Issue180.cs b/tests/FakeXrmEasy.Core.Tests/Issues/Issue180.cs
index 2c45e03a..0ac100e1 100644
--- a/tests/FakeXrmEasy.Core.Tests/Issues/Issue180.cs
+++ b/tests/FakeXrmEasy.Core.Tests/Issues/Issue180.cs
@@ -21,7 +21,7 @@ public void When_a_query_on_lookup_with_condition_in_contains_a_match_it_should_
};
_context.Initialize(new List { account });
- var ids = new[] { account.OriginatingLeadId.Id, Guid.NewGuid(), Guid.NewGuid() };
+ var ids = new object[] { account.OriginatingLeadId.Id, Guid.NewGuid(), Guid.NewGuid() };
var qe = new QueryExpression(Account.EntityLogicalName);
qe.Criteria.AddCondition("originatingleadid", ConditionOperator.In, ids);
@@ -44,7 +44,7 @@ public void When_a_query_on_lookup_with_condition_in_contains_no_match_it_should
_context.Initialize(new List { account });
- var ids = new[] { Guid.Empty, Guid.Empty, Guid.Empty };
+ var ids = new object[] { Guid.Empty, Guid.Empty, Guid.Empty };
var qe = new QueryExpression(Account.EntityLogicalName);
qe.Criteria.AddCondition("originatingleadid", ConditionOperator.In, ids);
From 5b3e20f446b1363b44351cd77b4b90ff2c491ee3 Mon Sep 17 00:00:00 2001
From: Betim Beja <11160171+BetimBeja@users.noreply.github.com>
Date: Sun, 28 May 2023 11:16:37 +0200
Subject: [PATCH 03/37] Correctly Implemented Like Operator
---
.../Extensions/XmlExtensionsForFetchXml.cs | 25 +---
...onditionExpressionExtensions.BeginsWith.cs | 22 +++
.../ConditionExpressionExtensions.Like.cs | 50 ++++---
.../Query/ConditionExpressionExtensions.cs | 14 +-
.../Query/FetchXml/ConditionOperatorTests.cs | 24 +--
.../Strings/StringOperatorTests.cs | 140 ++++++++++++++++++
6 files changed, 220 insertions(+), 55 deletions(-)
create mode 100644 src/FakeXrmEasy.Core/Query/ConditionExpressionExtensions.BeginsWith.cs
diff --git a/src/FakeXrmEasy.Core/Extensions/XmlExtensionsForFetchXml.cs b/src/FakeXrmEasy.Core/Extensions/XmlExtensionsForFetchXml.cs
index 8668d083..fc864aa8 100644
--- a/src/FakeXrmEasy.Core/Extensions/XmlExtensionsForFetchXml.cs
+++ b/src/FakeXrmEasy.Core/Extensions/XmlExtensionsForFetchXml.cs
@@ -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;
diff --git a/src/FakeXrmEasy.Core/Query/ConditionExpressionExtensions.BeginsWith.cs b/src/FakeXrmEasy.Core/Query/ConditionExpressionExtensions.BeginsWith.cs
new file mode 100644
index 00000000..ad76a3fb
--- /dev/null
+++ b/src/FakeXrmEasy.Core/Query/ConditionExpressionExtensions.BeginsWith.cs
@@ -0,0 +1,22 @@
+using System.Linq;
+using System.Linq.Expressions;
+using Microsoft.Xrm.Sdk.Query;
+
+namespace FakeXrmEasy.Query
+{
+ public static partial class ConditionExpressionExtensions
+ {
+ internal static Expression ToBeginsWithExpression(this TypedConditionExpression tc, Expression getAttributeValueExpr, Expression containsAttributeExpr)
+ {
+ var c = tc.CondExpression;
+
+ //Append a ´%´ at the end of each condition value
+ var computedCondition = new ConditionExpression(c.AttributeName, c.Operator, c.Values.Select(x => x.ToString() + "%").ToList());
+ var typedComputedCondition = new TypedConditionExpression(computedCondition);
+ typedComputedCondition.AttributeType = tc.AttributeType;
+
+ // Perhaps we are introducing some problems by converting a StartsWith to a Like Operator?
+ return typedComputedCondition.ToLikeExpression(getAttributeValueExpr, containsAttributeExpr);
+ }
+ }
+}
diff --git a/src/FakeXrmEasy.Core/Query/ConditionExpressionExtensions.Like.cs b/src/FakeXrmEasy.Core/Query/ConditionExpressionExtensions.Like.cs
index 67ad74f8..a3194ec2 100644
--- a/src/FakeXrmEasy.Core/Query/ConditionExpressionExtensions.Like.cs
+++ b/src/FakeXrmEasy.Core/Query/ConditionExpressionExtensions.Like.cs
@@ -1,6 +1,6 @@
using System;
using System.Linq.Expressions;
-
+using System.Text.RegularExpressions;
namespace FakeXrmEasy.Query
{
@@ -9,37 +9,51 @@ public static partial class ConditionExpressionExtensions
internal static Expression ToLikeExpression(this TypedConditionExpression tc, Expression getAttributeValueExpr, Expression containsAttributeExpr)
{
var c = tc.CondExpression;
-
BinaryExpression expOrValues = Expression.Or(Expression.Constant(false), Expression.Constant(false));
Expression convertedValueToStr = Expression.Convert(tc.AttributeType.GetAppropiateCastExpressionBasedOnType(getAttributeValueExpr, c.Values[0]), typeof(string));
Expression convertedValueToStrAndToLower = convertedValueToStr.ToCaseInsensitiveExpression();
- string sLikeOperator = "%";
+
foreach (object value in c.Values)
{
- var strValue = value.ToString();
- string sMethod = "";
-
- if (strValue.EndsWith(sLikeOperator) && strValue.StartsWith(sLikeOperator))
- sMethod = "Contains";
-
- else if (strValue.StartsWith(sLikeOperator))
- sMethod = "EndsWith";
-
- else
- sMethod = "StartsWith";
+ //convert a like into a regular expression
+ string input = value.ToString();
+ string result = "^";
+ int lastMatch = 0;
+ var regex = new Regex("([^\\[]*)(\\[[^\\]]*\\])", RegexOptions.IgnoreCase | RegexOptions.CultureInvariant);
+ foreach (Match match in regex.Matches(input))
+ {
+ if (match.Groups[1].Success)
+ {
+ result += ConvertToRegexDefinition(match.Groups[1].Value);
+ }
+ result += match.Groups[2].Value.Replace("\\", "\\\\");
+ lastMatch = match.Index + match.Length;
+ }
+ if (input.Length != lastMatch)
+ {
+ result += ConvertToRegexDefinition(input.Substring(lastMatch));
+ }
+ result += "$";
+
+ regex = new Regex(result, RegexOptions.IgnoreCase | RegexOptions.CultureInvariant);
expOrValues = Expression.Or(expOrValues, Expression.Call(
- convertedValueToStrAndToLower,
- typeof(string).GetMethod(sMethod, new Type[] { typeof(string) }),
- Expression.Constant(value.ToString().ToLowerInvariant().Replace("%", "")) //Linq2CRM adds the percentage value to be executed as a LIKE operator, here we are replacing it to just use the appropiate method
- ));
+ Expression.Constant(regex),
+ typeof(Regex).GetMethod("IsMatch", new Type[] { typeof(string) }),
+ convertedValueToStrAndToLower) //Linq2CRM adds the percentage value to be executed as a LIKE operator, here we are replacing it to just use the appropiate method
+ );
}
return Expression.AndAlso(
containsAttributeExpr,
expOrValues);
}
+
+ private static string ConvertToRegexDefinition(string value)
+ {
+ return value.Replace("\\", "\\\\").Replace("(", "\\(").Replace("{", "\\{").Replace(".", "\\.").Replace("*", "\\*").Replace("+", "\\+").Replace("?", "\\?").Replace("%", ".*").Replace("_", ".");
+ }
}
}
diff --git a/src/FakeXrmEasy.Core/Query/ConditionExpressionExtensions.cs b/src/FakeXrmEasy.Core/Query/ConditionExpressionExtensions.cs
index 7623ff7e..fb62b22b 100644
--- a/src/FakeXrmEasy.Core/Query/ConditionExpressionExtensions.cs
+++ b/src/FakeXrmEasy.Core/Query/ConditionExpressionExtensions.cs
@@ -159,6 +159,9 @@ internal static Expression ToExpression(this TypedConditionExpression c, QueryEx
break;
case ConditionOperator.BeginsWith:
+ operatorExpression = c.ToBeginsWithExpression(getNonBasicValueExpr, containsAttributeExpression);
+ break;
+
case ConditionOperator.Like:
operatorExpression = c.ToLikeExpression(getNonBasicValueExpr, containsAttributeExpression);
break;
@@ -176,12 +179,21 @@ internal static Expression ToExpression(this TypedConditionExpression c, QueryEx
break;
case ConditionOperator.DoesNotBeginWith:
+ operatorExpression = Expression.Not(c.ToBeginsWithExpression(getNonBasicValueExpr, containsAttributeExpression));
+ break;
+
case ConditionOperator.DoesNotEndWith:
+ operatorExpression = Expression.Not(c.ToEndsWithExpression(getNonBasicValueExpr, containsAttributeExpression));
+ break;
+
case ConditionOperator.NotLike:
- case ConditionOperator.DoesNotContain:
operatorExpression = Expression.Not(c.ToLikeExpression(getNonBasicValueExpr, containsAttributeExpression));
break;
+ case ConditionOperator.DoesNotContain:
+ operatorExpression = Expression.Not(c.ToContainsExpression(getNonBasicValueExpr, containsAttributeExpression));
+ break;
+
case ConditionOperator.Null:
operatorExpression = c.ToNullExpression(getNonBasicValueExpr, containsAttributeExpression);
break;
diff --git a/tests/FakeXrmEasy.Core.Tests/Query/FetchXml/ConditionOperatorTests.cs b/tests/FakeXrmEasy.Core.Tests/Query/FetchXml/ConditionOperatorTests.cs
index 3b04970d..be9ea79b 100644
--- a/tests/FakeXrmEasy.Core.Tests/Query/FetchXml/ConditionOperatorTests.cs
+++ b/tests/FakeXrmEasy.Core.Tests/Query/FetchXml/ConditionOperatorTests.cs
@@ -217,8 +217,8 @@ public void FetchXml_Operator_Like()
Assert.True(query.Criteria != null);
Assert.Single(query.Criteria.Conditions);
Assert.Equal("fullname", query.Criteria.Conditions[0].AttributeName);
- Assert.Equal(ConditionOperator.Contains, query.Criteria.Conditions[0].Operator);
- Assert.Equal("Messi", query.Criteria.Conditions[0].Values[0].ToString());
+ Assert.Equal(ConditionOperator.Like, query.Criteria.Conditions[0].Operator);
+ Assert.Equal("%Messi%", query.Criteria.Conditions[0].Values[0].ToString());
}
[Fact]
@@ -241,8 +241,8 @@ public void FetchXml_Operator_Like_As_BeginsWith()
Assert.True(query.Criteria != null);
Assert.Single(query.Criteria.Conditions);
Assert.Equal("fullname", query.Criteria.Conditions[0].AttributeName);
- Assert.Equal(ConditionOperator.BeginsWith, query.Criteria.Conditions[0].Operator);
- Assert.Equal("Messi", query.Criteria.Conditions[0].Values[0].ToString());
+ Assert.Equal(ConditionOperator.Like, query.Criteria.Conditions[0].Operator);
+ Assert.Equal("Messi%", query.Criteria.Conditions[0].Values[0].ToString());
}
[Fact]
@@ -313,8 +313,8 @@ public void FetchXml_Operator_Like_As_EndsWith()
Assert.True(query.Criteria != null);
Assert.Single(query.Criteria.Conditions);
Assert.Equal("fullname", query.Criteria.Conditions[0].AttributeName);
- Assert.Equal(ConditionOperator.EndsWith, query.Criteria.Conditions[0].Operator);
- Assert.Equal("Messi", query.Criteria.Conditions[0].Values[0].ToString());
+ Assert.Equal(ConditionOperator.Like, query.Criteria.Conditions[0].Operator);
+ Assert.Equal("%Messi", query.Criteria.Conditions[0].Values[0].ToString());
}
[Fact]
@@ -385,8 +385,8 @@ public void FetchXml_Operator_NotLike()
Assert.True(query.Criteria != null);
Assert.Single(query.Criteria.Conditions);
Assert.Equal("fullname", query.Criteria.Conditions[0].AttributeName);
- Assert.Equal(ConditionOperator.DoesNotContain, query.Criteria.Conditions[0].Operator);
- Assert.Equal("Messi", query.Criteria.Conditions[0].Values[0].ToString());
+ Assert.Equal(ConditionOperator.NotLike, query.Criteria.Conditions[0].Operator);
+ Assert.Equal("%Messi%", query.Criteria.Conditions[0].Values[0].ToString());
}
[Fact]
@@ -409,8 +409,8 @@ public void FetchXml_Operator_NotLike_As_Not_BeginWith()
Assert.True(query.Criteria != null);
Assert.Single(query.Criteria.Conditions);
Assert.Equal("fullname", query.Criteria.Conditions[0].AttributeName);
- Assert.Equal(ConditionOperator.DoesNotBeginWith, query.Criteria.Conditions[0].Operator);
- Assert.Equal("Messi", query.Criteria.Conditions[0].Values[0].ToString());
+ Assert.Equal(ConditionOperator.NotLike, query.Criteria.Conditions[0].Operator);
+ Assert.Equal("Messi%", query.Criteria.Conditions[0].Values[0].ToString());
}
[Fact]
@@ -433,8 +433,8 @@ public void FetchXml_Operator_NotLike_As_Not_EndWith()
Assert.True(query.Criteria != null);
Assert.Single(query.Criteria.Conditions);
Assert.Equal("fullname", query.Criteria.Conditions[0].AttributeName);
- Assert.Equal(ConditionOperator.DoesNotEndWith, query.Criteria.Conditions[0].Operator);
- Assert.Equal("Messi", query.Criteria.Conditions[0].Values[0].ToString());
+ Assert.Equal(ConditionOperator.NotLike, query.Criteria.Conditions[0].Operator);
+ Assert.Equal("%Messi", query.Criteria.Conditions[0].Values[0].ToString());
}
[Fact]
diff --git a/tests/FakeXrmEasy.Core.Tests/Query/FetchXml/OperatorTests/Strings/StringOperatorTests.cs b/tests/FakeXrmEasy.Core.Tests/Query/FetchXml/OperatorTests/Strings/StringOperatorTests.cs
index 9318a7e2..a1a9ec7f 100644
--- a/tests/FakeXrmEasy.Core.Tests/Query/FetchXml/OperatorTests/Strings/StringOperatorTests.cs
+++ b/tests/FakeXrmEasy.Core.Tests/Query/FetchXml/OperatorTests/Strings/StringOperatorTests.cs
@@ -5,6 +5,7 @@
using System.Reflection;
using Xunit;
using FakeXrmEasy.Query;
+using System.Linq;
namespace FakeXrmEasy.Tests.FakeContextTests.FetchXml.OperatorTests.Strings
{
@@ -111,5 +112,144 @@ public void FetchXml_Operator_Gt_Execution()
Assert.Equal("Bob", collection.Entities[0]["nickname"]);
Assert.Equal("Nati", collection.Entities[1]["nickname"]);
}
+
+ [Fact]
+ public void FetchXml_Operator_BeginsWith_Execution()
+ {
+ var fetchXml = @"
+
+
+
+
+
+
+ ";
+
+ var ct1 = new Contact() { Id = Guid.NewGuid(), NickName = "Alice" };
+ var ct2 = new Contact() { Id = Guid.NewGuid(), NickName = "Bob" };
+ var ct3 = new Contact() { Id = Guid.NewGuid(), NickName = "Nati" };
+ _context.Initialize(new[] { ct1, ct2, ct3 });
+
+ var collection = _service.RetrieveMultiple(new FetchExpression(fetchXml));
+
+ Assert.Equal(1, collection.Entities.Count);
+ Assert.Equal("Alice", collection.Entities[0]["nickname"]);
+ }
+
+ [Fact]
+ public void FetchXml_Operator_DoesNotBeginWith_Execution()
+ {
+ var fetchXml = @"
+
+
+
+
+
+
+ ";
+
+ var ct1 = new Contact() { Id = Guid.NewGuid(), NickName = "Alice" };
+ var ct2 = new Contact() { Id = Guid.NewGuid(), NickName = "Bob" };
+ var ct3 = new Contact() { Id = Guid.NewGuid(), NickName = "Nati" };
+ _context.Initialize(new[] { ct1, ct2, ct3 });
+
+ var collection = _service.RetrieveMultiple(new FetchExpression(fetchXml));
+
+ Assert.Equal(2, collection.Entities.Count);
+ Assert.DoesNotContain("Alice", collection.Entities.Select(e => e["nickname"]));
+ }
+
+ [Fact]
+ public void FetchXml_Operator_EndsWith_Execution()
+ {
+ var fetchXml = @"
+
+
+
+
+
+
+ ";
+
+ var ct1 = new Contact() { Id = Guid.NewGuid(), NickName = "Alice" };
+ var ct2 = new Contact() { Id = Guid.NewGuid(), NickName = "Bob" };
+ var ct3 = new Contact() { Id = Guid.NewGuid(), NickName = "Nati" };
+ _context.Initialize(new[] { ct1, ct2, ct3 });
+
+ var collection = _service.RetrieveMultiple(new FetchExpression(fetchXml));
+
+ Assert.Equal(1, collection.Entities.Count);
+ Assert.Equal("Alice", collection.Entities[0]["nickname"]);
+ }
+
+ [Fact]
+ public void FetchXml_Operator_DoesNotEndWith_Execution()
+ {
+ var fetchXml = @"
+
+
+
+
+
+
+ ";
+
+ var ct1 = new Contact() { Id = Guid.NewGuid(), NickName = "Alice" };
+ var ct2 = new Contact() { Id = Guid.NewGuid(), NickName = "Bob" };
+ var ct3 = new Contact() { Id = Guid.NewGuid(), NickName = "Nati" };
+ _context.Initialize(new[] { ct1, ct2, ct3 });
+
+ var collection = _service.RetrieveMultiple(new FetchExpression(fetchXml));
+
+ Assert.Equal(2, collection.Entities.Count);
+ Assert.DoesNotContain("Alice", collection.Entities.Select(e => e["nickname"]));
+ }
+
+
+ [Fact]
+ public void FetchXml_Operator_Like_Execution()
+ {
+ var fetchXml = @"
+
+
+
+
+
+
+ ";
+
+ var ct1 = new Contact() { Id = Guid.NewGuid(), NickName = "Alice" };
+ var ct2 = new Contact() { Id = Guid.NewGuid(), NickName = "Bob" };
+ var ct3 = new Contact() { Id = Guid.NewGuid(), NickName = "Nati" };
+ _context.Initialize(new[] { ct1, ct2, ct3 });
+
+ var collection = _service.RetrieveMultiple(new FetchExpression(fetchXml));
+
+ Assert.Equal(1, collection.Entities.Count);
+ Assert.Equal("Alice", collection.Entities[0]["nickname"]);
+ }
+
+ [Fact]
+ public void FetchXml_Operator_NotLike_Execution()
+ {
+ var fetchXml = @"
+
+
+
+
+
+
+ ";
+
+ var ct1 = new Contact() { Id = Guid.NewGuid(), NickName = "Alice" };
+ var ct2 = new Contact() { Id = Guid.NewGuid(), NickName = "Bob" };
+ var ct3 = new Contact() { Id = Guid.NewGuid(), NickName = "Nati" };
+ _context.Initialize(new[] { ct1, ct2, ct3 });
+
+ var collection = _service.RetrieveMultiple(new FetchExpression(fetchXml));
+
+ Assert.Equal(2, collection.Entities.Count);
+ Assert.DoesNotContain("Alice", collection.Entities.Select(e=> e["nickname"]));
+ }
}
}
From 331780cbd5d65589c63a4ab128fbd779358b0172 Mon Sep 17 00:00:00 2001
From: "temmy.raharjo"
Date: Wed, 25 Oct 2023 10:55:32 +0800
Subject: [PATCH 04/37] chore fix linkedentity query
---
.../Query/LinkEntityQueryExtensions.cs | 9 ++
.../FakeXrmEasy.Core.Tests/Issues/Issue63.cs | 94 +++++++++++++++++++
2 files changed, 103 insertions(+)
create mode 100644 tests/FakeXrmEasy.Core.Tests/Issues/Issue63.cs
diff --git a/src/FakeXrmEasy.Core/Query/LinkEntityQueryExtensions.cs b/src/FakeXrmEasy.Core/Query/LinkEntityQueryExtensions.cs
index d00503b2..68af9083 100644
--- a/src/FakeXrmEasy.Core/Query/LinkEntityQueryExtensions.cs
+++ b/src/FakeXrmEasy.Core/Query/LinkEntityQueryExtensions.cs
@@ -169,6 +169,15 @@ internal static List TranslateLinkedEntityFilterExpressionToExpressi
var entityAlias = !string.IsNullOrEmpty(le.EntityAlias) ? le.EntityAlias : le.LinkToEntityName;
ce.AttributeName = entityAlias + "." + ce.AttributeName;
}
+
+ foreach (var currentFe in fe.Filters)
+ {
+ foreach (var ce in currentFe.Conditions)
+ {
+ var entityAlias = !string.IsNullOrEmpty(le.EntityAlias) ? le.EntityAlias : le.LinkToEntityName;
+ ce.AttributeName = entityAlias + "." + ce.AttributeName;
+ }
+ }
}
}
diff --git a/tests/FakeXrmEasy.Core.Tests/Issues/Issue63.cs b/tests/FakeXrmEasy.Core.Tests/Issues/Issue63.cs
new file mode 100644
index 00000000..a12ab18c
--- /dev/null
+++ b/tests/FakeXrmEasy.Core.Tests/Issues/Issue63.cs
@@ -0,0 +1,94 @@
+using Crm;
+using Microsoft.Xrm.Sdk;
+using Microsoft.Xrm.Sdk.Query;
+using System;
+using FakeXrmEasy.Query;
+using Xunit;
+
+namespace FakeXrmEasy.Core.Tests.Issues
+{
+ // https://github.com/DynamicsValue/fake-xrm-easy/issues/63
+
+ public class Issue63 : FakeXrmEasyTestsBase
+ {
+ // setup
+ public Issue63()
+ {
+ _context.EnableProxyTypes(typeof(Account).Assembly);
+
+ var order = new SalesOrder() { Id = Guid.NewGuid() };
+
+ var uom = new UoM()
+ {
+ Id = Guid.NewGuid(),
+ Name = "KG"
+ };
+ var detail1 = new SalesOrderDetail()
+ {
+ Id = Guid.NewGuid(),
+ SalesOrderId = order.ToEntityReference(),
+ Quantity = 1,
+ ProductDescription = "A",
+ Description = "B",
+ UoMId = uom.ToEntityReference()
+ };
+ var detail2 = new SalesOrderDetail()
+ {
+ Id = Guid.NewGuid(),
+ SalesOrderId = order.ToEntityReference(),
+ Quantity = 1,
+ ProductDescription = "C",
+ Description = "D",
+ UoMId = uom.ToEntityReference()
+ };
+ var detail3 = new SalesOrderDetail()
+ {
+ Id = Guid.NewGuid(),
+ SalesOrderId = order.ToEntityReference(),
+ Quantity = 1,
+ ProductDescription = "E",
+ Description = "F",
+ UoMId = uom.ToEntityReference()
+ };
+
+ _context.Initialize(new Entity[] { order, detail1, detail2, detail3, uom });
+ }
+
+ // This test currently fails
+ [Fact]
+ public void When_A_QueryExpression_Contains_A_Complex_subquery_On_A_Link_Entity_It_Should_Return_The_Right_Records()
+ {
+ var query = new QueryExpression("salesorder");
+ // link to salesorderdetail
+ var query_salesorderdetail = query.AddLink("salesorderdetail", "salesorderid", "salesorderid");
+ // Quantity not null
+ query_salesorderdetail.LinkCriteria.AddCondition("quantity", ConditionOperator.NotNull);
+ var query_currency =
+ query_salesorderdetail.AddLink("uom", "uomid", "uomid");
+ query_currency.LinkCriteria.AddCondition("name", ConditionOperator.Equal, "KG");
+
+ // Add an 'Or' filter
+ var orFilter = new FilterExpression();
+ query_salesorderdetail.LinkCriteria.AddFilter(orFilter);
+ orFilter.FilterOperator = LogicalOperator.Or;
+
+ // Filter with two Ands - A and B - should find detail1
+ var aAndBFilter = new FilterExpression();
+ aAndBFilter.AddCondition("productdescription", ConditionOperator.Equal, "A");
+ aAndBFilter.AddCondition("description", ConditionOperator.Equal, "B");
+
+ // Filter with two Ands - C and D - should find detail2
+ var cAndDFilter = new FilterExpression();
+ cAndDFilter.AddCondition("productdescription", ConditionOperator.Equal, "C");
+ cAndDFilter.AddCondition("description", ConditionOperator.Equal, "D");
+
+ // Add the two and filters to the Or Filter
+ orFilter.AddFilter(aAndBFilter);
+ orFilter.AddFilter(cAndDFilter);
+
+ var records = _service.RetrieveMultiple(query);
+
+ Assert.Equal(2, records.Entities.Count);
+ }
+ }
+}
\ No newline at end of file
From 62b91842e37d29a46dec834668ee69c4fa38bbe0 Mon Sep 17 00:00:00 2001
From: Betim Beja <11160171+BetimBeja@users.noreply.github.com>
Date: Sun, 3 Dec 2023 17:27:23 +0100
Subject: [PATCH 05/37] Validate EntityReferences with null logicalName on
.Initialize()
fixes DynamicsValue/fake-xrm-easy#107
---
src/FakeXrmEasy.Core/XrmFakedContext.cs | 14 +++++++-
.../FakeContextTests/FakeContextTests.cs | 32 +++++++++++++------
2 files changed, 36 insertions(+), 10 deletions(-)
diff --git a/src/FakeXrmEasy.Core/XrmFakedContext.cs b/src/FakeXrmEasy.Core/XrmFakedContext.cs
index e37282da..c28e8813 100644
--- a/src/FakeXrmEasy.Core/XrmFakedContext.cs
+++ b/src/FakeXrmEasy.Core/XrmFakedContext.cs
@@ -300,14 +300,26 @@ public virtual void Initialize(IEnumerable entities)
throw new InvalidOperationException("The entities parameter must be not null");
}
- foreach (var e in entities)
+ foreach (var e in entities)
{
+ ValidateEntityReferences(e);
AddEntityWithDefaults(e, true);
}
Initialised = true;
}
+ private void ValidateEntityReferences(Entity e)
+ {
+ foreach (var item in e.Attributes)
+ {
+ if (item.Value is EntityReference entityReference && String.IsNullOrEmpty(entityReference.LogicalName))
+ {
+ throw new Exception($"Broken EntityReference record during Initialize() for column '{item.Key}' of a '{e.LogicalName}' record.");
+ }
+ }
+ }
+
///
/// Initializes the context with a single entity record
///
diff --git a/tests/FakeXrmEasy.Core.Tests/FakeContextTests/FakeContextTests.cs b/tests/FakeXrmEasy.Core.Tests/FakeContextTests/FakeContextTests.cs
index 5ca0212a..79fb4825 100644
--- a/tests/FakeXrmEasy.Core.Tests/FakeContextTests/FakeContextTests.cs
+++ b/tests/FakeXrmEasy.Core.Tests/FakeContextTests/FakeContextTests.cs
@@ -284,10 +284,10 @@ public void When_initialising_the_context_after_enabling_proxy_types_exception_i
}
[Fact]
- public void Should_return_entity_by_id()
+ public void Should_return_entity_by_id()
{
- var contact = new Contact()
- {
+ var contact = new Contact()
+ {
Id = Guid.NewGuid(),
FirstName = "Steve",
LastName = "Vai"
@@ -297,27 +297,41 @@ public void Should_return_entity_by_id()
var retrievedContact = _context.GetEntityById(contact.Id);
Assert.Equal(contact.Id, retrievedContact.Id);
Assert.Equal(contact.FirstName, retrievedContact.FirstName);
- Assert.Equal(contact.LastName, retrievedContact.LastName);
+ Assert.Equal(contact.LastName, retrievedContact.LastName);
}
[Fact]
public void Should_return_error_if_entity_logical_name_doesnt_exists()
{
- Assert.Throws(() =>_context.GetEntityById("doesNotExist", Guid.NewGuid()));
+ Assert.Throws(() =>_context.GetEntityById("doesNotExist", Guid.NewGuid()));
}
[Fact]
- public void Should_return_error_if_entity_id_does_not_exists()
+ public void Should_return_error_if_entity_id_does_not_exists()
{
- var contact = new Contact()
- {
+ var contact = new Contact()
+ {
Id = Guid.NewGuid(),
FirstName = "Steve",
LastName = "Vai"
};
_context.Initialize(contact);
- Assert.Throws(() =>_context.GetEntityById("contact", Guid.NewGuid()));
+ Assert.Throws(() =>_context.GetEntityById("contact", Guid.NewGuid()));
+ }
+
+ [Fact]
+ public void Should_return_error_if_contains_broken_entityreference()
+ {
+ var contact = new Contact()
+ {
+ Id = Guid.NewGuid(),
+ FirstName = "Steve",
+ LastName = "Vai",
+ ParentCustomerId = new EntityReference("", Guid.NewGuid())
+ };
+
+ Assert.Throws(() => _context.Initialize(contact));
}
[Fact]
From a95dc786ea20c5299a7c11b188317984dc2db51b Mon Sep 17 00:00:00 2001
From: Jordi
Date: Sun, 10 Mar 2024 23:13:03 +0100
Subject: [PATCH 06/37] adding initial support for bulk operations
DynamicsValue/fake-xrm-easy#122
---
CHANGELOG.md | 4 +
src/FakeXrmEasy.Core/FakeXrmEasy.Core.csproj | 6 +-
.../CreateMultipleRequestExecutor.cs | 126 ++
.../Crud/MiddlewareBuilderExtensions.Crud.cs | 3 +
src/FakeXrmEasy.Core/XrmFakedContext.cs | 4 +-
.../DataverseEntities.csproj | 8 +-
tests/DataverseEntities/Entities/dv_test.cs | 1204 +++++++++++++++++
.../AssertExtensions.cs | 17 +
.../FakeXrmEasy.Core.Tests.csproj | 16 +-
.../CreateMultipleRequestsTests.cs | 129 ++
10 files changed, 1499 insertions(+), 18 deletions(-)
create mode 100644 src/FakeXrmEasy.Core/Middleware/Crud/FakeMessageExecutors/CreateMultipleRequestExecutor.cs
create mode 100644 tests/DataverseEntities/Entities/dv_test.cs
create mode 100644 tests/FakeXrmEasy.Core.Tests/AssertExtensions.cs
create mode 100644 tests/FakeXrmEasy.Core.Tests/Middleware/Crud/FakeMessageExecutors/CreateMultipleRequestsTests/CreateMultipleRequestsTests.cs
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 8a53ff3d..a536f97a 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,3 +1,7 @@
+## [2.5.0]
+
+- Added support for bulk operations: CreateMultipleRequest
+
## [2.4.2]
### Added
diff --git a/src/FakeXrmEasy.Core/FakeXrmEasy.Core.csproj b/src/FakeXrmEasy.Core/FakeXrmEasy.Core.csproj
index 9096cbf1..274e3373 100644
--- a/src/FakeXrmEasy.Core/FakeXrmEasy.Core.csproj
+++ b/src/FakeXrmEasy.Core/FakeXrmEasy.Core.csproj
@@ -8,7 +8,7 @@
net452
net452
FakeXrmEasy.Core
- 2.4.2
+ 2.5.0
Jordi Montaña
Dynamics Value
FakeXrmEasy Core
@@ -57,8 +57,8 @@
-
-
+
+
diff --git a/src/FakeXrmEasy.Core/Middleware/Crud/FakeMessageExecutors/CreateMultipleRequestExecutor.cs b/src/FakeXrmEasy.Core/Middleware/Crud/FakeMessageExecutors/CreateMultipleRequestExecutor.cs
new file mode 100644
index 00000000..391cfbe8
--- /dev/null
+++ b/src/FakeXrmEasy.Core/Middleware/Crud/FakeMessageExecutors/CreateMultipleRequestExecutor.cs
@@ -0,0 +1,126 @@
+using FakeXrmEasy.Abstractions;
+using FakeXrmEasy.Abstractions.FakeMessageExecutors;
+using Microsoft.Xrm.Sdk;
+using Microsoft.Xrm.Sdk.Messages;
+using System;
+using System.Linq;
+
+namespace FakeXrmEasy.Middleware.Crud.FakeMessageExecutors
+{
+ ///
+ /// CreateMultipleRequest Executor
+ ///
+ public class CreateMultipleRequestExecutor : IFakeMessageExecutor
+ {
+ ///
+ ///
+ ///
+ ///
+ ///
+ public bool CanExecute(OrganizationRequest request)
+ {
+ return request is CreateMultipleRequest;
+ }
+
+ ///
+ /// Executes the CreateRequestMultiple request
+ ///
+ ///
+ ///
+ ///
+ public OrganizationResponse Execute(OrganizationRequest request, IXrmFakedContext ctx)
+ {
+ var createMultipleRequest = (CreateMultipleRequest)request;
+
+ ValidateRequest(createMultipleRequest, ctx);
+
+ var response = new CreateMultipleResponse();
+
+ return new CreateResponse()
+ {
+ ResponseName = "CreateMultipleResponse"
+ };
+ }
+
+ private void ValidateRequiredParameters(CreateMultipleRequest request, IXrmFakedContext ctx)
+ {
+ if (request.Targets == null)
+ {
+ throw FakeOrganizationServiceFaultFactory.New(ErrorCodes.InvalidArgument,
+ "Required field 'Targets' is missing");
+ }
+
+ var targets = request.Targets;
+ if (string.IsNullOrWhiteSpace(targets.EntityName))
+ {
+ throw FakeOrganizationServiceFaultFactory.New(ErrorCodes.InvalidArgument,
+ "Required member 'EntityName' missing for field 'Targets'");
+ }
+ }
+
+ private void ValidateEntityName(CreateMultipleRequest request, IXrmFakedContext ctx)
+ {
+ var targets = request.Targets;
+ if (ctx.ProxyTypesAssemblies.Any())
+ {
+ var earlyBoundType = ctx.FindReflectedType(targets.EntityName);
+ if (earlyBoundType == null)
+ {
+ throw FakeOrganizationServiceFaultFactory.New(ErrorCodes.QueryBuilderNoEntity,
+ $"The entity with a name = '{targets.EntityName}' with namemapping = 'Logical' was not found in the MetadataCache.");
+ }
+ }
+
+ if (ctx.CreateMetadataQuery().Any())
+ {
+ var entityMetadata = ctx.CreateMetadataQuery().FirstOrDefault(m => m.LogicalName == targets.EntityName);
+ if (entityMetadata == null)
+ {
+ throw FakeOrganizationServiceFaultFactory.New(ErrorCodes.QueryBuilderNoEntity,
+ $"The entity with a name = '{targets.EntityName}' with namemapping = 'Logical' was not found in the MetadataCache.");
+ }
+ }
+ }
+
+ private void ValidateRecords(CreateMultipleRequest request, IXrmFakedContext ctx)
+ {
+ var records = request.Targets.Entities;
+ if (records.Count == 0)
+ {
+ throw FakeOrganizationServiceFaultFactory.New(ErrorCodes.UnExpected,
+ $"System.ArgumentException: The value of the parameter 'Targets' cannot be null or empty.");
+ }
+
+ foreach (var record in records)
+ {
+ ValidateRecord(request, record, ctx);
+ }
+ }
+
+ private void ValidateRecord(CreateMultipleRequest request, Entity recordToCreate, IXrmFakedContext ctx)
+ {
+ var exists = ctx.ContainsEntity(recordToCreate.LogicalName, recordToCreate.Id);
+ if (exists)
+ {
+ throw FakeOrganizationServiceFaultFactory.New(ErrorCodes.DuplicateRecord,
+ $"Cannot insert duplicate key.");
+ }
+ }
+
+ private void ValidateRequest(CreateMultipleRequest request, IXrmFakedContext ctx)
+ {
+ ValidateRequiredParameters(request, ctx);
+ ValidateEntityName(request, ctx);
+ ValidateRecords(request, ctx);
+ }
+
+ ///
+ /// Returns CreateMultipleRequest
+ ///
+ ///
+ public Type GetResponsibleRequestType()
+ {
+ return typeof(CreateMultipleRequest);
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/FakeXrmEasy.Core/Middleware/Crud/MiddlewareBuilderExtensions.Crud.cs b/src/FakeXrmEasy.Core/Middleware/Crud/MiddlewareBuilderExtensions.Crud.cs
index 84d4b7e5..3d3fbd40 100644
--- a/src/FakeXrmEasy.Core/Middleware/Crud/MiddlewareBuilderExtensions.Crud.cs
+++ b/src/FakeXrmEasy.Core/Middleware/Crud/MiddlewareBuilderExtensions.Crud.cs
@@ -38,6 +38,7 @@ public static IMiddlewareBuilder AddCrud(this IMiddlewareBuilder builder)
//Get Crud Message Executors
var crudMessageExecutors = new CrudMessageExecutors();
crudMessageExecutors.Add(typeof(CreateRequest), new CreateRequestExecutor());
+
crudMessageExecutors.Add(typeof(RetrieveMultipleRequest), new RetrieveMultipleRequestExecutor());
crudMessageExecutors.Add(typeof(RetrieveRequest), new RetrieveRequestExecutor());
crudMessageExecutors.Add(typeof(UpdateRequest), new UpdateRequestExecutor());
@@ -45,6 +46,8 @@ public static IMiddlewareBuilder AddCrud(this IMiddlewareBuilder builder)
crudMessageExecutors.Add(typeof(AssociateRequest), new AssociateRequestExecutor());
crudMessageExecutors.Add(typeof(DisassociateRequest), new DisassociateRequestExecutor());
+ crudMessageExecutors.Add(typeof(CreateMultipleRequest), new CreateMultipleRequestExecutor());
+
#if !FAKE_XRM_EASY && !FAKE_XRM_EASY_2013 && !FAKE_XRM_EASY_2015
crudMessageExecutors.Add(typeof(UpsertRequest), new UpsertRequestExecutor());
#endif
diff --git a/src/FakeXrmEasy.Core/XrmFakedContext.cs b/src/FakeXrmEasy.Core/XrmFakedContext.cs
index 0e61a5e4..a03ce41f 100644
--- a/src/FakeXrmEasy.Core/XrmFakedContext.cs
+++ b/src/FakeXrmEasy.Core/XrmFakedContext.cs
@@ -13,21 +13,19 @@
using FakeXrmEasy.Permissions;
using FakeXrmEasy.Services;
using Microsoft.Xrm.Sdk;
-using Microsoft.Xrm.Sdk.Metadata;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
-using FakeXrmEasy.Abstractions.CommercialLicense;
[assembly: InternalsVisibleTo("FakeXrmEasy.Core.Tests, PublicKey=0024000004800000940000000602000000240000525341310004000001000100c124cb50761165a765adf6078bde555a7c5a2b692ed6e6ec9df0bd7d20da69170bae9bf95e874fa50995cc080af404ccad36515fa509c4ea6599a0502c1642db254a293e023c47c79ce69889c6ba921d124d896d87f0baaa9ea1d87b28589ffbe7b08492606bacef19dc4bc4cefb0d525be63ee722b02dc8c79688a7a8f623a2")]
namespace FakeXrmEasy
{
///
- /// A fake context that stores In-Memory entites indexed by logical name and then Entity records, simulating
+ /// A fake context that stores In-Memory entities indexed by logical name and then Entity records, simulating
/// how entities are persisted in Tables (with the logical name) and then the records themselves
/// where the Primary Key is the Guid
///
diff --git a/tests/DataverseEntities/DataverseEntities.csproj b/tests/DataverseEntities/DataverseEntities.csproj
index 5dad1807..d1924fe2 100644
--- a/tests/DataverseEntities/DataverseEntities.csproj
+++ b/tests/DataverseEntities/DataverseEntities.csproj
@@ -72,10 +72,10 @@
-
-
-
-
+
+
+
+
diff --git a/tests/DataverseEntities/Entities/dv_test.cs b/tests/DataverseEntities/Entities/dv_test.cs
new file mode 100644
index 00000000..fd99ecd8
--- /dev/null
+++ b/tests/DataverseEntities/Entities/dv_test.cs
@@ -0,0 +1,1204 @@
+#pragma warning disable CS1591
+//------------------------------------------------------------------------------
+//
+// This code was generated by a tool.
+//
+// Changes to this file may cause incorrect behavior and will be lost if
+// the code is regenerated.
+//
+//------------------------------------------------------------------------------
+
+namespace DataverseEntities
+{
+
+
+ [System.Runtime.Serialization.DataContractAttribute()]
+ [System.CodeDom.Compiler.GeneratedCodeAttribute("Dataverse Model Builder", "2.0.0.6")]
+ public enum dv_test_dv_choice_multiple
+ {
+
+ [System.Runtime.Serialization.EnumMemberAttribute()]
+ Option1 = 121940000,
+
+ [System.Runtime.Serialization.EnumMemberAttribute()]
+ Option2 = 121940001,
+
+ [System.Runtime.Serialization.EnumMemberAttribute()]
+ Option3 = 121940002,
+ }
+
+ [System.Runtime.Serialization.DataContractAttribute()]
+ [System.CodeDom.Compiler.GeneratedCodeAttribute("Dataverse Model Builder", "2.0.0.6")]
+ public enum dv_test_dv_choice_single
+ {
+
+ [System.Runtime.Serialization.EnumMemberAttribute()]
+ Option1 = 121940000,
+
+ [System.Runtime.Serialization.EnumMemberAttribute()]
+ Option2 = 121940001,
+
+ [System.Runtime.Serialization.EnumMemberAttribute()]
+ Option3 = 121940002,
+ }
+
+ ///
+ /// Status of the Test Table
+ ///
+ [System.Runtime.Serialization.DataContractAttribute()]
+ [System.CodeDom.Compiler.GeneratedCodeAttribute("Dataverse Model Builder", "2.0.0.6")]
+ public enum dv_test_statecode
+ {
+
+ [System.Runtime.Serialization.EnumMemberAttribute()]
+ Active = 0,
+
+ [System.Runtime.Serialization.EnumMemberAttribute()]
+ Inactive = 1,
+ }
+
+ ///
+ /// Reason for the status of the Test Table
+ ///
+ [System.Runtime.Serialization.DataContractAttribute()]
+ [System.CodeDom.Compiler.GeneratedCodeAttribute("Dataverse Model Builder", "2.0.0.6")]
+ public enum dv_test_statuscode
+ {
+
+ [System.Runtime.Serialization.EnumMemberAttribute()]
+ Active = 1,
+
+ [System.Runtime.Serialization.EnumMemberAttribute()]
+ Inactive = 2,
+ }
+
+ [System.Runtime.Serialization.DataContractAttribute()]
+ [Microsoft.Xrm.Sdk.Client.EntityLogicalNameAttribute("dv_test")]
+ [System.CodeDom.Compiler.GeneratedCodeAttribute("Dataverse Model Builder", "2.0.0.6")]
+ public partial class dv_test : Microsoft.Xrm.Sdk.Entity
+ {
+
+ ///
+ /// Available fields, a the time of codegen, for the dv_test entity
+ ///
+ public partial class Fields
+ {
+ public const string CreatedBy = "createdby";
+ public const string CreatedByName = "createdbyname";
+ public const string CreatedByYomiName = "createdbyyominame";
+ public const string CreatedOn = "createdon";
+ public const string CreatedOnBehalfBy = "createdonbehalfby";
+ public const string CreatedOnBehalfByName = "createdonbehalfbyname";
+ public const string CreatedOnBehalfByYomiName = "createdonbehalfbyyominame";
+ public const string dv_accountid = "dv_accountid";
+ public const string dv_accountidName = "dv_accountidname";
+ public const string dv_accountidYomiName = "dv_accountidyominame";
+ public const string dv_bool = "dv_bool";
+ public const string dv_boolName = "dv_boolname";
+ public const string dv_choice_multiple = "dv_choice_multiple";
+ public const string dv_choice_multipleName = "dv_choice_multiplename";
+ public const string dv_choice_single = "dv_choice_single";
+ public const string dv_choice_singleName = "dv_choice_singlename";
+ public const string dv_currency = "dv_currency";
+ public const string dv_currency_Base = "dv_currency_base";
+ public const string dv_customerid = "dv_customerid";
+ public const string dv_customeridName = "dv_customeridname";
+ public const string dv_customeridYomiName = "dv_customeridyominame";
+ public const string dv_date_only = "dv_date_only";
+ public const string dv_date_time = "dv_date_time";
+ public const string dv_decimal = "dv_decimal";
+ public const string dv_duration = "dv_duration";
+ public const string dv_email = "dv_email";
+ public const string dv_file = "dv_file";
+ public const string dv_file_Name = "dv_file_name";
+ public const string dv_float = "dv_float";
+ public const string dv_image = "dv_image";
+ public const string dv_image_Timestamp = "dv_image_timestamp";
+ public const string dv_image_URL = "dv_image_url";
+ public const string dv_imageId = "dv_imageid";
+ public const string dv_int = "dv_int";
+ public const string dv_language_code = "dv_language_code";
+ public const string dv_phone_number = "dv_phone_number";
+ public const string dv_string = "dv_string";
+ public const string dv_testId = "dv_testid";
+ public const string Id = "dv_testid";
+ public const string dv_textarea = "dv_textarea";
+ public const string dv_ticker_symbol = "dv_ticker_symbol";
+ public const string dv_time_zone = "dv_time_zone";
+ public const string dv_url = "dv_url";
+ public const string ExchangeRate = "exchangerate";
+ public const string ImportSequenceNumber = "importsequencenumber";
+ public const string ModifiedBy = "modifiedby";
+ public const string ModifiedByName = "modifiedbyname";
+ public const string ModifiedByYomiName = "modifiedbyyominame";
+ public const string ModifiedOn = "modifiedon";
+ public const string ModifiedOnBehalfBy = "modifiedonbehalfby";
+ public const string ModifiedOnBehalfByName = "modifiedonbehalfbyname";
+ public const string ModifiedOnBehalfByYomiName = "modifiedonbehalfbyyominame";
+ public const string OverriddenCreatedOn = "overriddencreatedon";
+ public const string OwnerId = "ownerid";
+ public const string OwnerIdName = "owneridname";
+ public const string OwnerIdYomiName = "owneridyominame";
+ public const string OwningBusinessUnit = "owningbusinessunit";
+ public const string OwningBusinessUnitName = "owningbusinessunitname";
+ public const string OwningTeam = "owningteam";
+ public const string OwningUser = "owninguser";
+ public const string statecode = "statecode";
+ public const string statecodeName = "statecodename";
+ public const string statuscode = "statuscode";
+ public const string statuscodeName = "statuscodename";
+ public const string TimeZoneRuleVersionNumber = "timezoneruleversionnumber";
+ public const string TransactionCurrencyId = "transactioncurrencyid";
+ public const string TransactionCurrencyIdName = "transactioncurrencyidname";
+ public const string UTCConversionTimeZoneCode = "utcconversiontimezonecode";
+ public const string VersionNumber = "versionnumber";
+ public const string dv_account_dv_test_393 = "dv_account_dv_test_393";
+ public const string dv_contact_dv_test_393 = "dv_contact_dv_test_393";
+ public const string dv_test_accountid_account = "dv_test_accountid_account";
+ }
+
+ ///
+ /// Default Constructor.
+ ///
+ public dv_test() :
+ base(EntityLogicalName)
+ {
+ }
+
+ public const string EntityLogicalName = "dv_test";
+
+ public const string EntityLogicalCollectionName = "dv_tests";
+
+ public const string EntitySetName = "dv_tests";
+
+ public const int EntityTypeCode = 10359;
+
+ ///
+ /// Unique identifier of the user who created the record.
+ ///
+ [Microsoft.Xrm.Sdk.AttributeLogicalNameAttribute("createdby")]
+ public Microsoft.Xrm.Sdk.EntityReference CreatedBy
+ {
+ get
+ {
+ return this.GetAttributeValue("createdby");
+ }
+ }
+
+ [Microsoft.Xrm.Sdk.AttributeLogicalNameAttribute("createdbyname")]
+ public string CreatedByName
+ {
+ get
+ {
+ if (this.FormattedValues.Contains("createdby"))
+ {
+ return this.FormattedValues["createdby"];
+ }
+ else
+ {
+ return default(string);
+ }
+ }
+ }
+
+ [Microsoft.Xrm.Sdk.AttributeLogicalNameAttribute("createdbyyominame")]
+ public string CreatedByYomiName
+ {
+ get
+ {
+ if (this.FormattedValues.Contains("createdby"))
+ {
+ return this.FormattedValues["createdby"];
+ }
+ else
+ {
+ return default(string);
+ }
+ }
+ }
+
+ ///
+ /// Date and time when the record was created.
+ ///
+ [Microsoft.Xrm.Sdk.AttributeLogicalNameAttribute("createdon")]
+ public System.Nullable CreatedOn
+ {
+ get
+ {
+ return this.GetAttributeValue>("createdon");
+ }
+ }
+
+ ///
+ /// Unique identifier of the delegate user who created the record.
+ ///
+ [Microsoft.Xrm.Sdk.AttributeLogicalNameAttribute("createdonbehalfby")]
+ public Microsoft.Xrm.Sdk.EntityReference CreatedOnBehalfBy
+ {
+ get
+ {
+ return this.GetAttributeValue("createdonbehalfby");
+ }
+ }
+
+ [Microsoft.Xrm.Sdk.AttributeLogicalNameAttribute("createdonbehalfbyname")]
+ public string CreatedOnBehalfByName
+ {
+ get
+ {
+ if (this.FormattedValues.Contains("createdonbehalfby"))
+ {
+ return this.FormattedValues["createdonbehalfby"];
+ }
+ else
+ {
+ return default(string);
+ }
+ }
+ }
+
+ [Microsoft.Xrm.Sdk.AttributeLogicalNameAttribute("createdonbehalfbyyominame")]
+ public string CreatedOnBehalfByYomiName
+ {
+ get
+ {
+ if (this.FormattedValues.Contains("createdonbehalfby"))
+ {
+ return this.FormattedValues["createdonbehalfby"];
+ }
+ else
+ {
+ return default(string);
+ }
+ }
+ }
+
+ [Microsoft.Xrm.Sdk.AttributeLogicalNameAttribute("dv_accountid")]
+ public Microsoft.Xrm.Sdk.EntityReference dv_accountid
+ {
+ get
+ {
+ return this.GetAttributeValue("dv_accountid");
+ }
+ set
+ {
+ this.SetAttributeValue("dv_accountid", value);
+ }
+ }
+
+ [Microsoft.Xrm.Sdk.AttributeLogicalNameAttribute("dv_accountidname")]
+ public string dv_accountidName
+ {
+ get
+ {
+ if (this.FormattedValues.Contains("dv_accountid"))
+ {
+ return this.FormattedValues["dv_accountid"];
+ }
+ else
+ {
+ return default(string);
+ }
+ }
+ }
+
+ [Microsoft.Xrm.Sdk.AttributeLogicalNameAttribute("dv_accountidyominame")]
+ public string dv_accountidYomiName
+ {
+ get
+ {
+ if (this.FormattedValues.Contains("dv_accountid"))
+ {
+ return this.FormattedValues["dv_accountid"];
+ }
+ else
+ {
+ return default(string);
+ }
+ }
+ }
+
+ [Microsoft.Xrm.Sdk.AttributeLogicalNameAttribute("dv_bool")]
+ public System.Nullable dv_bool
+ {
+ get
+ {
+ return this.GetAttributeValue>("dv_bool");
+ }
+ set
+ {
+ this.SetAttributeValue("dv_bool", value);
+ }
+ }
+
+ [Microsoft.Xrm.Sdk.AttributeLogicalNameAttribute("dv_boolname")]
+ public string dv_boolName
+ {
+ get
+ {
+ if (this.FormattedValues.Contains("dv_bool"))
+ {
+ return this.FormattedValues["dv_bool"];
+ }
+ else
+ {
+ return default(string);
+ }
+ }
+ }
+
+ [Microsoft.Xrm.Sdk.AttributeLogicalNameAttribute("dv_choice_multiple")]
+ public virtual System.Collections.Generic.IEnumerable dv_choice_multiple
+ {
+ get
+ {
+ return EntityOptionSetEnum.GetMultiEnum(this, "dv_choice_multiple");
+ }
+ set
+ {
+ this.SetAttributeValue("dv_choice_multiple", EntityOptionSetEnum.GetMultiEnum(this, "dv_choice_multiple", value));
+ }
+ }
+
+ [Microsoft.Xrm.Sdk.AttributeLogicalNameAttribute("dv_choice_multiplename")]
+ public string dv_choice_multipleName
+ {
+ get
+ {
+ if (this.FormattedValues.Contains("dv_choice_multiple"))
+ {
+ return this.FormattedValues["dv_choice_multiple"];
+ }
+ else
+ {
+ return default(string);
+ }
+ }
+ }
+
+ [Microsoft.Xrm.Sdk.AttributeLogicalNameAttribute("dv_choice_single")]
+ public virtual dv_test_dv_choice_single? dv_choice_single
+ {
+ get
+ {
+ return ((dv_test_dv_choice_single?)(EntityOptionSetEnum.GetEnum(this, "dv_choice_single")));
+ }
+ set
+ {
+ this.SetAttributeValue("dv_choice_single", value.HasValue ? new Microsoft.Xrm.Sdk.OptionSetValue((int)value) : null);
+ }
+ }
+
+ [Microsoft.Xrm.Sdk.AttributeLogicalNameAttribute("dv_choice_singlename")]
+ public string dv_choice_singleName
+ {
+ get
+ {
+ if (this.FormattedValues.Contains("dv_choice_single"))
+ {
+ return this.FormattedValues["dv_choice_single"];
+ }
+ else
+ {
+ return default(string);
+ }
+ }
+ }
+
+ [Microsoft.Xrm.Sdk.AttributeLogicalNameAttribute("dv_currency")]
+ public Microsoft.Xrm.Sdk.Money dv_currency
+ {
+ get
+ {
+ return this.GetAttributeValue("dv_currency");
+ }
+ set
+ {
+ this.SetAttributeValue("dv_currency", value);
+ }
+ }
+
+ ///
+ /// Value of the currency in base currency.
+ ///
+ [Microsoft.Xrm.Sdk.AttributeLogicalNameAttribute("dv_currency_base")]
+ public Microsoft.Xrm.Sdk.Money dv_currency_Base
+ {
+ get
+ {
+ return this.GetAttributeValue("dv_currency_base");
+ }
+ }
+
+ [Microsoft.Xrm.Sdk.AttributeLogicalNameAttribute("dv_customerid")]
+ public Microsoft.Xrm.Sdk.EntityReference dv_customerid
+ {
+ get
+ {
+ return this.GetAttributeValue("dv_customerid");
+ }
+ set
+ {
+ this.SetAttributeValue("dv_customerid", value);
+ }
+ }
+
+ [Microsoft.Xrm.Sdk.AttributeLogicalNameAttribute("dv_customeridname")]
+ public string dv_customeridName
+ {
+ get
+ {
+ if (this.FormattedValues.Contains("dv_customerid"))
+ {
+ return this.FormattedValues["dv_customerid"];
+ }
+ else
+ {
+ return default(string);
+ }
+ }
+ }
+
+ [Microsoft.Xrm.Sdk.AttributeLogicalNameAttribute("dv_customeridyominame")]
+ public string dv_customeridYomiName
+ {
+ get
+ {
+ if (this.FormattedValues.Contains("dv_customerid"))
+ {
+ return this.FormattedValues["dv_customerid"];
+ }
+ else
+ {
+ return default(string);
+ }
+ }
+ }
+
+ [Microsoft.Xrm.Sdk.AttributeLogicalNameAttribute("dv_date_only")]
+ public System.Nullable dv_date_only
+ {
+ get
+ {
+ return this.GetAttributeValue>("dv_date_only");
+ }
+ set
+ {
+ this.SetAttributeValue("dv_date_only", value);
+ }
+ }
+
+ [Microsoft.Xrm.Sdk.AttributeLogicalNameAttribute("dv_date_time")]
+ public System.Nullable dv_date_time
+ {
+ get
+ {
+ return this.GetAttributeValue>("dv_date_time");
+ }
+ set
+ {
+ this.SetAttributeValue("dv_date_time", value);
+ }
+ }
+
+ [Microsoft.Xrm.Sdk.AttributeLogicalNameAttribute("dv_decimal")]
+ public System.Nullable dv_decimal
+ {
+ get
+ {
+ return this.GetAttributeValue>("dv_decimal");
+ }
+ set
+ {
+ this.SetAttributeValue("dv_decimal", value);
+ }
+ }
+
+ [Microsoft.Xrm.Sdk.AttributeLogicalNameAttribute("dv_duration")]
+ public System.Nullable dv_duration
+ {
+ get
+ {
+ return this.GetAttributeValue>("dv_duration");
+ }
+ set
+ {
+ this.SetAttributeValue("dv_duration", value);
+ }
+ }
+
+ [Microsoft.Xrm.Sdk.AttributeLogicalNameAttribute("dv_email")]
+ public string dv_email
+ {
+ get
+ {
+ return this.GetAttributeValue("dv_email");
+ }
+ set
+ {
+ this.SetAttributeValue("dv_email", value);
+ }
+ }
+
+ [Microsoft.Xrm.Sdk.AttributeLogicalNameAttribute("dv_file")]
+ public object dv_file
+ {
+ get
+ {
+ return this.GetAttributeValue