Summary

Class:FakeXrmEasy.XrmFakedContext
Assembly:FakeXrmEasy
File(s):C:\code\jordimontana82\fake-xrm-easy\FakeXrmEasy.Shared\XrmFakedContext.Aggregations.cs
C:\code\jordimontana82\fake-xrm-easy\FakeXrmEasy.Shared\XrmFakedContext.CodeActivities.cs
C:\code\jordimontana82\fake-xrm-easy\FakeXrmEasy.Shared\XrmFakedContext.Crud.cs
C:\code\jordimontana82\fake-xrm-easy\FakeXrmEasy.Shared\XrmFakedContext.cs
C:\code\jordimontana82\fake-xrm-easy\FakeXrmEasy.Shared\XrmFakedContext.DateTime.cs
C:\code\jordimontana82\fake-xrm-easy\FakeXrmEasy.Shared\XrmFakedContext.Metadata.cs
C:\code\jordimontana82\fake-xrm-easy\FakeXrmEasy.Shared\XrmFakedContext.Pipeline.cs
C:\code\jordimontana82\fake-xrm-easy\FakeXrmEasy.Shared\XrmFakedContext.Plugins.cs
C:\code\jordimontana82\fake-xrm-easy\FakeXrmEasy.Shared\XrmFakedContext.Queries.cs
Covered lines:2578
Uncovered lines:315
Coverable lines:2893
Total lines:4738
Line coverage:89.1%
Branch coverage:73.7%

Metrics

MethodCyclomatic ComplexitySequence CoverageBranch Coverage
RegisterPluginStep(...)1100100
RegisterPluginStep(...)810072.73
ExecutePipelineStage(...)1100100
ExecutePipelineStage(...)27566.67
ExecutePipelinePlugins(...)3100100
GetPluginMethod(...)1100100
GetStepsForStage(...)310066.67
DefaultDateBehaviour()1100100
ProcessAggregateFetchXml(...)3484.5272.34
OrderAggregateResult(...)88066.67
ProcessAggregatesForSingleGroup(...)4100100
ProcessGroupedAggregate(...)7100100
Process(...)1100100
AggregateValues(...)4100100
AggregateValues(...)1100100
AggregateAliasedValues(...)2100100
AggregateAliasedValues(...)2100100
AggregateAliasedValues(...)2161.5460
AggregateAliasedValues(...)2061.5460
AggregateAliasedValues(...)1743.4830.77
AggregateAliasedValues(...)1759.0953.85
Process(...)3100100
Equals(...)1100100
GetHashCode(...)3100100
.ctor(...)1100100
System.IComparable.CompareTo(...)300
Equals(...)461.5460
GetHashCode()2100100
FindGroupValue(...)2100100
FindGroupValue(...)1958.8258.33
GetDefaultWorkflowContext()3100100
ExecuteCodeActivity(...)1100100
ExecuteCodeActivity(...)200
ExecuteCodeActivity(...)574.0757.14
GetRecordUniqueId(...)1886.6788.89
FakeRetrieve(...)1100100
FakeCreate(...)1100100
FakeUpdate(...)1100100
UpdateEntity(...)11100100
ResolveEntityReference(...)5100100
ResolveEntityReferenceByAlternateKeys(...)1100100
FakeDelete(...)1100100
DeleteEntity(...)992.3192.31
EnsureEntityNameExistsInMetadata(...)483.3385.71
AddEntityDefaultAttributes(...)610077.78
ValidateEntity(...)481.8285.71
CreateEntity(...)149693.33
AddEntityWithDefaults(...)5100100
AddEntity(...)17100100
AttributeExistsInMetadata(...)979.1776.92
AttributeExistsInInjectedMetadata(...)100
ConvertToUtc(...)1100100
.ctor()6100100
Initialize(...)5100100
Initialize(...)1100100
EnableProxyTypes(...)377.7880
AddExecutionMock(...)2100100
RemoveExecutionMock()1100100
AddFakeMessageExecutor(...)28066.67
RemoveFakeMessageExecutor()1100100
AddGenericFakeMessageExecutor(...)28066.67
RemoveGenericFakeMessageExecutor(...)210066.67
AddRelationship(...)1100100
RemoveRelationship(...)100
GetRelationship(...)2100100
AddAttributeMapping(...)571.4355.56
GetOrganizationService()257.1466.67
GetFakedOrganizationService()1100100
GetFakedOrganizationService(...)2100100
FakeExecute(...)1100100
FakeAssociate(...)1100100
FakeDisassociate(...)1100100
FakeRetrieveMultiple(...)1100100
GetFakedServiceEndpointNotificationService()2100100
InitializeMetadata(...)6100100
InitializeMetadata(...)1100100
InitializeMetadata(...)210066.67
CreateMetadataQuery()2100100
GetEntityMetadataByName(...)2100100
SetEntityMetadata(...)28066.67
GetAttributeMetadataFor(...)400
GetDefaultPluginContext()310080
GetFakedPluginContext(...)1100100
PopulateExecutionContextPropertiesFromFakedContext(...)510085.71
GetFakedExecutionContext(...)100
ExecutePluginWith(...)2100100
ExecutePluginWith(...)1100100
ExecutePluginWith(...)1100100
ExecutePluginWithConfigurations(...)3100100
ExecutePluginWithConfigurations(...)100
ExecutePluginWithTarget(...)100
ExecutePluginWithTarget(...)1100100
ExecutePluginWithTarget(...)1100100
ExecutePluginWithTargetReference(...)100
ExecutePluginWithTargetReference(...)100
ExecutePluginWithTargetAndPreEntityImages(...)1100100
ExecutePluginWithTargetAndPostEntityImages(...)1100100
ExecutePluginWithTargetAndInputParameters(...)100
GetFakedServiceProvider(...)1100100
GetFakeTracingService()1100100
FindReflectedType(...)529.4166.67
FindReflectedType(...)531.5866.67
FindAttributeTypeInInjectedMetadata(...)2146.6736.36
FindReflectedAttributeType(...)1273.6882.35
GetEarlyBoundTypeAttribute(...)2100100
CreateQuery(...)1100100
CreateQuery()410071.43
CreateQuery(...)9100100
CreateQueryFromEntityName(...)100
TranslateLinkedEntityToLinq(...)1685.4576.19
EnsureUniqueLinkedEntityAlias(...)2100100
RetrieveFetchXmlNode(...)1100100
ParseFetchXml(...)1100100
TranslateFetchXmlToQueryExpression(...)1100100
TranslateFetchXmlDocumentToQueryExpression(...)1194.4490.91
TranslateQueryExpressionToLinq(...)1010093.33
TranslateConditionExpression(...)3695.1875.95
ValidateSupportedTypedExpression(...)391.67100
GetAppropiateTypedValue(...)663.6472.73
GetAppropiateTypeForValue(...)38080
GetAppropiateTypedValueAndType(...)14100100
GetAppropiateCastExpressionBasedOnType(...)1100100
GetAppropiateCastExpressionBasedOnValueInherentType(...)992.8690.91
GetAppropiateCastExpressionBasedOnAttributeTypeOrValue(...)1596.1591.30
GetAppropiateCastExpressionBasedOnString(...)38080
GetAppropiateCastExpressionBasedOnStringAndType(...)371.4366.67
GetAppropiateCastExpressionBasedOnDateTime(...)583.3366.67
GetAppropiateCastExpressionDefault(...)1100100
GetAppropiateCastExpressionBasedGuid(...)1100100
GetAppropiateCastExpressionBasedOnEntityReference(...)3100100
GetAppropiateCastExpressionBasedOnDecimal(...)1100100
GetAppropiateCastExpressionBasedOnBoolean(...)1100100
GetAppropiateCastExpressionBasedOnInt(...)1100100
GetAppropiateCastExpressionBasedOnOptionSetValueCollection(...)1100100
TransformExpressionGetDateOnlyPart(...)1100100
TransformExpressionValueBasedOnOperator(...)3100100
TranslateConditionExpressionEqual(...)11100100
GetSingleConditionValue(...)792100
TranslateConditionExpressionIn(...)7100100
TranslateConditionExpressionGreaterThanOrEqual(...)1100100
TranslateConditionExpressionGreaterThan(...)883.3371.43
TranslateConditionExpressionGreaterThanString(...)3100100
TranslateConditionExpressionLessThanOrEqual(...)1100100
GetCompareToExpression(...)1100100
TranslateConditionExpressionLessThanString(...)3100100
TranslateConditionExpressionLessThan(...)891.6785.71
TranslateConditionExpressionLast(...)810061.54
TranslateConditionExpressionBetweenDates(...)1410075
TranslateConditionExpressionOlderThan(...)88476.92
TranslateConditionExpressionBetween(...)1100100
TranslateConditionExpressionNull(...)1100100
TranslateConditionExpressionOlderThan(...)1100100
TranslateConditionExpressionEndsWith(...)2100100
GetToStringExpression(...)1100100
GetCaseInsensitiveExpression(...)1100100
TranslateConditionExpressionLike(...)6100100
TranslateConditionExpressionContains(...)2100100
TranslateMultipleConditionExpressions(...)1310094.12
TranslateMultipleFilterExpressions(...)4100100
TranslateLinkedEntityFilterExpressionToExpression(...)2275.4157.89
TranslateQueryExpressionFiltersToExpression(...)8100100
TranslateFilterExpressionToExpression(...)11100100
TranslateConditionExpressionNext(...)810061.54
ValidateAliases(...)510080
ValidateAliases(...)510080
ValidateAliases(...)810071.43
ValidateAliases(...)97566.67
MatchByEntity(...)610060
MatchByAlias(...)510080
GetFakedEntityDataSourceRetrieverService()1100100
ConvertToHashSetOfInt(...)2481.6380
TranslateConditionExpressionContainValues(...)1100100

File(s)

C:\code\jordimontana82\fake-xrm-easy\FakeXrmEasy.Shared\XrmFakedContext.Aggregations.cs

#LineLine coverage
 1using FakeXrmEasy.Extensions.FetchXml;
 2using Microsoft.Xrm.Sdk;
 3using System;
 4using System.Collections.Generic;
 5using System.Linq;
 6using System.Xml.Linq;
 7
 8namespace FakeXrmEasy
 9{
 10    public partial class XrmFakedContext
 11    {
 12        internal static List<Entity> ProcessAggregateFetchXml(XrmFakedContext ctx, XDocument xmlDoc, List<Entity> result
 14413        {
 14            // Validate that <all-attributes> is not present,
 15            // that all attributes have groupby or aggregate, and an alias,
 16            // and that there is exactly 1 groupby.
 14417             if (RetrieveFetchXmlNode(xmlDoc, "all-attributes") != null)
 018            {
 019                throw new Exception("Can't have <all-attributes /> present when using aggregate");
 20            }
 21
 14422            var ns = xmlDoc.Root.Name.Namespace;
 23
 14424             var entityName = RetrieveFetchXmlNode(xmlDoc, "entity")?.GetAttribute("name")?.Value;
 14425             if (string.IsNullOrEmpty(entityName))
 026            {
 027                throw new Exception("Can't find entity name for aggregate query");
 28            }
 29
 14430            var aggregates = new List<FetchAggregate>();
 14431            var groups = new List<FetchGrouping>();
 32
 84033            foreach (var attr in xmlDoc.Descendants(ns + "attribute"))
 20434            {
 35                //TODO: Find entity alias. Handle aliasedvalue in the query result.
 21036                 var namespacedAlias = attr.Ancestors(ns + "link-entity").Select(x => x.GetAttribute("alias")?.Value != n
 20437                 namespacedAlias.Add(attr.GetAttribute("alias")?.Value);
 20438                var alias = string.Join(".", namespacedAlias);
 20439                namespacedAlias.RemoveAt(namespacedAlias.Count - 1);
 20440                 namespacedAlias.Add(attr.GetAttribute("name")?.Value);
 20441                var logicalName = string.Join(".", namespacedAlias);
 42
 20443                 if (string.IsNullOrEmpty("alias"))
 044                {
 045                    throw new Exception("Missing alias for attribute in aggregate fetch xml");
 46                }
 20447                 if (string.IsNullOrEmpty("name"))
 048                {
 049                    throw new Exception("Missing name for attribute in aggregate fetch xml");
 50                }
 51
 20452                 if (attr.IsAttributeTrue("groupby"))
 6053                {
 6054                     var dategrouping = attr.GetAttribute("dategrouping")?.Value;
 6055                     if (dategrouping != null)
 4256                    {
 57                        DateGroupType t;
 4258                         if (!Enum.TryParse(dategrouping, true, out t))
 059                        {
 060                            throw new Exception("Unknown dategrouping value '" + dategrouping + "'");
 61                        }
 4262                        groups.Add(new DateTimeGroup()
 4263                        {
 4264                            Type = t,
 4265                            OutputAlias = alias,
 4266                            Attribute = logicalName
 4267                        });
 4268                    }
 69                    else
 1870                    {
 1871                        groups.Add(new SimpleValueGroup()
 1872                        {
 1873                            OutputAlias = alias,
 1874                            Attribute = logicalName
 1875                        });
 1876                    }
 6077                }
 78                else
 14479                {
 14480                     var agrFn = attr.GetAttribute("aggregate")?.Value;
 14481                     if (string.IsNullOrEmpty(agrFn))
 082                    {
 083                        throw new Exception("Attributes must have be aggregated or grouped by when using aggregation");
 84                    }
 85
 14486                    FetchAggregate newAgr = null;
 14487                     switch (agrFn?.ToLower())
 88                    {
 89                        case "count":
 6090                            newAgr = new CountAggregate();
 6091                            break;
 92
 93                        case "countcolumn":
 1294                             if (attr.IsAttributeTrue("distinct"))
 695                            {
 696                                newAgr = new CountDistinctAggregate();
 697                            }
 98                            else
 699                            {
 6100                                newAgr = new CountColumnAggregate();
 6101                            }
 12102                            break;
 103
 104                        case "min":
 18105                            newAgr = new MinAggregate();
 18106                            break;
 107
 108                        case "max":
 18109                            newAgr = new MaxAggregate();
 18110                            break;
 111
 112                        case "avg":
 12113                            newAgr = new AvgAggregate();
 12114                            break;
 115
 116                        case "sum":
 24117                            newAgr = new SumAggregate();
 24118                            break;
 119
 120                        default:
 0121                            throw new Exception("Unknown aggregate function '" + agrFn + "'");
 122                    }
 123
 144124                    newAgr.OutputAlias = alias;
 144125                    newAgr.Attribute = logicalName;
 144126                    aggregates.Add(newAgr);
 144127                }
 204128            }
 129
 130            List<Entity> aggregateResult;
 131
 144132             if (groups.Any())
 60133            {
 60134                aggregateResult = ProcessGroupedAggregate(entityName, resultOfQuery, aggregates, groups);
 60135            }
 136            else
 84137            {
 84138                aggregateResult = new List<Entity>();
 84139                var ent = ProcessAggregatesForSingleGroup(entityName, resultOfQuery, aggregates);
 84140                aggregateResult.Add(ent);
 84141            }
 142
 144143            return OrderAggregateResult(xmlDoc, aggregateResult.AsQueryable());
 144144        }
 145
 146        private static List<Entity> OrderAggregateResult(XDocument xmlDoc, IQueryable<Entity> result)
 144147        {
 144148            var ns = xmlDoc.Root.Name.Namespace;
 336149            foreach (var order in
 144150                xmlDoc.Root.Element(ns + "entity")
 144151                .Elements(ns + "order"))
 24152            {
 24153                 var alias = order.GetAttribute("alias")?.Value;
 154
 155                // These error is also thrown by CRM
 24156                 if (order.GetAttribute("attribute") != null)
 0157                {
 0158                    throw new Exception("An attribute cannot be specified for an order clause for an aggregate Query. Us
 159                }
 24160                 if (string.IsNullOrEmpty("alias"))
 0161                {
 0162                    throw new Exception("An alias is required for an order clause for an aggregate Query.");
 163                }
 164
 24165                 if (order.IsAttributeTrue("descending"))
 12166                    result = result.OrderByDescending(e => e.Attributes.ContainsKey(alias) ? e.Attributes[alias] : null,
 167                else
 12168                    result = result.OrderBy(e => e.Attributes.ContainsKey(alias) ? e.Attributes[alias] : null, new XrmOr
 24169            }
 170
 144171            return result.ToList();
 144172        }
 173
 174        private static Entity ProcessAggregatesForSingleGroup(string entityName, IEnumerable<Entity> entities, IList<Fet
 234175        {
 234176            var ent = new Entity(entityName);
 177
 1170178            foreach (var agg in aggregates)
 234179            {
 234180                var val = agg.Process(entities);
 234181                 if (val != null)
 222182                {
 222183                    ent[agg.OutputAlias] = new AliasedValue(null, agg.Attribute, val);
 222184                }
 185                else
 12186                {
 187                    //if the aggregate value cannot be calculated
 188                    //CRM still returns an alias
 12189                    ent[agg.OutputAlias] = new AliasedValue(null, agg.Attribute, null);
 12190                }
 234191            }
 192
 234193            return ent;
 234194        }
 195
 196        private static List<Entity> ProcessGroupedAggregate(string entityName, IList<Entity> resultOfQuery, IList<FetchA
 60197        {
 198            // Group by the groupBy-attribute
 60199            var grouped = resultOfQuery.GroupBy(e =>
 282200            {
 282201                return groups
 504202                    .Select(g => g.Process(e))
 282203                    .ToArray();
 282204            }, new ArrayComparer());
 205
 206            // Perform aggregates in each group
 60207            var result = new List<Entity>();
 480208            foreach (var g in grouped)
 150209            {
 150210                var firstInGroup = g.First();
 211
 212                // Find the aggregates values in the group
 150213                var ent = ProcessAggregatesForSingleGroup(entityName, g, aggregates);
 214
 215                // Find the group values
 600216                 for (var rule = 0; rule < groups.Count; ++rule)
 150217                {
 150218                     if (g.Key[rule] != null)
 144219                    {
 144220                        object value = g.Key[rule];
 144221                         ent[groups[rule].OutputAlias] = new AliasedValue(null, groups[rule].Attribute, value is Comparab
 144222                    }
 150223                }
 224
 150225                result.Add(ent);
 150226            }
 227
 60228            return result;
 60229        }
 230
 231        private abstract class FetchAggregate
 232        {
 1536233            public string Attribute { get; set; }
 378234            public string OutputAlias { get; set; }
 235
 236            public object Process(IEnumerable<Entity> entities)
 234237            {
 234238                return AggregateValues(entities.Select(e =>
 840239                     e.Contains(Attribute) ? e[Attribute] : null
 234240                ));
 234241            }
 242
 243            protected abstract object AggregateValues(IEnumerable<object> values);
 244        }
 245
 246        private abstract class AliasedAggregate : FetchAggregate
 247        {
 248            protected override object AggregateValues(IEnumerable<object> values)
 96249            {
 504250                var lst = values.Where(x => x != null);
 96251                bool alisedValue = lst.FirstOrDefault() is AliasedValue;
 96252                 if (alisedValue)
 6253                {
 12254                     lst = lst.Select(x => (x as AliasedValue)?.Value);
 6255                }
 256
 96257                return AggregateAliasedValues(lst);
 96258            }
 259
 260            protected abstract object AggregateAliasedValues(IEnumerable<object> values);
 261        }
 262
 263        private class CountAggregate : FetchAggregate
 264        {
 265            protected override object AggregateValues(IEnumerable<object> values)
 138266            {
 138267                return values.Count();
 138268            }
 269        }
 270
 271        private class CountColumnAggregate : AliasedAggregate
 272        {
 273            protected override object AggregateAliasedValues(IEnumerable<object> values)
 18274            {
 42275                return values.Where(x => x != null).Count();
 18276            }
 277        }
 278
 279        private class CountDistinctAggregate : AliasedAggregate
 280        {
 281            protected override object AggregateAliasedValues(IEnumerable<object> values)
 6282            {
 42283                return values.Where(x => x != null).Distinct().Count();
 6284            }
 285        }
 286
 287        private class MinAggregate : AliasedAggregate
 288        {
 289            protected override object AggregateAliasedValues(IEnumerable<object> values)
 18290            {
 96291                var lst = values.Where(x => x != null);
 18292                 if (!lst.Any()) return null;
 293
 36294                var firstValue = lst.Where(x => x != null).First();
 18295                var valType = firstValue.GetType();
 296
 18297                 if (valType == typeof(decimal) || valType == typeof(decimal?))
 0298                {
 0299                    return lst.Min(x => (decimal)x);
 300                }
 301
 18302                 if (valType == typeof(Money))
 0303                {
 0304                    return new Money(lst.Min(x => (x as Money).Value));
 305                }
 306
 18307                 if (valType == typeof(int) || valType == typeof(int?))
 6308                {
 18309                    return lst.Min(x => (int)x);
 310                }
 311
 12312                 if (valType == typeof(float) || valType == typeof(float?))
 0313                {
 0314                    return lst.Min(x => (float)x);
 315                }
 316
 12317                 if (valType == typeof(double) || valType == typeof(double?))
 0318                {
 0319                    return lst.Min(x => (double)x);
 320                }
 321
 12322                 if (valType == typeof(DateTime) || valType == typeof(DateTime?))
 12323                {
 42324                    return lst.Min(x => (DateTime)x);
 325                }
 326
 0327                throw new Exception("Unhndled property type '" + valType.FullName + "' in 'min' aggregate");
 18328            }
 329        }
 330
 331        private class MaxAggregate : AliasedAggregate
 332        {
 333            protected override object AggregateAliasedValues(IEnumerable<object> values)
 18334            {
 96335                var lst = values.Where(x => x != null);
 18336                 if (!lst.Any()) return null;
 337
 18338                var firstValue = lst.First();
 18339                var valType = firstValue.GetType();
 340
 18341                 if (valType == typeof(decimal) || valType == typeof(decimal?))
 6342                {
 18343                    return lst.Max(x => (decimal)x);
 344                }
 345
 12346                 if (valType == typeof(Money))
 0347                {
 0348                    return new Money(lst.Max(x => (x as Money).Value));
 349                }
 350
 12351                 if (valType == typeof(int) || valType == typeof(int?))
 0352                {
 0353                    return lst.Max(x => (int)x);
 354                }
 355
 12356                 if (valType == typeof(float) || valType == typeof(float?))
 0357                {
 0358                    return lst.Max(x => (float)x);
 359                }
 360
 12361                 if (valType == typeof(double) || valType == typeof(double?))
 0362                {
 0363                    return lst.Max(x => (double)x);
 364                }
 365
 12366                 if (valType == typeof(DateTime) || valType == typeof(DateTime?))
 12367                {
 42368                    return lst.Max(x => (DateTime)x);
 369                }
 370
 0371                throw new Exception("Unhndled property type '" + valType.FullName + "' in 'max' aggregate");
 18372            }
 373        }
 374
 375        private class AvgAggregate : AliasedAggregate
 376        {
 377            protected override object AggregateAliasedValues(IEnumerable<object> values)
 12378            {
 36379                var lst = values.Where(x => x != null);
 18380                 if (!lst.Any()) return null;
 381
 6382                var firstValue = lst.First();
 6383                var valType = firstValue.GetType();
 384
 6385                 if (valType == typeof(decimal) || valType == typeof(decimal?))
 6386                {
 18387                    return lst.Average(x => (decimal)x);
 388                }
 389
 0390                 if (valType == typeof(Money))
 0391                {
 0392                    return new Money(lst.Average(x => (x as Money).Value));
 393                }
 394
 0395                 if (valType == typeof(int) || valType == typeof(int?))
 0396                {
 0397                    return lst.Average(x => (int)x);
 398                }
 399
 0400                 if (valType == typeof(float) || valType == typeof(float?))
 0401                {
 0402                    return lst.Average(x => (float)x);
 403                }
 404
 0405                 if (valType == typeof(double) || valType == typeof(double?))
 0406                {
 0407                    return lst.Average(x => (double)x);
 408                }
 409
 0410                throw new Exception("Unhndled property type '" + valType.FullName + "' in 'avg' aggregate");
 12411            }
 412        }
 413
 414        private class SumAggregate : AliasedAggregate
 415        {
 416            protected override object AggregateAliasedValues(IEnumerable<object> values)
 24417            {
 90418                var lst = values.ToList().Where(x => x != null);
 419                // TODO: Check these cases in CRM proper
 30420                 if (!lst.Any()) return null;
 421
 18422                var valType = lst.First().GetType();
 423
 18424                 if (valType == typeof(decimal) || valType == typeof(decimal?))
 0425                {
 0426                     return lst.Sum(x => x as decimal? ?? 0m);
 427                }
 18428                 if (valType == typeof(Money))
 12429                {
 30430                     return new Money(lst.Sum(x => (x as Money)?.Value ?? 0m));
 431                }
 432
 6433                 if (valType == typeof(int) || valType == typeof(int?))
 6434                {
 18435                     return lst.Sum(x => x as int? ?? 0);
 436                }
 437
 0438                 if (valType == typeof(float) || valType == typeof(float?))
 0439                {
 0440                     return lst.Sum(x => x as float? ?? 0f);
 441                }
 442
 0443                 if (valType == typeof(double) || valType == typeof(double?))
 0444                {
 0445                     return lst.Sum(x => x as double? ?? 0d);
 446                }
 447
 0448                throw new Exception("Unhndled property type '" + valType.FullName + "' in 'sum' aggregate");
 24449            }
 450        }
 451
 452        private abstract class FetchGrouping
 453        {
 642454            public string Attribute { get; set; }
 204455            public string OutputAlias { get; set; }
 456
 457            public IComparable Process(Entity entity)
 222458            {
 222459                 var attr = entity.Contains(Attribute) ? entity[Attribute] : null;
 222460                return FindGroupValue(attr);
 222461            }
 462
 463            public abstract IComparable FindGroupValue(object attributeValue);
 464        }
 465
 466        /// <summary>
 467        /// Used to compare array of objects, in order to group by a variable number of conditions.
 468        /// </summary>
 469        private class ArrayComparer : IEqualityComparer<IComparable[]>
 470        {
 471            public bool Equals(IComparable[] x, IComparable[] y)
 72472            {
 72473                return x.SequenceEqual(y);
 72474            }
 475
 476            public int GetHashCode(IComparable[] obj)
 222477            {
 222478                int result = 0;
 1110479                foreach (IComparable x in obj)
 222480                {
 222481                     result ^= x == null ? 0 : x.GetHashCode();
 222482                }
 222483                return result;
 222484            }
 485        }
 486
 487        private class ComparableEntityReference : IComparable
 488        {
 102489            public EntityReference entityReference { get; private set; }
 490
 18491            public ComparableEntityReference(EntityReference entityReference)
 18492            {
 18493                this.entityReference = entityReference;
 18494            }
 495
 496            int IComparable.CompareTo(object obj)
 0497            {
 0498                 return Equals(obj) ? 0 : 1;
 0499            }
 500
 501            public override bool Equals(object obj)
 6502            {
 503                EntityReference other;
 6504                 if (obj is EntityReference)
 0505                {
 0506                    other = obj as EntityReference;
 0507                }
 6508                 else if (obj is ComparableEntityReference)
 6509                {
 6510                    other = (obj as ComparableEntityReference).entityReference;
 6511                }
 512                else
 0513                {
 0514                    return false;
 515                }
 6516                return entityReference.Id == other.Id && entityReference.LogicalName == other.LogicalName;
 6517            }
 518
 519            public override int GetHashCode()
 18520            {
 18521                return (entityReference.LogicalName == null ? 0 : entityReference.LogicalName.GetHashCode()) ^ entityRef
 18522            }
 523        }
 524
 525        private class SimpleValueGroup : FetchGrouping
 526        {
 527            public override IComparable FindGroupValue(object attributeValue)
 54528            {
 54529                 if (attributeValue is EntityReference)
 18530                {
 18531                    return new ComparableEntityReference(attributeValue as EntityReference) as IComparable;
 532                }
 533                else
 36534                {
 36535                    return attributeValue as IComparable;
 536                }
 54537            }
 538        }
 539
 540        private enum DateGroupType
 541        {
 542            DateTime,
 543            Day,
 544            Week,
 545            Month,
 546            Quarter,
 547            Year
 548        }
 549
 550        private class DateTimeGroup : FetchGrouping
 551        {
 210552            public DateGroupType Type { get; set; }
 553
 554            public override IComparable FindGroupValue(object attributeValue)
 168555            {
 168556                 if (attributeValue == null) return null;
 557
 168558                 if (!(attributeValue is DateTime || attributeValue is DateTime?))
 0559                {
 0560                    throw new Exception("Can only do date grouping of DateTime values");
 561                }
 562
 168563                var d = attributeValue as DateTime?;
 564
 168565                 switch (Type)
 566                {
 567                    case DateGroupType.DateTime:
 0568                        return d;
 569
 570                    case DateGroupType.Day:
 24571                        return d?.Day;
 572
 573                    case DateGroupType.Week:
 0574                        var cal = System.Globalization.DateTimeFormatInfo.InvariantInfo;
 0575                        return cal.Calendar.GetWeekOfYear(d.Value, cal.CalendarWeekRule, cal.FirstDayOfWeek);
 576
 577                    case DateGroupType.Month:
 96578                        return d?.Month;
 579
 580                    case DateGroupType.Quarter:
 24581                        return (d?.Month + 2) / 3;
 582
 583                    case DateGroupType.Year:
 24584                        return d?.Year;
 585
 586                    default:
 0587                        throw new Exception("Unhandled date group type");
 588                }
 168589            }
 590        }
 591    }
 592}

C:\code\jordimontana82\fake-xrm-easy\FakeXrmEasy.Shared\XrmFakedContext.CodeActivities.cs

#LineLine coverage
 1using FakeItEasy;
 2using Microsoft.Xrm.Sdk;
 3using Microsoft.Xrm.Sdk.Workflow;
 4using System;
 5using System.Activities;
 6using System.Collections.Generic;
 7
 8namespace FakeXrmEasy
 9{
 10    public partial class XrmFakedContext : IXrmContext
 11    {
 12        public XrmFakedWorkflowContext GetDefaultWorkflowContext()
 4213        {
 4214             var userId = CallerId?.Id ?? Guid.NewGuid();
 4215             Guid businessUnitId = BusinessUnitId?.Id ?? Guid.NewGuid();
 16
 4217            return new XrmFakedWorkflowContext
 4218            {
 4219                Depth = 1,
 4220                IsExecutingOffline = false,
 4221                MessageName = "Create",
 4222                UserId = userId,
 4223                BusinessUnitId = businessUnitId,
 4224                InitiatingUserId = userId,
 4225                InputParameters = new ParameterCollection(),
 4226                OutputParameters = new ParameterCollection(),
 4227                SharedVariables = new ParameterCollection(),
 4228                PreEntityImages = new EntityImageCollection(),
 4229                PostEntityImages = new EntityImageCollection()
 4230            };
 4231        }
 32
 33        /// <summary>
 34        /// Executes a code activity against this context
 35        /// </summary>
 36        /// <typeparam name="T"></typeparam>
 37        public IDictionary<string, object> ExecuteCodeActivity<T>(Dictionary<string, object> inputs, T instance = null)
 38            where T : CodeActivity, new()
 3039        {
 3040            var wfContext = GetDefaultWorkflowContext();
 3041            return this.ExecuteCodeActivity(wfContext, inputs, instance);
 3042        }
 43
 44        public IDictionary<string, object> ExecuteCodeActivity<T>(Entity primaryEntity, Dictionary<string, object> input
 45            where T : CodeActivity, new()
 046        {
 047            var wfContext = GetDefaultWorkflowContext();
 048            wfContext.PrimaryEntityId = primaryEntity.Id;
 049            wfContext.PrimaryEntityName = primaryEntity.LogicalName;
 50
 051             if (inputs == null)
 052            {
 053                inputs = new Dictionary<string, object>();
 054            }
 55
 056            return this.ExecuteCodeActivity(wfContext, inputs, instance);
 057        }
 58
 59        /// <summary>
 60        /// Executes a code activity passing the primary entity
 61        /// </summary>
 62        /// <typeparam name="T"></typeparam>
 63        public IDictionary<string, object> ExecuteCodeActivity<T>(XrmFakedWorkflowContext wfContext, Dictionary<string, 
 64            where T : CodeActivity, new()
 3665        {
 3666            var debugText = "";
 67            try
 3668            {
 3669                debugText = "Creating instance..." + Environment.NewLine;
 3670                 if (instance == null)
 2471                {
 2472                    instance = new T();
 2473                }
 3674                var invoker = new WorkflowInvoker(instance);
 3675                debugText += "Invoker created" + Environment.NewLine;
 3676                debugText += "Adding extensions..." + Environment.NewLine;
 7277                invoker.Extensions.Add<ITracingService>(() => TracingService);
 7278                invoker.Extensions.Add<IWorkflowContext>(() => wfContext);
 3679                invoker.Extensions.Add(() =>
 7280                {
 7281                    var fakedServiceFactory = A.Fake<IOrganizationServiceFactory>();
 10882                    A.CallTo(() => fakedServiceFactory.CreateOrganizationService(A<Guid?>._)).ReturnsLazily((Guid? g) =>
 7283                    return fakedServiceFactory;
 7284                });
 7285                invoker.Extensions.Add<IServiceEndpointNotificationService>(() => GetFakedServiceEndpointNotificationSer
 86
 3687                debugText += "Adding extensions...ok." + Environment.NewLine;
 3688                debugText += "Invoking activity..." + Environment.NewLine;
 89
 3690                 if (inputs == null)
 091                {
 092                    inputs = new Dictionary<string, object>();
 093                }
 94
 3695                return invoker.Invoke(inputs);
 96            }
 097            catch (TypeLoadException exception)
 098            {
 099                 var typeName = exception.TypeName != null ? exception.TypeName : "(null)";
 0100                throw new TypeLoadException($"When loading type: {typeName}.{exception.Message}in domain directory: {App
 101            }
 36102        }
 103    }
 104}

C:\code\jordimontana82\fake-xrm-easy\FakeXrmEasy.Shared\XrmFakedContext.Crud.cs

#LineLine coverage
 1using FakeItEasy;
 2using FakeXrmEasy.Extensions;
 3using Microsoft.Xrm.Sdk;
 4using Microsoft.Xrm.Sdk.Messages;
 5using Microsoft.Xrm.Sdk.Query;
 6using System;
 7using System.Collections.Generic;
 8using System.Linq;
 9using System.Reflection;
 10using System.ServiceModel;
 11
 12namespace FakeXrmEasy
 13{
 14    public partial class XrmFakedContext : IXrmContext
 15    {
 16        protected const int EntityActiveStateCode = 0;
 17        protected const int EntityInactiveStateCode = 1;
 18
 9727419        public bool ValidateReferences { get; set; }
 20
 21        #region CRUD
 22        public Guid GetRecordUniqueId(EntityReference record, bool validate = true)
 112923        {
 112924             if (string.IsNullOrWhiteSpace(record.LogicalName))
 2425            {
 2426                throw new InvalidOperationException("The entity logical name must not be null or empty.");
 27            }
 28
 29            // Don't fail with invalid operation exception, if no record of this entity exists, but entity is known
 110530             if (!Data.ContainsKey(record.LogicalName) && !EntityMetadata.ContainsKey(record.LogicalName))
 2731            {
 2732                 if (ProxyTypesAssembly == null)
 633                {
 634                    throw new InvalidOperationException($"The entity logical name {record.LogicalName} is not valid.");
 35                }
 36
 4237                 if (!ProxyTypesAssembly.GetTypes().Any(type => FindReflectedType(record.LogicalName) != null))
 038                {
 039                    throw new InvalidOperationException($"The entity logical name {record.LogicalName} is not valid.");
 40                }
 2141            }
 42
 43#if !FAKE_XRM_EASY && !FAKE_XRM_EASY_2013 && !FAKE_XRM_EASY_2015
 58044             if (record.Id == Guid.Empty && record.HasKeyAttributes())
 3345            {
 3346                 if (EntityMetadata.ContainsKey(record.LogicalName))
 3047                {
 3048                    var entityMetadata = EntityMetadata[record.LogicalName];
 12349                    foreach (var key in entityMetadata.Keys)
 3050                    {
 6051                         if (record.KeyAttributes.Keys.Count == key.KeyAttributes.Length && key.KeyAttributes.All(x => re
 3052                        {
 3053                             if (Data.ContainsKey(record.LogicalName))
 2754                            {
 9355                                var matchedRecord = Data[record.LogicalName].Values.SingleOrDefault(x => record.KeyAttri
 2756                                 if (matchedRecord != null)
 2757                                {
 2758                                    return matchedRecord.Id;
 59                                }
 060                            }
 361                             if (validate)
 062                            {
 063                                new FaultException<OrganizationServiceFault>(new OrganizationServiceFault(), $"{record.L
 064                            }
 365                        }
 366                    }
 367                }
 668                 if (validate)
 369                {
 370                    throw new InvalidOperationException($"The requested key attributes do not exist for the entity {reco
 71                }
 372            }
 73#endif
 74            /*
 75            if (validate && record.Id == Guid.Empty)
 76            {
 77                throw new InvalidOperationException("The id must not be empty.");
 78            }
 79            */
 80
 106981            return record.Id;
 109682        }
 83
 84        /// <summary>
 85        /// A fake retrieve method that will query the FakedContext to retrieve the specified
 86        /// entity and Guid, or null, if the entity was not found
 87        /// </summary>
 88        /// <param name="context">The faked context</param>
 89        /// <param name="fakedService">The faked service where the Retrieve method will be faked</param>
 90        /// <returns></returns>
 91        protected static void FakeRetrieve(XrmFakedContext context, IOrganizationService fakedService)
 320392        {
 320393            A.CallTo(() => fakedService.Retrieve(A<string>._, A<Guid>._, A<ColumnSet>._))
 320394                .ReturnsLazily((string entityName, Guid id, ColumnSet columnSet) =>
 388595                {
 388596                    RetrieveRequest retrieveRequest = new RetrieveRequest()
 388597                    {
 388598                        Target = new EntityReference() { LogicalName = entityName, Id = id },
 388599                        ColumnSet = columnSet
 3885100                    };
 3885101                    var executor = context.FakeMessageExecutors[typeof(RetrieveRequest)];
 3203102
 3885103                    RetrieveResponse retrieveResponse = (RetrieveResponse)executor.Execute(retrieveRequest, context);
 3203104
 3819105                    return retrieveResponse.Entity;
 3819106                });
 3203107        }
 108        /// <summary>
 109        /// Fakes the Create message
 110        /// </summary>
 111        /// <param name="context"></param>
 112        /// <param name="fakedService"></param>
 113        protected static void FakeCreate(XrmFakedContext context, IOrganizationService fakedService)
 3203114        {
 3203115            A.CallTo(() => fakedService.Create(A<Entity>._))
 3203116                .ReturnsLazily((Entity e) =>
 4467117                {
 4467118                    return context.CreateEntity(e);
 4407119                });
 3203120        }
 121
 122        protected static void FakeUpdate(XrmFakedContext context, IOrganizationService fakedService)
 3203123        {
 3203124            A.CallTo(() => fakedService.Update(A<Entity>._))
 3203125                .Invokes((Entity e) =>
 3539126                {
 3539127                    context.UpdateEntity(e);
 3503128                });
 3203129        }
 130
 131        protected void UpdateEntity(Entity e)
 336132        {
 336133             if (e == null)
 6134            {
 6135                throw new InvalidOperationException("The entity must not be null");
 136            }
 330137            e = e.Clone(e.GetType());
 330138            var reference = e.ToEntityReferenceWithKeyAttributes();
 330139            e.Id = GetRecordUniqueId(reference);
 140
 141            // Update specific validations: The entity record must exist in the context
 324142             if (Data.ContainsKey(e.LogicalName) &&
 324143                Data[e.LogicalName].ContainsKey(e.Id))
 306144            {
 306145                 if (this.UsePipelineSimulation)
 24146                {
 24147                    ExecutePipelineStage("Update", ProcessingStepStage.Preoperation, ProcessingStepMode.Synchronous, e);
 24148                }
 149
 150                // Add as many attributes to the entity as the ones received (this will keep existing ones)
 306151                var cachedEntity = Data[e.LogicalName][e.Id];
 4210152                foreach (var sAttributeName in e.Attributes.Keys.ToList())
 1649153                {
 1649154                    var attribute = e[sAttributeName];
 1649155                     if (attribute == null)
 6156                    {
 6157                        cachedEntity.Attributes.Remove(sAttributeName);
 6158                    }
 1643159                     else if (attribute is DateTime)
 323160                    {
 323161                        cachedEntity[sAttributeName] = ConvertToUtc((DateTime)e[sAttributeName]);
 323162                    }
 163                    else
 1320164                    {
 1320165                         if (attribute is EntityReference && ValidateReferences)
 51166                        {
 51167                            var target = (EntityReference)e[sAttributeName];
 51168                            attribute = ResolveEntityReference(target);
 45169                        }
 1314170                        cachedEntity[sAttributeName] = attribute;
 1314171                    }
 1643172                }
 173
 174                // Update ModifiedOn
 300175                cachedEntity["modifiedon"] = DateTime.UtcNow;
 300176                cachedEntity["modifiedby"] = CallerId;
 177
 300178                 if (this.UsePipelineSimulation)
 24179                {
 24180                    ExecutePipelineStage("Update", ProcessingStepStage.Postoperation, ProcessingStepMode.Synchronous, e)
 181
 24182                    var clone = e.Clone(e.GetType());
 24183                    ExecutePipelineStage("Update", ProcessingStepStage.Postoperation, ProcessingStepMode.Asynchronous, c
 24184                }
 300185            }
 186            else
 18187            {
 188                // The entity record was not found, return a CRM-ish update error message
 18189                throw new FaultException<OrganizationServiceFault>(new OrganizationServiceFault(), $"{e.LogicalName} wit
 190            }
 300191        }
 192
 193        protected EntityReference ResolveEntityReference(EntityReference er)
 225194        {
 225195             if (!Data.ContainsKey(er.LogicalName) || !Data[er.LogicalName].ContainsKey(er.Id))
 24196            {
 24197                 if (er.Id == Guid.Empty && er.HasKeyAttributes())
 12198                {
 12199                    return ResolveEntityReferenceByAlternateKeys(er);
 200                }
 201                else
 12202                {
 12203                    throw new FaultException<OrganizationServiceFault>(new OrganizationServiceFault(), $"{er.LogicalName
 204                }
 205            }
 201206            return er;
 213207        }
 208
 209        protected EntityReference ResolveEntityReferenceByAlternateKeys(EntityReference er)
 12210        {
 12211            var resolvedId = GetRecordUniqueId(er);
 212
 12213            return new EntityReference()
 12214            {
 12215                LogicalName = er.LogicalName,
 12216                Id = resolvedId
 12217            };
 12218        }
 219        /// <summary>
 220        /// Fakes the delete method. Very similar to the Retrieve one
 221        /// </summary>
 222        /// <param name="context"></param>
 223        /// <param name="fakedService"></param>
 224        protected static void FakeDelete(XrmFakedContext context, IOrganizationService fakedService)
 3203225        {
 3203226            A.CallTo(() => fakedService.Delete(A<string>._, A<Guid>._))
 3203227                .Invokes((string entityName, Guid id) =>
 3347228                {
 3347229                     if (string.IsNullOrWhiteSpace(entityName))
 3221230                    {
 3221231                        throw new InvalidOperationException("The entity logical name must not be null or empty.");
 3203232                    }
 3203233
 3329234                     if (id == Guid.Empty)
 3209235                    {
 3209236                        throw new InvalidOperationException("The id must not be empty.");
 3203237                    }
 3203238
 3323239                    var entityReference = new EntityReference(entityName, id);
 3203240
 3323241                    context.DeleteEntity(entityReference);
 3305242                });
 3203243        }
 244
 245        protected void DeleteEntity(EntityReference er)
 120246        {
 247            // Don't fail with invalid operation exception, if no record of this entity exists, but entity is known
 120248             if (!this.Data.ContainsKey(er.LogicalName))
 12249            {
 12250                 if (this.ProxyTypesAssembly == null)
 6251                {
 6252                    throw new InvalidOperationException($"The entity logical name {er.LogicalName} is not valid.");
 253                }
 254
 12255                 if (!this.ProxyTypesAssembly.GetTypes().Any(type => this.FindReflectedType(er.LogicalName) != null))
 0256                {
 0257                    throw new InvalidOperationException($"The entity logical name {er.LogicalName} is not valid.");
 258                }
 6259            }
 260
 261            // Entity logical name exists, so , check if the requested entity exists
 114262             if (this.Data.ContainsKey(er.LogicalName) && this.Data[er.LogicalName] != null &&
 114263                this.Data[er.LogicalName].ContainsKey(er.Id))
 102264            {
 102265                 if (this.UsePipelineSimulation)
 18266                {
 18267                    ExecutePipelineStage("Delete", ProcessingStepStage.Preoperation, ProcessingStepMode.Synchronous, er)
 18268                }
 269
 270                // Entity found => return only the subset of columns specified or all of them
 102271                this.Data[er.LogicalName].Remove(er.Id);
 272
 102273                 if (this.UsePipelineSimulation)
 18274                {
 18275                    ExecutePipelineStage("Delete", ProcessingStepStage.Postoperation, ProcessingStepMode.Synchronous, er
 18276                    ExecutePipelineStage("Delete", ProcessingStepStage.Postoperation, ProcessingStepMode.Asynchronous, e
 18277                }
 102278            }
 279            else
 12280            {
 281                // Entity not found in the context => throw not found exception
 282                // The entity record was not found, return a CRM-ish update error message
 12283                throw new FaultException<OrganizationServiceFault>(new OrganizationServiceFault(), $"{er.LogicalName} wi
 284            }
 102285        }
 286        #endregion
 287
 288        #region Other protected methods
 289        protected void EnsureEntityNameExistsInMetadata(string sEntityName)
 6119290        {
 6599291             if (Relationships.Values.Any(value => new[] { value.Entity1LogicalName, value.Entity2LogicalName, value.Inte
 456292            {
 456293                return;
 294            }
 295
 296            // Entity metadata is checked differently when we are using a ProxyTypesAssembly => we can infer that from t
 5663297             if (ProxyTypesAssembly != null)
 4124298            {
 4124299                var subClassType = FindReflectedType(sEntityName);
 4124300                 if (subClassType == null)
 0301                {
 0302                    throw new Exception($"Entity {sEntityName} does not exist in the metadata cache");
 303                }
 4124304            }
 305            //else if (!Data.ContainsKey(sEntityName))
 306            //{
 307            //    //No Proxy Types Assembly
 308            //    throw new Exception(string.Format("Entity {0} does not exist in the metadata cache", sEntityName));
 309            //};
 6119310        }
 311
 312        protected void AddEntityDefaultAttributes(Entity e)
 28494313        {
 314            // Add createdon, modifiedon, createdby, modifiedby properties
 28494315             if (CallerId == null)
 2980316            {
 2980317                CallerId = new EntityReference("systemuser", Guid.NewGuid()); // Create a new instance by default
 2980318                 if (ValidateReferences)
 33319                {
 33320                     if (!Data.ContainsKey("systemuser"))
 33321                    {
 33322                        Data.Add("systemuser", new Dictionary<Guid, Entity>());
 33323                    }
 33324                     if (!Data["systemuser"].ContainsKey(CallerId.Id))
 33325                    {
 33326                        Data["systemuser"].Add(CallerId.Id, new Entity("systemuser") { Id = CallerId.Id });
 33327                    }
 33328                }
 329
 2980330            }
 331
 28494332            var isManyToManyRelationshipEntity = e.LogicalName != null && this.Relationships.ContainsKey(e.LogicalName);
 333
 28494334            EntityInitializerService.Initialize(e, CallerId.Id, this, isManyToManyRelationshipEntity);
 28494335        }
 336
 337        protected void ValidateEntity(Entity e)
 29782338        {
 29782339             if (e == null)
 0340            {
 0341                throw new InvalidOperationException("The entity must not be null");
 342            }
 343
 344            // Validate the entity
 29782345             if (string.IsNullOrWhiteSpace(e.LogicalName))
 12346            {
 12347                throw new InvalidOperationException("The LogicalName property must not be empty");
 348            }
 349
 29770350             if (e.Id == Guid.Empty)
 6351            {
 6352                throw new InvalidOperationException("The Id property must not be empty");
 353            }
 29764354        }
 355
 356        protected internal Guid CreateEntity(Entity e)
 1294357        {
 1294358             if (e == null)
 6359            {
 6360                throw new InvalidOperationException("The entity must not be null");
 361            }
 362
 1288363            var clone = e.Clone(e.GetType());
 364
 1288365             if (clone.Id == Guid.Empty)
 994366            {
 994367                clone.Id = Guid.NewGuid(); // Add default guid if none present
 994368            }
 369
 370            // Hack for Dynamic Entities where the Id property doesn't populate the "entitynameid" primary key
 1288371            var primaryKeyAttribute = $"{e.LogicalName}id";
 1288372             if (!clone.Attributes.ContainsKey(primaryKeyAttribute))
 784373            {
 784374                clone[primaryKeyAttribute] = clone.Id;
 784375            }
 376
 1288377            ValidateEntity(clone);
 378
 379            // Create specific validations
 1282380             if (clone.Id != Guid.Empty && Data.ContainsKey(clone.LogicalName) &&
 1282381                Data[clone.LogicalName].ContainsKey(clone.Id))
 24382            {
 24383                throw new InvalidOperationException($"There is already a record of entity {clone.LogicalName} with id {c
 384            }
 385
 386            // Create specific validations
 1258387             if (clone.Attributes.ContainsKey("statecode"))
 6388            {
 6389                throw new InvalidOperationException($"When creating an entity with logical name '{clone.LogicalName}', o
 390            }
 391
 1252392            AddEntityWithDefaults(clone, false, this.UsePipelineSimulation);
 393
 1240394             if (e.RelatedEntities.Count > 0)
 18395            {
 84396                foreach (var relationshipSet in e.RelatedEntities)
 18397                {
 18398                    var relationship = relationshipSet.Key;
 399
 18400                    var entityReferenceCollection = new EntityReferenceCollection();
 401
 114402                    foreach (var relatedEntity in relationshipSet.Value.Entities)
 30403                    {
 30404                        var relatedId = CreateEntity(relatedEntity);
 30405                        entityReferenceCollection.Add(new EntityReference(relatedEntity.LogicalName, relatedId));
 30406                    }
 407
 18408                     if (FakeMessageExecutors.ContainsKey(typeof(AssociateRequest)))
 18409                    {
 18410                        var request = new AssociateRequest
 18411                        {
 18412                            Target = clone.ToEntityReference(),
 18413                            Relationship = relationship,
 18414                            RelatedEntities = entityReferenceCollection
 18415                        };
 18416                        FakeMessageExecutors[typeof(AssociateRequest)].Execute(request, this);
 12417                    }
 418                    else
 0419                    {
 0420                        throw PullRequestException.NotImplementedOrganizationRequest(typeof(AssociateRequest));
 421                    }
 12422                }
 12423            }
 424
 1234425            return clone.Id;
 1234426        }
 427
 428        protected internal void AddEntityWithDefaults(Entity e, bool clone = false, bool usePluginPipeline = false)
 28494429        {
 430            // Create the entity with defaults
 28494431            AddEntityDefaultAttributes(e);
 432
 28494433             if (usePluginPipeline)
 42434            {
 42435                ExecutePipelineStage("Create", ProcessingStepStage.Preoperation, ProcessingStepMode.Synchronous, e);
 42436            }
 437
 438            // Store
 28494439             AddEntity(clone ? e.Clone(e.GetType()) : e);
 440
 28470441             if (usePluginPipeline)
 42442            {
 42443                ExecutePipelineStage("Create", ProcessingStepStage.Postoperation, ProcessingStepMode.Synchronous, e);
 42444                ExecutePipelineStage("Create", ProcessingStepStage.Postoperation, ProcessingStepMode.Asynchronous, e);
 42445            }
 28470446        }
 447
 448        protected internal void AddEntity(Entity e)
 28494449        {
 450            //Automatically detect proxy types assembly if an early bound type was used.
 28494451             if (ProxyTypesAssembly == null &&
 28494452                e.GetType().IsSubclassOf(typeof(Entity)))
 1438453            {
 1438454                ProxyTypesAssembly = Assembly.GetAssembly(e.GetType());
 1438455            }
 456
 28494457            ValidateEntity(e); //Entity must have a logical name and an Id
 458
 532218459            foreach (var sAttributeName in e.Attributes.Keys.ToList())
 223389460            {
 223389461                var attribute = e[sAttributeName];
 223389462                 if (attribute is DateTime)
 57964463                {
 57964464                    e[sAttributeName] = ConvertToUtc((DateTime)e[sAttributeName]);
 57964465                }
 223389466                 if (attribute is EntityReference && ValidateReferences)
 174467                {
 174468                    var target = (EntityReference)e[sAttributeName];
 174469                    e[sAttributeName] = ResolveEntityReference(target);
 168470                }
 223383471            }
 472
 473            //Add the entity collection
 28476474             if (!Data.ContainsKey(e.LogicalName))
 4921475            {
 4921476                Data.Add(e.LogicalName, new Dictionary<Guid, Entity>());
 4921477            }
 478
 28476479             if (Data[e.LogicalName].ContainsKey(e.Id))
 6480            {
 6481                Data[e.LogicalName][e.Id] = e;
 6482            }
 483            else
 28470484            {
 28470485                Data[e.LogicalName].Add(e.Id, e);
 28470486            }
 487
 488            //Update metadata for that entity
 28476489             if (!AttributeMetadataNames.ContainsKey(e.LogicalName))
 4921490                AttributeMetadataNames.Add(e.LogicalName, new Dictionary<string, string>());
 491
 492            //Update attribute metadata
 28476493             if (ProxyTypesAssembly != null)
 6404494            {
 495                //If the context is using a proxy types assembly then we can just guess the metadata from the generated 
 6404496                var type = FindReflectedType(e.LogicalName);
 6404497                 if (type != null)
 6398498                {
 6398499                    var props = type.GetProperties();
 3221870500                    foreach (var p in props)
 1601338501                    {
 1601338502                         if (!AttributeMetadataNames[e.LogicalName].ContainsKey(p.Name))
 769860503                            AttributeMetadataNames[e.LogicalName].Add(p.Name, p.Name);
 1601338504                    }
 6398505                }
 506                else
 6507                    throw new Exception(string.Format("Couldnt find reflected type for {0}", e.LogicalName));
 508
 6398509            }
 510            else
 22072511            {
 512                //If dynamic entities are being used, then the only way of guessing if a property exists is just by chec
 513                //if the entity has the attribute in the dictionary
 408142514                foreach (var attKey in e.Attributes.Keys)
 170963515                {
 170963516                     if (!AttributeMetadataNames[e.LogicalName].ContainsKey(attKey))
 15837517                        AttributeMetadataNames[e.LogicalName].Add(attKey, attKey);
 170963518                }
 22072519            }
 520
 28470521        }
 522
 523        protected internal bool AttributeExistsInMetadata(string sEntityName, string sAttributeName)
 6384524        {
 6696525            var relationships = this.Relationships.Values.Where(value => new[] { value.Entity1LogicalName, value.Entity2
 6660526             if (relationships.Any(e => e.Entity1Attribute == sAttributeName || e.Entity2Attribute == sAttributeName))
 102527            {
 102528                return true;
 529            }
 530
 531            //Early bound types
 6282532             if (ProxyTypesAssembly != null)
 5286533            {
 534                //Check if attribute exists in the early bound type
 5286535                var earlyBoundType = FindReflectedType(sEntityName);
 5286536                 if (earlyBoundType != null)
 5286537                {
 538                    //Get that type properties
 5286539                    var attributeFound = earlyBoundType
 5286540                        .GetProperties()
 490899541                        .Where(pi => pi.GetCustomAttributes(typeof(AttributeLogicalNameAttribute), true).Length > 0)
 485552542                        .Where(pi => (pi.GetCustomAttributes(typeof(AttributeLogicalNameAttribute), true)[0] as Attribut
 5286543                        .FirstOrDefault();
 544
 5286545                     if (attributeFound != null)
 5262546                        return true;
 547
 24548                     if (attributeFound == null && EntityMetadata.ContainsKey(sEntityName))
 0549                    {
 550                        //Try with metadata
 0551                        return AttributeExistsInInjectedMetadata(sEntityName, sAttributeName);
 552                    }
 553                    else
 24554                    {
 24555                        return false;
 556                    }
 557                }
 558                //Try with metadata
 0559                return false;
 560            }
 561
 996562             if (EntityMetadata.ContainsKey(sEntityName))
 0563            {
 564                //Try with metadata
 0565                return AttributeExistsInInjectedMetadata(sEntityName, sAttributeName);
 566            }
 567
 568            //Dynamic entities and not entity metadata injected for entity => just return true if not found
 996569            return true;
 6384570        }
 571
 572        protected internal bool AttributeExistsInInjectedMetadata(string sEntityName, string sAttributeName)
 0573        {
 0574            var attributeInMetadata = FindAttributeTypeInInjectedMetadata(sEntityName, sAttributeName);
 0575            return attributeInMetadata != null;
 0576        }
 577
 578        protected internal DateTime ConvertToUtc(DateTime attribute)
 58287579        {
 58287580            return DateTime.SpecifyKind(attribute, DateTimeKind.Utc);
 58287581        }
 582        #endregion
 583    }
 584}

C:\code\jordimontana82\fake-xrm-easy\FakeXrmEasy.Shared\XrmFakedContext.cs

#LineLine coverage
 1using FakeItEasy;
 2using FakeXrmEasy.FakeMessageExecutors;
 3using FakeXrmEasy.Permissions;
 4using FakeXrmEasy.Services;
 5using Microsoft.Xrm.Sdk;
 6using Microsoft.Xrm.Sdk.Messages;
 7using Microsoft.Xrm.Sdk.Metadata;
 8using Microsoft.Xrm.Sdk.Query;
 9using System;
 10using System.Collections.Generic;
 11using System.Linq;
 12using System.Reflection;
 13
 14namespace FakeXrmEasy
 15{
 16    /// <summary>
 17    /// A fake context that stores In-Memory entites indexed by logical name and then Entity records, simulating
 18    /// how entities are persisted in Tables (with the logical name) and then the records themselves
 19    /// where the Primary Key is the Guid
 20    /// </summary>
 21    public partial class XrmFakedContext : IXrmContext
 22    {
 1181323        protected internal IOrganizationService Service { get; set; }
 24
 25        private IServiceEndpointNotificationService _serviceEndpointNotificationService;
 26
 446327        private readonly Lazy<XrmFakedTracingService> _tracingService = new Lazy<XrmFakedTracingService>(() => new XrmFa
 28
 29        /// <summary>
 30        /// All proxy type assemblies available on mocked database.
 31        /// </summary>
 14136532        private List<Assembly> ProxyTypesAssemblies { get; set; }
 33
 27134        protected internal XrmFakedTracingService TracingService => _tracingService.Value;
 35
 533236        protected internal bool Initialised { get; set; }
 37
 11453238        public Dictionary<string, Dictionary<Guid, Entity>> Data { get; set; }
 39
 40        /// <summary>
 41        /// Specify which assembly is used to search for early-bound proxy
 42        /// types when used within simulated CRM context.
 43        ///
 44        /// If you want to specify multiple different assemblies for early-bound
 45        /// proxy types please use <see cref="EnableProxyTypes(Assembly)"/>
 46        /// instead.
 47        /// </summary>
 48        public Assembly ProxyTypesAssembly
 49        {
 50            get
 10278551            {
 52                // TODO What we should do when ProxyTypesAssemblies contains multiple assemblies? One shouldn't throw ex
 10278553                return ProxyTypesAssemblies.FirstOrDefault();
 10278554            }
 55            set
 232156            {
 232157                ProxyTypesAssemblies = new List<Assembly>();
 232158                 if (value != null)
 232159                {
 232160                    ProxyTypesAssemblies.Add(value);
 232161                }
 232162            }
 63        }
 64
 65        /// <summary>
 66        /// Sets the user to assign the CreatedBy and ModifiedBy properties when entities are added to the context.
 67        /// All requests will be executed on behalf of this user
 68        /// </summary>
 6075269        public EntityReference CallerId { get; set; }
 70
 28371        public EntityReference BusinessUnitId { get; set; }
 72
 73        public delegate OrganizationResponse ServiceRequestExecution(OrganizationRequest req);
 74
 75        /// <summary>
 76        /// Probably should be replaced by FakeMessageExecutors, more generic, which can use custom interfaces rather th
 77        /// </summary>
 628678        private Dictionary<Type, ServiceRequestExecution> ExecutionMocks { get; set; }
 79
 1112080        private Dictionary<Type, IFakeMessageExecutor> FakeMessageExecutors { get; set; }
 81
 433682        private Dictionary<string, IFakeMessageExecutor> GenericFakeMessageExecutors { get; set; }
 83
 4603584        private Dictionary<string, XrmFakedRelationship> Relationships { get; set; }
 85
 86
 3277087        public IEntityInitializerService EntityInitializerService { get; set; }
 455988        public IAccessRightsRepository AccessRightsRepository { get; set; }
 89
 2031790        public int MaxRetrieveCount { get; set; }
 91
 3277092        public EntityInitializationLevel InitializationLevel { get; set; }
 93
 94        [Obsolete("FakeXrmEasy v1.x is deprecated and will stop receiving updates soon. Please start planning your upgra
 427095        public XrmFakedContext()
 427096        {
 427097            MaxRetrieveCount = 5000;
 98
 427099            AttributeMetadataNames = new Dictionary<string, Dictionary<string, string>>();
 4270100            Data = new Dictionary<string, Dictionary<Guid, Entity>>();
 4270101            ExecutionMocks = new Dictionary<Type, ServiceRequestExecution>();
 4270102            OptionSetValuesMetadata = new Dictionary<string, OptionSetMetadata>();
 4270103            StatusAttributeMetadata = new Dictionary<string, StatusAttributeMetadata>();
 104
 4270105            FakeMessageExecutors = Assembly.GetExecutingAssembly()
 4270106                .GetTypes()
 873811107                .Where(t => t.GetInterfaces().Contains(typeof(IFakeMessageExecutor)))
 202400108                .Select(t => Activator.CreateInstance(t) as IFakeMessageExecutor)
 400530109                .ToDictionary(t => t.GetResponsibleRequestType(), t => t);
 110
 4270111            GenericFakeMessageExecutors = new Dictionary<string, IFakeMessageExecutor>();
 112
 4270113            Relationships = new Dictionary<string, XrmFakedRelationship>();
 114
 4270115            EntityInitializerService = new DefaultEntityInitializerService();
 116
 4270117            AccessRightsRepository = new AccessRightsRepository();
 118
 4270119            SystemTimeZone = TimeZoneInfo.Local;
 4270120            DateBehaviour = DefaultDateBehaviour();
 121
 4270122            EntityMetadata = new Dictionary<string, EntityMetadata>();
 123
 4270124            UsePipelineSimulation = false;
 125
 4270126            InitializationLevel = EntityInitializationLevel.Default;
 127
 4270128            ProxyTypesAssemblies = new List<Assembly>();
 4270129        }
 130
 131        /// <summary>
 132        /// Initializes the context with the provided entities
 133        /// </summary>
 134        /// <param name="entities"></param>
 135        [Obsolete("FakeXrmEasy v1.x is deprecated and will stop receiving updates soon. Please start planning your upgra
 136        public virtual void Initialize(IEnumerable<Entity> entities)
 2678137        {
 2678138             if (Initialised)
 6139            {
 6140                throw new Exception("Initialize should be called only once per unit test execution and XrmFakedContext i
 141            }
 142
 2672143             if (entities == null)
 6144            {
 6145                throw new InvalidOperationException("The entities parameter must be not null");
 146            }
 147
 61798148            foreach (var e in entities)
 26906149            {
 26906150                AddEntityWithDefaults(e, true);
 26894151            }
 152
 2654153            Initialised = true;
 2654154        }
 155
 156        public void Initialize(Entity e)
 111157        {
 111158            this.Initialize(new List<Entity>() { e });
 111159        }
 160
 161        /// <summary>
 162        /// Enables support for the early-cound types exposed in a specified assembly.
 163        /// </summary>
 164        /// <param name="assembly">
 165        /// An assembly containing early-bound entity types.
 166        /// </param>
 167        /// <remarks>
 168        /// See issue #334 on GitHub. This has quite similar idea as is on SDK method
 169        /// https://docs.microsoft.com/en-us/dotnet/api/microsoft.xrm.sdk.client.organizationserviceproxy.enableproxytyp
 170        /// </remarks>
 171        public void EnableProxyTypes(Assembly assembly)
 24172        {
 24173             if (assembly == null)
 0174            {
 0175                throw new ArgumentNullException(nameof(assembly));
 176            }
 177
 24178             if (ProxyTypesAssemblies.Contains(assembly))
 6179            {
 6180                throw new InvalidOperationException($"Proxy types assembly { assembly.GetName().Name } is already enable
 181            }
 182
 18183            ProxyTypesAssemblies.Add(assembly);
 18184        }
 185
 186        public void AddExecutionMock<T>(ServiceRequestExecution mock) where T : OrganizationRequest
 18187        {
 18188             if (!ExecutionMocks.ContainsKey(typeof(T)))
 12189                ExecutionMocks.Add(typeof(T), mock);
 190            else
 6191                ExecutionMocks[typeof(T)] = mock;
 18192        }
 193
 194        public void RemoveExecutionMock<T>() where T : OrganizationRequest
 12195        {
 12196            ExecutionMocks.Remove(typeof(T));
 12197        }
 198
 199        public void AddFakeMessageExecutor<T>(IFakeMessageExecutor executor) where T : OrganizationRequest
 30200        {
 30201             if (!FakeMessageExecutors.ContainsKey(typeof(T)))
 0202                FakeMessageExecutors.Add(typeof(T), executor);
 203            else
 30204                FakeMessageExecutors[typeof(T)] = executor;
 30205        }
 206
 207        public void RemoveFakeMessageExecutor<T>() where T : OrganizationRequest
 6208        {
 6209            FakeMessageExecutors.Remove(typeof(T));
 6210        }
 211
 212        public void AddGenericFakeMessageExecutor(string message, IFakeMessageExecutor executor)
 12213        {
 12214             if (!GenericFakeMessageExecutors.ContainsKey(message))
 12215                GenericFakeMessageExecutors.Add(message, executor);
 216            else
 0217                GenericFakeMessageExecutors[message] = executor;
 12218        }
 219
 220        public void RemoveGenericFakeMessageExecutor(string message)
 6221        {
 6222             if (GenericFakeMessageExecutors.ContainsKey(message))
 6223                GenericFakeMessageExecutors.Remove(message);
 6224        }
 225
 226        public void AddRelationship(string schemaname, XrmFakedRelationship relationship)
 198227        {
 198228            Relationships.Add(schemaname, relationship);
 198229        }
 230
 231        public void RemoveRelationship(string schemaname)
 0232        {
 0233            Relationships.Remove(schemaname);
 0234        }
 235
 236        public XrmFakedRelationship GetRelationship(string schemaName)
 294237        {
 294238             if (Relationships.ContainsKey(schemaName))
 282239            {
 282240                return Relationships[schemaName];
 241            }
 242
 12243            return null;
 294244        }
 245
 246        public void AddAttributeMapping(string sourceEntityName, string sourceAttributeName, string targetEntityName, st
 18247        {
 18248             if (string.IsNullOrWhiteSpace(sourceEntityName))
 0249                throw new ArgumentNullException("sourceEntityName");
 18250             if (string.IsNullOrWhiteSpace(sourceAttributeName))
 0251                throw new ArgumentNullException("sourceAttributeName");
 18252             if (string.IsNullOrWhiteSpace(targetEntityName))
 0253                throw new ArgumentNullException("targetEntityName");
 18254             if (string.IsNullOrWhiteSpace(targetAttributeName))
 0255                throw new ArgumentNullException("targetAttributeName");
 256
 18257            var entityMap = new Entity
 18258            {
 18259                LogicalName = "entitymap",
 18260                Id = Guid.NewGuid(),
 18261                ["targetentityname"] = targetEntityName,
 18262                ["sourceentityname"] = sourceEntityName
 18263            };
 264
 18265            var attributeMap = new Entity
 18266            {
 18267                LogicalName = "attributemap",
 18268                Id = Guid.NewGuid(),
 18269                ["entitymapid"] = new EntityReference("entitymap", entityMap.Id),
 18270                ["targetattributename"] = targetAttributeName,
 18271                ["sourceattributename"] = sourceAttributeName
 18272            };
 273
 18274            AddEntityWithDefaults(entityMap);
 18275            AddEntityWithDefaults(attributeMap);
 18276        }
 277
 278        public virtual IOrganizationService GetOrganizationService()
 2350279        {
 2350280             if (this is XrmRealContext)
 0281            {
 0282                Service = GetOrganizationService();
 0283                return Service;
 284            }
 2350285            return GetFakedOrganizationService(this);
 2350286        }
 287
 288        /// <summary>
 289        /// Deprecated. Use GetOrganizationService instead
 290        /// </summary>
 291        /// <returns></returns>
 292        [Obsolete("Use GetOrganizationService instead")]
 293        public IOrganizationService GetFakedOrganizationService()
 1685294        {
 1685295            return GetFakedOrganizationService(this);
 1685296        }
 297
 298        protected IOrganizationService GetFakedOrganizationService(XrmFakedContext context)
 4035299        {
 4035300             if (context.Service != null)
 832301            {
 832302                return context.Service;
 303            }
 304
 3203305            var fakedService = A.Fake<IOrganizationService>();
 306
 307            //Fake CRUD methods
 3203308            FakeRetrieve(context, fakedService);
 3203309            FakeCreate(context, fakedService);
 3203310            FakeUpdate(context, fakedService);
 3203311            FakeDelete(context, fakedService);
 312
 313            //Fake / Intercept Retrieve Multiple Requests
 3203314            FakeRetrieveMultiple(context, fakedService);
 315
 316            //Fake / Intercept other requests
 3203317            FakeExecute(context, fakedService);
 3203318            FakeAssociate(context, fakedService);
 3203319            FakeDisassociate(context, fakedService);
 3203320            context.Service = fakedService;
 321
 3203322            return context.Service;
 4035323        }
 324
 325        /// <summary>
 326        /// Fakes the Execute method of the organization service.
 327        /// Not all the OrganizationRequest are going to be implemented, so stay tunned on updates!
 328        /// </summary>
 329        /// <param name="context"></param>
 330        /// <param name="fakedService"></param>
 331        public static void FakeExecute(XrmFakedContext context, IOrganizationService fakedService)
 3203332        {
 3203333            OrganizationResponse response = null;
 3203334            Func<OrganizationRequest, OrganizationResponse> execute = (req) =>
 5159335            {
 5159336                 if (context.ExecutionMocks.ContainsKey(req.GetType()))
 3215337                    return context.ExecutionMocks[req.GetType()].Invoke(req);
 3203338
 5147339                 if (context.FakeMessageExecutors.ContainsKey(req.GetType())
 5147340                    && context.FakeMessageExecutors[req.GetType()].CanExecute(req))
 5129341                    return context.FakeMessageExecutors[req.GetType()].Execute(req, context);
 3203342
 3221343                 if (req.GetType() == typeof(OrganizationRequest)
 3221344                    && context.GenericFakeMessageExecutors.ContainsKey(req.RequestName))
 3215345                    return context.GenericFakeMessageExecutors[req.RequestName].Execute(req, context);
 3203346
 3209347                throw PullRequestException.NotImplementedOrganizationRequest(req.GetType());
 4891348            };
 349
 3203350            A.CallTo(() => fakedService.Execute(A<OrganizationRequest>._))
 5159351                .Invokes((OrganizationRequest req) => response = execute(req))
 4891352                .ReturnsLazily((OrganizationRequest req) => response);
 3203353        }
 354
 355        public static void FakeAssociate(XrmFakedContext context, IOrganizationService fakedService)
 3203356        {
 3203357            A.CallTo(() => fakedService.Associate(A<string>._, A<Guid>._, A<Relationship>._, A<EntityReferenceCollection
 3203358                .Invokes((string entityName, Guid entityId, Relationship relationship, EntityReferenceCollection entityC
 3311359                {
 3311360                     if (context.FakeMessageExecutors.ContainsKey(typeof(AssociateRequest)))
 3311361                    {
 3311362                        var request = new AssociateRequest()
 3311363                        {
 3311364                            Target = new EntityReference() { Id = entityId, LogicalName = entityName },
 3311365                            Relationship = relationship,
 3311366                            RelatedEntities = entityCollection
 3311367                        };
 3311368                        context.FakeMessageExecutors[typeof(AssociateRequest)].Execute(request, context);
 3311369                    }
 3203370                    else
 3203371                        throw PullRequestException.NotImplementedOrganizationRequest(typeof(AssociateRequest));
 3311372                });
 3203373        }
 374
 375        public static void FakeDisassociate(XrmFakedContext context, IOrganizationService fakedService)
 3203376        {
 3203377            A.CallTo(() => fakedService.Disassociate(A<string>._, A<Guid>._, A<Relationship>._, A<EntityReferenceCollect
 3203378                .Invokes((string entityName, Guid entityId, Relationship relationship, EntityReferenceCollection entityC
 3221379                {
 3221380                     if (context.FakeMessageExecutors.ContainsKey(typeof(DisassociateRequest)))
 3221381                    {
 3221382                        var request = new DisassociateRequest()
 3221383                        {
 3221384                            Target = new EntityReference() { Id = entityId, LogicalName = entityName },
 3221385                            Relationship = relationship,
 3221386                            RelatedEntities = entityCollection
 3221387                        };
 3221388                        context.FakeMessageExecutors[typeof(DisassociateRequest)].Execute(request, context);
 3221389                    }
 3203390                    else
 3203391                        throw PullRequestException.NotImplementedOrganizationRequest(typeof(DisassociateRequest));
 3221392                });
 3203393        }
 394
 395        public static void FakeRetrieveMultiple(XrmFakedContext context, IOrganizationService fakedService)
 3203396        {
 3203397            EntityCollection entities = null;
 3203398            Func<QueryBase, EntityCollection> retriveMultiple = (QueryBase req) =>
 4769399            {
 4769400                var request = new RetrieveMultipleRequest { Query = req };
 3203401
 4769402                var executor = new RetrieveMultipleRequestExecutor();
 4769403                var response = executor.Execute(request, context) as RetrieveMultipleResponse;
 3203404
 4737405                return response.EntityCollection;
 4737406            };
 407
 408            //refactored from RetrieveMultipleExecutor
 3203409            A.CallTo(() => fakedService.RetrieveMultiple(A<QueryBase>._))
 4769410                .Invokes((QueryBase req) => entities = retriveMultiple(req))
 4737411                .ReturnsLazily((QueryBase req) => entities);
 3203412        }
 413
 414        public IServiceEndpointNotificationService GetFakedServiceEndpointNotificationService()
 48415        {
 48416            return _serviceEndpointNotificationService ??
 48417                   (_serviceEndpointNotificationService = A.Fake<IServiceEndpointNotificationService>());
 48418        }
 419#if FAKE_XRM_EASY_9
 420        public IEntityDataSourceRetrieverService GetFakedEntityDataSourceRetrieverService()
 1421        {
 1422            var service = A.Fake<IEntityDataSourceRetrieverService>();
 1423            A.CallTo(() => service.RetrieveEntityDataSource())
 2424                .ReturnsLazily(() => EntityDataSourceRetriever);
 1425            return service;
 1426        }
 427#endif
 428    }
 429}

C:\code\jordimontana82\fake-xrm-easy\FakeXrmEasy.Shared\XrmFakedContext.DateTime.cs

#LineLine coverage
 1using FakeXrmEasy.Metadata;
 2using System;
 3using System.Collections.Generic;
 4
 5namespace FakeXrmEasy
 6{
 7    public partial class XrmFakedContext : IXrmContext
 8    {
 42709        public TimeZoneInfo SystemTimeZone { get; set; }
 10
 1211        public FiscalYearSettings FiscalYearSettings { get; set; }
 12
 2770413        public Dictionary<string, Dictionary<string, DateTimeAttributeBehavior>> DateBehaviour { get; set; }
 14
 15        private static Dictionary<string, Dictionary<string, DateTimeAttributeBehavior>> DefaultDateBehaviour()
 427016        {
 17#if FAKE_XRM_EASY || FAKE_XRM_EASY_2013
 136418            return new Dictionary<string, Dictionary<string, DateTimeAttributeBehavior>>();
 19#else
 290620            return new Dictionary<string, Dictionary<string, DateTimeAttributeBehavior>>
 290621            {
 290622                {
 290623                    "contact", new Dictionary<string, DateTimeAttributeBehavior>
 290624                    {
 290625                        { "anniversary", DateTimeAttributeBehavior.DateOnly },
 290626                        { "birthdate", DateTimeAttributeBehavior.DateOnly }
 290627                    }
 290628                },
 290629                {
 290630                    "invoice", new Dictionary<string, DateTimeAttributeBehavior>
 290631                    {
 290632                        { "duedate", DateTimeAttributeBehavior.DateOnly }
 290633                    }
 290634                },
 290635                {
 290636                    "lead", new Dictionary<string, DateTimeAttributeBehavior>
 290637                    {
 290638                        { "estimatedclosedate", DateTimeAttributeBehavior.DateOnly }
 290639                    }
 290640                },
 290641                {
 290642                    "opportunity", new Dictionary<string, DateTimeAttributeBehavior>
 290643                    {
 290644                        { "actualclosedate", DateTimeAttributeBehavior.DateOnly },
 290645                        { "estimatedclosedate", DateTimeAttributeBehavior.DateOnly },
 290646                        { "finaldecisiondate", DateTimeAttributeBehavior.DateOnly }
 290647                    }
 290648                },
 290649                {
 290650                    "product", new Dictionary<string, DateTimeAttributeBehavior>
 290651                    {
 290652                        { "validfromdate", DateTimeAttributeBehavior.DateOnly },
 290653                        { "validtodate", DateTimeAttributeBehavior.DateOnly }
 290654                    }
 290655                },
 290656                {
 290657                    "quote", new Dictionary<string, DateTimeAttributeBehavior>
 290658                    {
 290659                        { "closedon", DateTimeAttributeBehavior.DateOnly },
 290660                        { "dueby", DateTimeAttributeBehavior.DateOnly }
 290661                    }
 290662                }
 290663            };
 64#endif
 427065        }
 66    }
 67}

C:\code\jordimontana82\fake-xrm-easy\FakeXrmEasy.Shared\XrmFakedContext.Metadata.cs

#LineLine coverage
 1using Microsoft.Xrm.Sdk.Metadata;
 2using System;
 3using System.Collections.Generic;
 4using System.Linq;
 5using FakeXrmEasy.Extensions;
 6using System.Reflection;
 7using Microsoft.Xrm.Sdk.Client;
 8using Microsoft.Xrm.Sdk;
 9using FakeXrmEasy.Metadata;
 10
 11namespace FakeXrmEasy
 12{
 13    public partial class XrmFakedContext : IXrmContext
 14    {
 15        /// <summary>
 16        /// Stores some minimal metadata info if dynamic entities are used and no injected metadata was used
 17        /// </summary>
 259856418        protected internal Dictionary<string, Dictionary<string, string>> AttributeMetadataNames { get; set; }
 19
 20        /// <summary>
 21        /// Stores fake global option set metadata
 22        /// </summary>
 444423        public Dictionary<string, OptionSetMetadata> OptionSetValuesMetadata { get; set; }
 24
 25        /// <summary>
 26        /// Stores fake global status values metadata
 27        /// </summary>
 430028        public Dictionary<string, StatusAttributeMetadata> StatusAttributeMetadata { get; set; }
 29
 30        /// <summary>
 31        /// Stores fake entity metadata
 32        /// </summary>
 18008333        protected internal Dictionary<string, EntityMetadata> EntityMetadata { get; set; }
 34
 35
 36        public void InitializeMetadata(IEnumerable<EntityMetadata> entityMetadataList)
 16437        {
 16438             if (entityMetadataList == null)
 639            {
 640                throw new Exception("Entity metadata parameter can not be null");
 41            }
 42
 43            //  this.EntityMetadata = new Dictionary<string, EntityMetadata>();
 661844            foreach (var eMetadata in entityMetadataList)
 308145            {
 308146                 if (string.IsNullOrWhiteSpace(eMetadata.LogicalName))
 1247                {
 1248                    throw new Exception("An entity metadata record must have a LogicalName property.");
 49                }
 50
 306951                 if (EntityMetadata.ContainsKey(eMetadata.LogicalName))
 652                {
 653                    throw new Exception("An entity metadata record with the same logical name was previously added. ");
 54                }
 306355                EntityMetadata.Add(eMetadata.LogicalName, eMetadata.Copy());
 306356            }
 14057        }
 58
 59        public void InitializeMetadata(EntityMetadata entityMetadata)
 9860        {
 9861            this.InitializeMetadata(new List<EntityMetadata>() { entityMetadata });
 9862        }
 63
 64        public void InitializeMetadata(Assembly earlyBoundEntitiesAssembly)
 1265        {
 1266            IEnumerable<EntityMetadata> entityMetadatas = MetadataGenerator.FromEarlyBoundEntities(earlyBoundEntitiesAss
 1267             if (entityMetadatas.Any())
 1268            {
 1269                this.InitializeMetadata(entityMetadatas);
 1270            }
 1271        }
 72
 73        public IQueryable<EntityMetadata> CreateMetadataQuery()
 3074        {
 3075            return this.EntityMetadata.Values
 151976                    .Select(em => em.Copy())
 3077                    .ToList()
 3078                    .AsQueryable();
 3079        }
 80
 81        public EntityMetadata GetEntityMetadataByName(string sLogicalName)
 9082        {
 9083             if (EntityMetadata.ContainsKey(sLogicalName))
 7884                return EntityMetadata[sLogicalName].Copy();
 85
 1286            return null;
 9087        }
 88
 89        public void SetEntityMetadata(EntityMetadata em)
 3090        {
 3091             if (this.EntityMetadata.ContainsKey(em.LogicalName))
 3092                this.EntityMetadata[em.LogicalName] = em.Copy();
 93            else
 094                this.EntityMetadata.Add(em.LogicalName, em.Copy());
 3095        }
 96
 97        public AttributeMetadata GetAttributeMetadataFor(string sEntityName, string sAttributeName, Type attributeType)
 098        {
 099             if (EntityMetadata.ContainsKey(sEntityName))
 0100            {
 0101                var entityMetadata = GetEntityMetadataByName(sEntityName);
 0102                var attribute = entityMetadata.Attributes
 0103                                .Where(a => a.LogicalName.Equals(sAttributeName))
 0104                                .FirstOrDefault();
 105
 0106                 if (attribute != null)
 0107                    return attribute;
 0108            }
 109
 0110             if (attributeType == typeof(string))
 0111            {
 0112                return new StringAttributeMetadata(sAttributeName);
 113            }
 114            //Default
 0115            return new StringAttributeMetadata(sAttributeName);
 0116        }
 117
 118    }
 119}

C:\code\jordimontana82\fake-xrm-easy\FakeXrmEasy.Shared\XrmFakedContext.Pipeline.cs

#LineLine coverage
 1using System;
 2using System.Collections.Generic;
 3using System.Linq;
 4using System.Reflection;
 5using Microsoft.Xrm.Sdk;
 6using Microsoft.Xrm.Sdk.Query;
 7
 8namespace FakeXrmEasy
 9{
 10    public partial class XrmFakedContext : IXrmContext
 11    {
 641612        public bool UsePipelineSimulation { get; set; }
 13
 14        /// <summary>
 15        /// Registers the <typeparamref name="TPlugin"/> as a SDK Message Processing Step for the Entity <typeparamref n
 16        /// </summary>
 17        /// <typeparam name="TPlugin">The plugin to register the step for.</typeparam>
 18        /// <typeparam name="TEntity">The entity to filter this step for.</typeparam>
 19        /// <param name="message">The message that should trigger the execution of plugin.</param>
 20        /// <param name="stage">The stage when the plugin should be executed.</param>
 21        /// <param name="mode">The mode in which the plugin should be executed.</param>
 22        /// <param name="rank">The order in which this plugin should be executed in comparison to other plugins register
 23        /// <param name="filteringAttributes">When not one of these attributes is present in the execution context, the 
 24        public void RegisterPluginStep<TPlugin, TEntity>(string message, ProcessingStepStage stage = ProcessingStepStage
 25            where TPlugin : IPlugin
 26            where TEntity : Entity, new()
 6627        {
 6628            var entity = new TEntity();
 6629            var entityTypeCode = (int)entity.GetType().GetField("EntityTypeCode").GetValue(entity);
 30
 6631            RegisterPluginStep<TPlugin>(message, stage, mode, rank, filteringAttributes, entityTypeCode);
 6632        }
 33
 34        /// <summary>
 35        /// Registers the <typeparamref name="TPlugin"/> as a SDK Message Processing Step.
 36        /// </summary>
 37        /// <typeparam name="TPlugin">The plugin to register the step for.</typeparam>
 38        /// <param name="message">The message that should trigger the execution of plugin.</param>
 39        /// <param name="stage">The stage when the plugin should be executed.</param>
 40        /// <param name="mode">The mode in which the plugin should be executed.</param>
 41        /// <param name="rank">The order in which this plugin should be executed in comparison to other plugins register
 42        /// <param name="filteringAttributes">When not one of these attributes is present in the execution context, the 
 43        /// <param name="primaryEntityTypeCode">The entity type code to filter this step for.</param>
 44        public void RegisterPluginStep<TPlugin>(string message, ProcessingStepStage stage = ProcessingStepStage.Postoper
 45            where TPlugin : IPlugin
 7846        {
 47            // Message
 7848            var sdkMessage = this.CreateQuery("sdkmessage").FirstOrDefault(sm => string.Equals(sm.GetAttributeValue<stri
 7849             if (sdkMessage == null)
 7850            {
 7851                sdkMessage = new Entity("sdkmessage")
 7852                {
 7853                    Id = Guid.NewGuid(),
 7854                    ["name"] = message
 7855                };
 7856                this.AddEntityWithDefaults(sdkMessage);
 7857            }
 58
 59            // Plugin Type
 7860            var type = typeof(TPlugin);
 7861            var assemblyName = type.Assembly.GetName();
 62
 7863            var pluginType = this.CreateQuery("plugintype").FirstOrDefault(pt => string.Equals(pt.GetAttributeValue<stri
 7864             if (pluginType == null)
 7865            {
 7866                pluginType = new Entity("plugintype")
 7867                {
 7868                    Id = Guid.NewGuid(),
 7869                    ["name"] = type.FullName,
 7870                    ["typename"] = type.FullName,
 7871                    ["assemblyname"] = assemblyName.Name,
 7872                    ["major"] = assemblyName.Version.Major,
 7873                    ["minor"] = assemblyName.Version.Minor,
 7874                    ["version"] = assemblyName.Version.ToString(),
 7875                };
 7876                this.AddEntityWithDefaults(pluginType);
 7877            }
 78
 79            // Filter
 7880            Entity sdkFilter = null;
 7881             if (primaryEntityTypeCode.HasValue)
 6682            {
 6683                sdkFilter = new Entity("sdkmessagefilter")
 6684                {
 6685                    Id = Guid.NewGuid(),
 6686                    ["primaryobjecttypecode"] = primaryEntityTypeCode
 6687                };
 6688                this.AddEntityWithDefaults(sdkFilter);
 6689            }
 90
 91            // Message Step
 7892             var sdkMessageProcessingStep = new Entity("sdkmessageprocessingstep")
 7893            {
 7894                Id = Guid.NewGuid(),
 7895                ["eventhandler"] = pluginType.ToEntityReference(),
 7896                ["sdkmessageid"] = sdkMessage.ToEntityReference(),
 7897                ["sdkmessagefilterid"] = sdkFilter?.ToEntityReference(),
 7898                ["filteringattributes"] = filteringAttributes != null ? string.Join(",", filteringAttributes) : null,
 7899                ["mode"] = new OptionSetValue((int)mode),
 78100                ["stage"] = new OptionSetValue((int)stage),
 78101                ["rank"] = rank
 78102            };
 78103            this.AddEntityWithDefaults(sdkMessageProcessingStep);
 78104        }
 105
 106        private void ExecutePipelineStage(string method, ProcessingStepStage stage, ProcessingStepMode mode, Entity enti
 198107        {
 198108            var plugins = GetStepsForStage(method, stage, mode, entity);
 109
 198110            ExecutePipelinePlugins(plugins, entity);
 198111        }
 112
 113        private void ExecutePipelineStage(string method, ProcessingStepStage stage, ProcessingStepMode mode, EntityRefer
 54114        {
 54115            var entityType = FindReflectedType(entityReference.LogicalName);
 54116             if (entityType == null)
 0117            {
 0118                return;
 119            }
 120
 54121            var plugins = GetStepsForStage(method, stage, mode, (Entity)Activator.CreateInstance(entityType));
 122
 54123            ExecutePipelinePlugins(plugins, entityReference);
 54124        }
 125
 126        private void ExecutePipelinePlugins(IEnumerable<Entity> plugins, object target)
 252127        {
 900128            foreach (var plugin in plugins)
 72129            {
 72130                var pluginMethod = GetPluginMethod(plugin);
 131
 72132                var pluginContext = this.GetDefaultPluginContext();
 72133                pluginContext.Mode = plugin.GetAttributeValue<OptionSetValue>("mode").Value;
 72134                pluginContext.Stage = plugin.GetAttributeValue<OptionSetValue>("stage").Value;
 72135                pluginContext.MessageName = (string)plugin.GetAttributeValue<AliasedValue>("sdkmessage.name").Value;
 72136                pluginContext.InputParameters = new ParameterCollection
 72137                {
 72138                    { "Target", target }
 72139                };
 72140                pluginContext.OutputParameters = new ParameterCollection();
 72141                pluginContext.PreEntityImages = new EntityImageCollection();
 72142                pluginContext.PostEntityImages = new EntityImageCollection();
 143
 72144                pluginMethod.Invoke(this, new object[] { pluginContext });
 72145            }
 252146        }
 147
 148        private static MethodInfo GetPluginMethod(Entity pluginEntity)
 72149        {
 72150            var assemblyName = (string)pluginEntity.GetAttributeValue<AliasedValue>("plugintype.assemblyname").Value;
 72151            var assembly = AppDomain.CurrentDomain.Load(assemblyName);
 152
 72153            var pluginTypeName = (string)pluginEntity.GetAttributeValue<AliasedValue>("plugintype.typename").Value;
 72154            var pluginType = assembly.GetType(pluginTypeName);
 155
 72156            var methodInfo = typeof(XrmFakedContext).GetMethod("ExecutePluginWith", new[] { typeof(XrmFakedPluginExecuti
 72157            var pluginMethod = methodInfo.MakeGenericMethod(pluginType);
 158
 72159            return pluginMethod;
 72160        }
 161
 162        private IEnumerable<Entity> GetStepsForStage(string method, ProcessingStepStage stage, ProcessingStepMode mode, 
 252163        {
 252164            var query = new QueryExpression("sdkmessageprocessingstep")
 252165            {
 252166                ColumnSet = new ColumnSet("configuration", "filteringattributes", "stage", "mode"),
 252167                Criteria =
 252168                {
 252169                    Conditions =
 252170                    {
 252171                        new ConditionExpression("stage", ConditionOperator.Equal, (int)stage),
 252172                        new ConditionExpression("mode", ConditionOperator.Equal, (int)mode)
 252173                    }
 252174                },
 252175                Orders =
 252176                {
 252177                    new OrderExpression("rank", OrderType.Ascending)
 252178                },
 252179                LinkEntities =
 252180                {
 252181                    new LinkEntity("sdkmessageprocessingstep", "sdkmessagefilter", "sdkmessagefilterid", "sdkmessagefilt
 252182                    {
 252183                        EntityAlias = "sdkmessagefilter",
 252184                        Columns = new ColumnSet("primaryobjecttypecode")
 252185                    },
 252186                    new LinkEntity("sdkmessageprocessingstep", "sdkmessage", "sdkmessageid", "sdkmessageid", JoinOperato
 252187                    {
 252188                        EntityAlias = "sdkmessage",
 252189                        Columns = new ColumnSet("name"),
 252190                        LinkCriteria =
 252191                        {
 252192                            Conditions =
 252193                            {
 252194                                new ConditionExpression("name", ConditionOperator.Equal, method)
 252195                            }
 252196                        }
 252197                    },
 252198                    new LinkEntity("sdkmessageprocessingstep", "plugintype", "eventhandler", "plugintypeid", JoinOperato
 252199                    {
 252200                        EntityAlias = "plugintype",
 252201                        Columns = new ColumnSet("assemblyname", "typename")
 252202                    }
 252203                }
 252204            };
 205
 252206             var entityTypeCode = (int?)entity.GetType().GetField("EntityTypeCode")?.GetValue(entity);
 207
 252208            var plugins = this.Service.RetrieveMultiple(query).Entities.AsEnumerable();
 252209            plugins = plugins.Where(p =>
 330210            {
 330211                var primaryObjectTypeCode = p.GetAttributeValue<AliasedValue>("sdkmessagefilter.primaryobjecttypecode");
 252212
 330213                return primaryObjectTypeCode == null || entityTypeCode.HasValue && (int)primaryObjectTypeCode.Value == e
 330214            });
 215
 216            // Todo: Filter on attributes
 217
 252218            return plugins;
 252219        }
 220    }
 221}

C:\code\jordimontana82\fake-xrm-easy\FakeXrmEasy.Shared\XrmFakedContext.Plugins.cs

#LineLine coverage
 1using FakeItEasy;
 2using Microsoft.Xrm.Sdk;
 3using System;
 4using System.Linq;
 5
 6namespace FakeXrmEasy
 7{
 8    public partial class XrmFakedContext : IXrmContext
 9    {
 10        /// <summary>
 11        /// Returns a plugin context with default properties one can override
 12        /// </summary>
 13        /// <returns></returns>
 14        public XrmFakedPluginExecutionContext GetDefaultPluginContext()
 22915        {
 22916             var userId = CallerId?.Id ?? Guid.NewGuid();
 22917             Guid businessUnitId = BusinessUnitId?.Id ?? Guid.NewGuid();
 18
 22919            return new XrmFakedPluginExecutionContext
 22920            {
 22921                Depth = 1,
 22922                IsExecutingOffline = false,
 22923                MessageName = "Create",
 22924                UserId = userId,
 22925                BusinessUnitId = businessUnitId,
 22926                InitiatingUserId = userId,
 22927                InputParameters = new ParameterCollection(),
 22928                OutputParameters = new ParameterCollection(),
 22929                SharedVariables = new ParameterCollection(),
 22930                PreEntityImages = new EntityImageCollection(),
 22931                PostEntityImages = new EntityImageCollection(),
 22932                IsolationMode = 1
 22933            };
 22934        }
 35
 36        protected IPluginExecutionContext GetFakedPluginContext(XrmFakedPluginExecutionContext ctx)
 23537        {
 23538            var context = A.Fake<IPluginExecutionContext>();
 39
 23540            PopulateExecutionContextPropertiesFromFakedContext(context, ctx);
 41
 23542            A.CallTo(() => context.ParentContext).ReturnsLazily(() => ctx.ParentContext);
 31343            A.CallTo(() => context.Stage).ReturnsLazily(() => ctx.Stage);
 44
 23545            return context;
 23546        }
 47
 48        protected void PopulateExecutionContextPropertiesFromFakedContext(IExecutionContext context, XrmFakedPluginExecu
 23549        {
 23550            var newUserId = Guid.NewGuid();
 51
 24152            A.CallTo(() => context.Depth).ReturnsLazily(() => ctx.Depth <= 0 ? 1 : ctx.Depth);
 23553            A.CallTo(() => context.IsExecutingOffline).ReturnsLazily(() => ctx.IsExecutingOffline);
 47654            A.CallTo(() => context.InputParameters).ReturnsLazily(() => ctx.InputParameters);
 26055            A.CallTo(() => context.OutputParameters).ReturnsLazily(() => ctx.OutputParameters);
 24756            A.CallTo(() => context.PreEntityImages).ReturnsLazily(() => ctx.PreEntityImages);
 24757            A.CallTo(() => context.PostEntityImages).ReturnsLazily(() => ctx.PostEntityImages);
 34358            A.CallTo(() => context.MessageName).ReturnsLazily(() => ctx.MessageName);
 28959            A.CallTo(() => context.Mode).ReturnsLazily(() => ctx.Mode);
 24160            A.CallTo(() => context.OrganizationName).ReturnsLazily(() => ctx.OrganizationName);
 23561            A.CallTo(() => context.OrganizationId).ReturnsLazily(() => ctx.OrganizationId);
 23562            A.CallTo(() => context.OwningExtension).ReturnsLazily(() => ctx.OwningExtension);
 25963            A.CallTo(() => context.InitiatingUserId).ReturnsLazily(() => ctx.InitiatingUserId == Guid.Empty ? newUserId 
 32064            A.CallTo(() => context.UserId).ReturnsLazily(() => ctx.UserId == Guid.Empty ? newUserId : ctx.UserId);
 23565            A.CallTo(() => context.PrimaryEntityId).ReturnsLazily(() => ctx.PrimaryEntityId);
 24166            A.CallTo(() => context.PrimaryEntityName).ReturnsLazily(() => ctx.PrimaryEntityName);
 23567            A.CallTo(() => context.SecondaryEntityName).ReturnsLazily(() => ctx.SecondaryEntityName);
 24168            A.CallTo(() => context.SharedVariables).ReturnsLazily(() => ctx.SharedVariables);
 23569            A.CallTo(() => context.BusinessUnitId).ReturnsLazily(() => ctx.BusinessUnitId);
 23570            A.CallTo(() => context.CorrelationId).ReturnsLazily(() => ctx.CorrelationId);
 23571            A.CallTo(() => context.OperationCreatedOn).ReturnsLazily(() => ctx.OperationCreatedOn);
 23572            A.CallTo(() => context.IsolationMode).ReturnsLazily(() => ctx.IsolationMode);
 23573            A.CallTo(() => context.IsInTransaction).ReturnsLazily(() => ctx.IsInTransaction);
 74
 75
 76            // Create message will pass an Entity as the target but this is not always true
 77            // For instance, a Delete request will receive an EntityReference
 23578             if (ctx.InputParameters != null && ctx.InputParameters.ContainsKey("Target"))
 18679            {
 18680                 if (ctx.InputParameters["Target"] is Entity)
 16881                {
 16882                    var target = (Entity)ctx.InputParameters["Target"];
 16883                    A.CallTo(() => context.PrimaryEntityId).ReturnsLazily(() => target.Id);
 19884                    A.CallTo(() => context.PrimaryEntityName).ReturnsLazily(() => target.LogicalName);
 16885                }
 1886                 else if (ctx.InputParameters["Target"] is EntityReference)
 1887                {
 1888                    var target = (EntityReference)ctx.InputParameters["Target"];
 1889                    A.CallTo(() => context.PrimaryEntityId).ReturnsLazily(() => target.Id);
 1890                    A.CallTo(() => context.PrimaryEntityName).ReturnsLazily(() => target.LogicalName);
 1891                }
 18692            }
 23593        }
 94
 95        protected IExecutionContext GetFakedExecutionContext(XrmFakedPluginExecutionContext ctx)
 096        {
 097            var context = A.Fake<IExecutionContext>();
 98
 099            PopulateExecutionContextPropertiesFromFakedContext(context, ctx);
 100
 0101            return context;
 0102        }
 103
 104        /// <summary>
 105        /// Executes a plugin passing a custom context. This is useful whenever we need to mock more complex plugin cont
 106        /// </summary>
 107        /// <typeparam name="T">Must be a plugin</typeparam>
 108        /// <param name="ctx"></param>
 109        /// <returns></returns>
 110        public IPlugin ExecutePluginWith<T>(XrmFakedPluginExecutionContext ctx = null)
 111            where T : IPlugin, new()
 150112        {
 150113             if (ctx == null)
 24114            {
 24115                ctx = GetDefaultPluginContext();
 24116            }
 117
 150118            return this.ExecutePluginWith(ctx, new T());
 150119        }
 120
 121        /// <summary>
 122        /// Executes a plugin passing a custom context. This is useful whenever we need to mock more complex plugin cont
 123        /// </summary>
 124        /// <param name="ctx"></param>
 125        /// <param name="instance"></param>
 126        /// <returns></returns>
 127        public IPlugin ExecutePluginWith(XrmFakedPluginExecutionContext ctx, IPlugin instance)
 211128        {
 211129            var fakedServiceProvider = GetFakedServiceProvider(ctx);
 130
 211131            var fakedPlugin = A.Fake<IPlugin>();
 211132            A.CallTo(() => fakedPlugin.Execute(A<IServiceProvider>._))
 211133                .Invokes((IServiceProvider provider) =>
 422134                {
 422135                    var plugin = instance;
 422136                    plugin.Execute(fakedServiceProvider);
 416137                });
 138
 211139            fakedPlugin.Execute(fakedServiceProvider); //Execute the plugin
 205140            return fakedPlugin;
 205141        }
 142
 143        public IPlugin ExecutePluginWith<T>(ParameterCollection inputParameters, ParameterCollection outputParameters, E
 144            where T : IPlugin, new()
 24145        {
 24146            var ctx = GetDefaultPluginContext();
 24147            ctx.InputParameters.AddRange(inputParameters);
 24148            ctx.OutputParameters.AddRange(outputParameters);
 24149            ctx.PreEntityImages.AddRange(preEntityImages);
 24150            ctx.PostEntityImages.AddRange(postEntityImages);
 151
 24152            var fakedServiceProvider = GetFakedServiceProvider(ctx);
 153
 24154            var fakedPlugin = A.Fake<IPlugin>();
 24155            A.CallTo(() => fakedPlugin.Execute(A<IServiceProvider>._))
 24156                .Invokes((IServiceProvider provider) =>
 48157                {
 48158                    var plugin = new T();
 48159                    plugin.Execute(fakedServiceProvider);
 48160                });
 161
 24162            fakedPlugin.Execute(fakedServiceProvider); //Execute the plugin
 24163            return fakedPlugin;
 24164        }
 165
 166        public IPlugin ExecutePluginWithConfigurations<T>(XrmFakedPluginExecutionContext plugCtx, string unsecureConfigu
 167            where T : class, IPlugin
 31168        {
 31169            var pluginType = typeof(T);
 31170            var constructors = pluginType.GetConstructors().ToList();
 171
 106172             if (!constructors.Any(c => c.GetParameters().Length == 2 && c.GetParameters().All(param => param.ParameterTy
 12173            {
 12174                throw new ArgumentException("The plugin you are trying to execute does not specify a constructor for pas
 175            }
 176
 19177            var pluginInstance = (T)Activator.CreateInstance(typeof(T), unsecureConfiguration, secureConfiguration);
 178
 19179            return this.ExecutePluginWith(plugCtx, pluginInstance);
 19180        }
 181
 182        [Obsolete("Use ExecutePluginWith(XrmFakedPluginExecutionContext ctx, IPlugin instance).")]
 183        public IPlugin ExecutePluginWithConfigurations<T>(XrmFakedPluginExecutionContext plugCtx, T instance, string uns
 184            where T : class, IPlugin
 0185        {
 0186            var fakedServiceProvider = GetFakedServiceProvider(plugCtx);
 187
 0188            var fakedPlugin = A.Fake<IPlugin>();
 189
 0190            A.CallTo(() => fakedPlugin.Execute(A<IServiceProvider>._))
 0191                .Invokes((IServiceProvider provider) =>
 0192                {
 0193                    var pluginType = typeof(T);
 0194                    var constructors = pluginType.GetConstructors();
 0195
 0196                     if (!constructors.Any(c => c.GetParameters().Length == 2 && c.GetParameters().All(param => param.Par
 0197                    {
 0198                        throw new ArgumentException("The plugin you are trying to execute does not specify a constructor
 0199                    }
 0200
 0201                    var plugin = instance;
 0202                    plugin.Execute(fakedServiceProvider);
 0203                });
 204
 0205            fakedPlugin.Execute(fakedServiceProvider); //Execute the plugin
 0206            return fakedPlugin;
 0207        }
 208
 209        public IPlugin ExecutePluginWithTarget<T>(XrmFakedPluginExecutionContext ctx, Entity target, string messageName 
 210          where T : IPlugin, new()
 0211        {
 0212            ctx.InputParameters.Add("Target", target);
 0213            ctx.MessageName = messageName;
 0214            ctx.Stage = stage;
 215
 0216            return this.ExecutePluginWith<T>(ctx);
 0217        }
 218
 219        /// <summary>
 220        /// Executes the plugin of type T against the faked context for an entity target
 221        /// and returns the faked plugin
 222        /// </summary>
 223        /// <typeparam name="T"></typeparam>
 224        /// <param name="target">The entity to execute the plug-in for.</param>
 225        /// <param name="messageName">Sets the message name.</param>
 226        /// <param name="stage">Sets the stage.</param>
 227        /// <returns></returns>
 228        public IPlugin ExecutePluginWithTarget<T>(Entity target, string messageName = "Create", int stage = 40)
 229            where T : IPlugin, new()
 36230        {
 36231            return this.ExecutePluginWithTarget(new T(), target, messageName, stage);
 30232        }
 233
 234        /// <summary>
 235        /// Executes the plugin of type T against the faked context for an entity target
 236        /// and returns the faked plugin
 237        /// </summary>
 238        /// <param name="instance"></param>
 239        /// <param name="target">The entity to execute the plug-in for.</param>
 240        /// <param name="messageName">Sets the message name.</param>
 241        /// <param name="stage">Sets the stage.</param>
 242        /// <returns></returns>
 243        public IPlugin ExecutePluginWithTarget(IPlugin instance, Entity target, string messageName = "Create", int stage
 36244        {
 36245            var ctx = GetDefaultPluginContext();
 246
 247            // Add the target entity to the InputParameters
 36248            ctx.InputParameters.Add("Target", target);
 36249            ctx.MessageName = messageName;
 36250            ctx.Stage = stage;
 251
 36252            return this.ExecutePluginWith(ctx, instance);
 30253        }
 254
 255        /// <summary>
 256        /// Executes the plugin of type T against the faked context for an entity reference target
 257        /// and returns the faked plugin
 258        /// </summary>
 259        /// <typeparam name="T"></typeparam>
 260        /// <param name="target">The entity reference to execute the plug-in for.</param>
 261        /// <param name="messageName">Sets the message name.</param>
 262        /// <param name="stage">Sets the stage.</param>
 263        /// <returns></returns>
 264        public IPlugin ExecutePluginWithTargetReference<T>(EntityReference target, string messageName = "Delete", int st
 265            where T : IPlugin, new()
 0266        {
 0267            return this.ExecutePluginWithTargetReference(new T(), target, messageName, stage);
 0268        }
 269
 270        /// <summary>
 271        /// Executes the plugin of type T against the faked context for an entity reference target
 272        /// and returns the faked plugin
 273        /// </summary>
 274        /// <param name="instance"></param>
 275        /// <param name="target">The entity reference to execute the plug-in for.</param>
 276        /// <param name="messageName">Sets the message name.</param>
 277        /// <param name="stage">Sets the stage.</param>
 278        /// <returns></returns>
 279        public IPlugin ExecutePluginWithTargetReference(IPlugin instance, EntityReference target, string messageName = "
 0280        {
 0281            var ctx = GetDefaultPluginContext();
 282            // Add the target entity to the InputParameters
 0283            ctx.InputParameters.Add("Target", target);
 0284            ctx.MessageName = messageName;
 0285            ctx.Stage = stage;
 286
 0287            return this.ExecutePluginWith(ctx, instance);
 0288        }
 289
 290        /// <summary>
 291        /// Returns a faked plugin with a target and the specified pre entity images
 292        /// </summary>
 293        /// <typeparam name="T"></typeparam>
 294        /// <returns></returns>
 295        [Obsolete]
 296        public IPlugin ExecutePluginWithTargetAndPreEntityImages<T>(object target, EntityImageCollection preEntityImages
 297            where T : IPlugin, new()
 6298        {
 6299            var ctx = GetDefaultPluginContext();
 300            // Add the target entity to the InputParameters
 6301            ctx.InputParameters.Add("Target", target);
 6302            ctx.PreEntityImages.AddRange(preEntityImages);
 6303            ctx.MessageName = messageName;
 6304            ctx.Stage = stage;
 305
 6306            return this.ExecutePluginWith<T>(ctx);
 6307        }
 308
 309        /// <summary>
 310        /// Returns a faked plugin with a target and the specified post entity images
 311        /// </summary>
 312        /// <typeparam name="T"></typeparam>
 313        /// <returns></returns>
 314        [Obsolete]
 315        public IPlugin ExecutePluginWithTargetAndPostEntityImages<T>(object target, EntityImageCollection postEntityImag
 316            where T : IPlugin, new()
 6317        {
 6318            var ctx = GetDefaultPluginContext();
 319            // Add the target entity to the InputParameters
 6320            ctx.InputParameters.Add("Target", target);
 6321            ctx.PostEntityImages.AddRange(postEntityImages);
 6322            ctx.MessageName = messageName;
 6323            ctx.Stage = stage;
 324
 6325            return this.ExecutePluginWith<T>(ctx);
 6326        }
 327
 328        [Obsolete]
 329        public IPlugin ExecutePluginWithTargetAndInputParameters<T>(Entity target, ParameterCollection inputParameters, 
 330            where T : IPlugin, new()
 0331        {
 0332            var ctx = GetDefaultPluginContext();
 333
 0334            ctx.InputParameters.AddRange(inputParameters);
 335
 0336            return this.ExecutePluginWithTarget<T>(ctx, target, messageName, stage);
 0337        }
 338
 339        protected IServiceProvider GetFakedServiceProvider(XrmFakedPluginExecutionContext plugCtx)
 235340        {
 235341            var fakedServiceProvider = A.Fake<IServiceProvider>();
 342
 235343            A.CallTo(() => fakedServiceProvider.GetService(A<Type>._))
 235344               .ReturnsLazily((Type t) =>
 707345               {
 707346                    if (t == typeof(IOrganizationService))
 247347                   {
 235348                       //Return faked or real organization service
 247349                       return GetOrganizationService();
 235350                   }
 235351
 695352                    if (t == typeof(ITracingService))
 386353                   {
 386354                       return TracingService;
 235355                   }
 235356
 544357                    if (t == typeof(IPluginExecutionContext))
 470358                   {
 470359                       return GetFakedPluginContext(plugCtx);
 235360                   }
 235361
 309362                    if (t == typeof(IExecutionContext))
 235363                   {
 235364                       return GetFakedExecutionContext(plugCtx);
 235365                   }
 235366
 309367                    if (t == typeof(IOrganizationServiceFactory))
 302368                   {
 302369                       var fakedServiceFactory = A.Fake<IOrganizationServiceFactory>();
 369370                       A.CallTo(() => fakedServiceFactory.CreateOrganizationService(A<Guid?>._)).ReturnsLazily((Guid? g)
 302371                       return fakedServiceFactory;
 235372                   }
 235373
 242374                    if (t == typeof(IServiceEndpointNotificationService))
 241375                   {
 241376                       return GetFakedServiceEndpointNotificationService();
 235377                   }
 235378#if FAKE_XRM_EASY_9
 236379                    if (t == typeof(IEntityDataSourceRetrieverService))
 236380                   {
 236381                       return GetFakedEntityDataSourceRetrieverService();
 235382                   }
 235383#endif
 235384                   throw new PullRequestException("The specified service type is not supported");
 707385               });
 386
 235387            return fakedServiceProvider;
 235388        }
 389
 390#if FAKE_XRM_EASY_9
 2391        public Entity EntityDataSourceRetriever { get; set; }
 392#endif
 393
 394        public XrmFakedTracingService GetFakeTracingService()
 84395        {
 84396            return TracingService;
 84397        }
 398    }
 399}

C:\code\jordimontana82\fake-xrm-easy\FakeXrmEasy.Shared\XrmFakedContext.Queries.cs

#LineLine coverage
 1using System;
 2using System.Collections;
 3using System.Collections.Generic;
 4using System.Globalization;
 5using System.Linq;
 6using System.Linq.Expressions;
 7using System.Reflection;
 8using System.ServiceModel;
 9using System.Text.RegularExpressions;
 10using System.Xml.Linq;
 11using FakeXrmEasy.Extensions;
 12using FakeXrmEasy.Extensions.FetchXml;
 13using FakeXrmEasy.Models;
 14using Microsoft.Xrm.Sdk;
 15using Microsoft.Xrm.Sdk.Client;
 16using Microsoft.Xrm.Sdk.Query;
 17
 18namespace FakeXrmEasy
 19{
 20    public partial class XrmFakedContext : IXrmContext
 21    {
 22        protected internal Type FindReflectedType(string logicalName)
 2962623        {
 2962624            var types =
 8449825                ProxyTypesAssemblies.Select(a => FindReflectedType(logicalName, a))
 8449826                                    .Where(t => t != null);
 27
 2962628             if (types.Count() > 1)
 029            {
 030                var errorMsg = $"Type { logicalName } is defined in multiple assemblies: ";
 031                foreach (var type in types)
 032                {
 033                    errorMsg += type.Assembly
 034                                    .GetName()
 035                                    .Name + "; ";
 036                }
 037                var lastIndex = errorMsg.LastIndexOf("; ");
 038                errorMsg = errorMsg.Substring(0, lastIndex) + ".";
 039                throw new InvalidOperationException(errorMsg);
 40            }
 41
 2962642            return types.SingleOrDefault();
 2962643        }
 44
 45        /// <summary>
 46        /// Finds reflected type for given entity from given assembly.
 47        /// </summary>
 48        /// <param name="logicalName">
 49        /// Entity logical name which type is searched from given
 50        /// <paramref name="assembly"/>.
 51        /// </param>
 52        /// <param name="assembly">
 53        /// Assembly where early-bound type is searched for given
 54        /// <paramref name="logicalName"/>.
 55        /// </param>
 56        /// <returns>
 57        /// Early-bound type of <paramref name="logicalName"/> if it's found
 58        /// from <paramref name="assembly"/>. Otherwise null is returned.
 59        /// </returns>
 60        private static Type FindReflectedType(string logicalName,
 61                                              Assembly assembly)
 5487262        {
 63            try
 5487264            {
 5487265                 if (assembly == null)
 066                {
 067                    throw new ArgumentNullException(nameof(assembly));
 68                }
 69
 70                /* This wasn't building within the CI FAKE build script...
 71                var subClassType = assembly.GetTypes()
 72                        .Where(t => typeof(Entity).IsAssignableFrom(t))
 73                        .Where(t => t.GetCustomAttributes<EntityLogicalNameAttribute>(true).Any())
 74                        .FirstOrDefault(t => t.GetCustomAttributes<EntityLogicalNameAttribute>(true).First().LogicalName
 75
 76                */
 5487277                var subClassType = assembly.GetTypes()
 714919878                        .Where(t => typeof(Entity).IsAssignableFrom(t))
 432962279                        .Where(t => t.GetCustomAttributes(typeof(EntityLogicalNameAttribute), true).Length > 0)
 432962280                        .Where(t => ((EntityLogicalNameAttribute)t.GetCustomAttributes(typeof(EntityLogicalNameAttribute
 5487281                        .FirstOrDefault();
 82
 5487283                return subClassType;
 84            }
 085            catch (ReflectionTypeLoadException exception)
 086            {
 87                // now look at ex.LoaderExceptions - this is an Exception[], so:
 088                var s = "";
 089                foreach (var innerException in exception.LoaderExceptions)
 090                {
 91                    // write details of "inner", in particular inner.Message
 092                    s += innerException.Message + " ";
 093                }
 94
 095                throw new Exception("XrmFakedContext.FindReflectedType: " + s);
 96            }
 5487297        }
 98
 99        protected internal Type FindAttributeTypeInInjectedMetadata(string sEntityName, string sAttributeName)
 14100        {
 14101             if (!EntityMetadata.ContainsKey(sEntityName))
 6102                return null;
 103
 8104             if (EntityMetadata[sEntityName].Attributes == null)
 0105                return null;
 106
 8107            var attribute = EntityMetadata[sEntityName].Attributes
 16108                                .Where(a => a.LogicalName == sAttributeName)
 8109                                .FirstOrDefault();
 110
 8111             if (attribute == null)
 0112                return null;
 113
 8114             if (attribute.AttributeType == null)
 0115                return null;
 116
 8117             switch (attribute.AttributeType.Value)
 118            {
 119                case Microsoft.Xrm.Sdk.Metadata.AttributeTypeCode.BigInt:
 0120                    return typeof(long);
 121
 122                case Microsoft.Xrm.Sdk.Metadata.AttributeTypeCode.Integer:
 0123                    return typeof(int);
 124
 125                case Microsoft.Xrm.Sdk.Metadata.AttributeTypeCode.Boolean:
 0126                    return typeof(bool);
 127
 128                case Microsoft.Xrm.Sdk.Metadata.AttributeTypeCode.CalendarRules:
 0129                    throw new Exception("CalendarRules: Type not yet supported");
 130
 131                case Microsoft.Xrm.Sdk.Metadata.AttributeTypeCode.Lookup:
 132                case Microsoft.Xrm.Sdk.Metadata.AttributeTypeCode.Customer:
 133                case Microsoft.Xrm.Sdk.Metadata.AttributeTypeCode.Owner:
 0134                    return typeof(EntityReference);
 135
 136                case Microsoft.Xrm.Sdk.Metadata.AttributeTypeCode.DateTime:
 0137                    return typeof(DateTime);
 138
 139                case Microsoft.Xrm.Sdk.Metadata.AttributeTypeCode.Decimal:
 0140                    return typeof(decimal);
 141
 142                case Microsoft.Xrm.Sdk.Metadata.AttributeTypeCode.Double:
 0143                    return typeof(double);
 144
 145                case Microsoft.Xrm.Sdk.Metadata.AttributeTypeCode.EntityName:
 146                case Microsoft.Xrm.Sdk.Metadata.AttributeTypeCode.Memo:
 147                case Microsoft.Xrm.Sdk.Metadata.AttributeTypeCode.String:
 6148                    return typeof(string);
 149
 150                case Microsoft.Xrm.Sdk.Metadata.AttributeTypeCode.Money:
 0151                    return typeof(Money);
 152
 153                case Microsoft.Xrm.Sdk.Metadata.AttributeTypeCode.PartyList:
 0154                    return typeof(EntityReferenceCollection);
 155
 156                case Microsoft.Xrm.Sdk.Metadata.AttributeTypeCode.Picklist:
 157                case Microsoft.Xrm.Sdk.Metadata.AttributeTypeCode.State:
 158                case Microsoft.Xrm.Sdk.Metadata.AttributeTypeCode.Status:
 1159                    return typeof(OptionSetValue);
 160
 161                case Microsoft.Xrm.Sdk.Metadata.AttributeTypeCode.Uniqueidentifier:
 0162                    return typeof(Guid);
 163
 164                case Microsoft.Xrm.Sdk.Metadata.AttributeTypeCode.Virtual:
 165#if FAKE_XRM_EASY_9
 1166                     if (attribute.AttributeTypeName.Value == "MultiSelectPicklistType")
 1167                    {
 1168                        return typeof(OptionSetValueCollection);
 169                    }
 170#endif
 0171                    throw new Exception("Virtual: Type not yet supported");
 172
 173                default:
 0174                    return typeof(string);
 175
 176            }
 177
 14178        }
 179        protected internal Type FindReflectedAttributeType(Type earlyBoundType, string sEntityName, string attributeName
 3198180        {
 181            //Get that type properties
 3198182            var attributeInfo = GetEarlyBoundTypeAttribute(earlyBoundType, attributeName);
 3198183             if (attributeInfo == null && attributeName.EndsWith("name"))
 6184            {
 185                // Special case for referencing the name of a EntityReference
 6186                attributeName = attributeName.Substring(0, attributeName.Length - 4);
 6187                attributeInfo = GetEarlyBoundTypeAttribute(earlyBoundType, attributeName);
 188
 6189                 if (attributeInfo.PropertyType != typeof(EntityReference))
 0190                {
 191                    // Don't mess up if other attributes follow this naming pattern
 0192                    attributeInfo = null;
 0193                }
 6194            }
 195
 3198196             if (attributeInfo == null || attributeInfo.PropertyType.FullName == null)
 14197            {
 198                //Try with metadata
 14199                var injectedType = FindAttributeTypeInInjectedMetadata(sEntityName, attributeName);
 200
 14201                 if (injectedType == null)
 6202                {
 6203                    throw new Exception($"XrmFakedContext.FindReflectedAttributeType: Attribute {attributeName} not foun
 204                }
 205
 8206                return injectedType;
 207            }
 208
 3184209             if (attributeInfo.PropertyType.FullName.EndsWith("Enum") || attributeInfo.PropertyType.BaseType.FullName.End
 0210            {
 0211                return typeof(int);
 212            }
 213
 3184214             if (!attributeInfo.PropertyType.FullName.StartsWith("System."))
 879215            {
 216                try
 879217                {
 879218                    var instance = Activator.CreateInstance(attributeInfo.PropertyType);
 879219                     if (instance is Entity)
 0220                    {
 0221                        return typeof(EntityReference);
 222                    }
 879223                }
 0224                catch
 0225                {
 226                    // ignored
 0227                }
 879228            }
 229#if FAKE_XRM_EASY_2015 || FAKE_XRM_EASY_2016 || FAKE_XRM_EASY_365 || FAKE_XRM_EASY_9
 1568230             else if (attributeInfo.PropertyType.FullName.StartsWith("System.Nullable"))
 936231            {
 936232                return attributeInfo.PropertyType.GenericTypeArguments[0];
 233            }
 234#endif
 235
 2248236            return attributeInfo.PropertyType;
 3192237        }
 238
 239        private static PropertyInfo GetEarlyBoundTypeAttribute(Type earlyBoundType, string attributeName)
 3589240        {
 3589241            var attributeInfo = earlyBoundType.GetProperties()
 231512242                .Where(pi => pi.GetCustomAttributes(typeof(AttributeLogicalNameAttribute), true).Length > 0)
 224655243                .Where(pi => (pi.GetCustomAttributes(typeof(AttributeLogicalNameAttribute), true)[0] as AttributeLogical
 3589244                .FirstOrDefault();
 245
 3589246            return attributeInfo;
 3589247        }
 248
 249        public IQueryable<Entity> CreateQuery(string entityLogicalName)
 1540250        {
 1540251            return this.CreateQuery<Entity>(entityLogicalName);
 1534252        }
 253
 254        public IQueryable<T> CreateQuery<T>()
 255            where T : Entity
 261256        {
 261257            var typeParameter = typeof(T);
 258
 261259             if (ProxyTypesAssembly == null)
 6260            {
 261                //Try to guess proxy types assembly
 6262                var assembly = Assembly.GetAssembly(typeof(T));
 6263                 if (assembly != null)
 6264                {
 6265                    ProxyTypesAssembly = assembly;
 6266                }
 6267            }
 268
 261269            var logicalName = "";
 270
 261271             if (typeParameter.GetCustomAttributes(typeof(EntityLogicalNameAttribute), true).Length > 0)
 261272            {
 261273                logicalName = (typeParameter.GetCustomAttributes(typeof(EntityLogicalNameAttribute), true)[0] as EntityL
 261274            }
 275
 261276            return this.CreateQuery<T>(logicalName);
 261277        }
 278
 279        protected IQueryable<T> CreateQuery<T>(string entityLogicalName)
 280            where T : Entity
 5953281        {
 5953282            var subClassType = FindReflectedType(entityLogicalName);
 5953283             if (subClassType == null && !(typeof(T) == typeof(Entity)) || (typeof(T) == typeof(Entity) && string.IsNullO
 6284            {
 6285                throw new Exception($"The type {entityLogicalName} was not found");
 286            }
 287
 5947288            var lst = new List<T>();
 5947289             if (!Data.ContainsKey(entityLogicalName))
 458290            {
 458291                return lst.AsQueryable(); //Empty list
 292            }
 293
 145251294            foreach (var e in Data[entityLogicalName].Values)
 64392295            {
 64392296                 if (subClassType != null)
 23553297                {
 23553298                    var cloned = e.Clone(subClassType);
 23553299                    lst.Add((T)cloned);
 23553300                }
 301                else
 40839302                    lst.Add((T)e.Clone());
 64392303            }
 304
 5489305            return lst.AsQueryable();
 5947306        }
 307
 308        public IQueryable<Entity> CreateQueryFromEntityName(string entityName)
 0309        {
 0310            return Data[entityName].Values.AsQueryable();
 0311        }
 312
 313        public static IQueryable<Entity> TranslateLinkedEntityToLinq(XrmFakedContext context, LinkEntity le, IQueryable<
 1593314        {
 1593315             if (!string.IsNullOrEmpty(le.EntityAlias))
 1593316            {
 1593317                 if (!Regex.IsMatch(le.EntityAlias, "^[A-Za-z_](\\w|\\.)*$", RegexOptions.ECMAScript))
 0318                {
 0319                FakeOrganizationServiceFault.Throw(ErrorCodes.QueryBuilderInvalid_Alias, $"Invalid character specified f
 0320                }
 1593321            }
 322
 1593323            var leAlias = string.IsNullOrWhiteSpace(le.EntityAlias) ? le.LinkToEntityName : le.EntityAlias;
 1593324             context.EnsureEntityNameExistsInMetadata(le.LinkFromEntityName != linkFromAlias ? le.LinkFromEntityName : li
 1593325            context.EnsureEntityNameExistsInMetadata(le.LinkToEntityName);
 326
 1593327             if (!context.AttributeExistsInMetadata(le.LinkToEntityName, le.LinkToAttributeName))
 12328            {
 12329                FakeOrganizationServiceFault.Throw(ErrorCodes.QueryBuilderNoAttribute, string.Format("The attribute {0} 
 0330            }
 331
 1581332            IQueryable<Entity> inner = null;
 1581333             if (le.JoinOperator == JoinOperator.LeftOuter)
 362334            {
 335                //inner = context.CreateQuery<Entity>(le.LinkToEntityName);
 336
 337
 338                //filters are applied in the inner query and then ignored during filter evaluation
 362339                var outerQueryExpression = new QueryExpression()
 362340                {
 362341                    EntityName = le.LinkToEntityName,
 362342                    Criteria = le.LinkCriteria,
 362343                    ColumnSet = new ColumnSet(true)
 362344                };
 345
 362346                var outerQuery = TranslateQueryExpressionToLinq(context, outerQueryExpression);
 362347                inner = outerQuery;
 348
 362349            }
 350            else
 1219351            {
 352                //Filters are applied after joins
 1219353                inner = context.CreateQuery<Entity>(le.LinkToEntityName);
 1219354            }
 355
 356            //if (!le.Columns.AllColumns && le.Columns.Columns.Count == 0)
 357            //{
 358            //    le.Columns.AllColumns = true;   //Add all columns in the joined entity, otherwise we can't filter by r
 359            //}
 360
 1581361             if (string.IsNullOrWhiteSpace(linkFromAlias))
 1467362            {
 1467363                linkFromAlias = le.LinkFromAttributeName;
 1467364            }
 365            else
 114366            {
 114367                linkFromAlias += "." + le.LinkFromAttributeName;
 114368            }
 369
 1581370             switch (le.JoinOperator)
 371            {
 372                case JoinOperator.Inner:
 373                case JoinOperator.Natural:
 1219374                    query = query.Join(inner,
 1219375                                    outerKey => outerKey.KeySelector(linkFromAlias, context),
 1219376                                    innerKey => innerKey.KeySelector(le.LinkToAttributeName, context),
 1219377                                    (outerEl, innerEl) => outerEl.Clone(outerEl.GetType(), context).JoinAttributes(inner
 378
 1219379                    break;
 380                case JoinOperator.LeftOuter:
 362381                    query = query.GroupJoin(inner,
 362382                                    outerKey => outerKey.KeySelector(linkFromAlias, context),
 362383                                    innerKey => innerKey.KeySelector(le.LinkToAttributeName, context),
 362384                                    (outerEl, innerElemsCol) => new { outerEl, innerElemsCol })
 362385                                                .SelectMany(x => x.innerElemsCol.DefaultIfEmpty()
 362386                                                            , (x, y) => x.outerEl
 362387                                                                            .JoinAttributes(y, new ColumnSet(true), leAl
 388
 389
 362390                    break;
 391                default: //This shouldn't be reached as there are only 3 types of Join...
 0392                    throw new PullRequestException(string.Format("The join operator {0} is currently not supported. Feel
 393
 394            }
 395
 396            // Process nested linked entities recursively
 4971397            foreach (var nestedLinkedEntity in le.LinkEntities)
 114398            {
 114399                 if (string.IsNullOrWhiteSpace(le.EntityAlias))
 0400                {
 0401                    le.EntityAlias = le.LinkToEntityName;
 0402                }
 403
 114404                 if (string.IsNullOrWhiteSpace(nestedLinkedEntity.EntityAlias))
 54405                {
 54406                    nestedLinkedEntity.EntityAlias = EnsureUniqueLinkedEntityAlias(linkedEntities, nestedLinkedEntity.Li
 54407                }
 408
 114409                query = TranslateLinkedEntityToLinq(context, nestedLinkedEntity, query, le.Columns, linkedEntities, le.E
 114410            }
 411
 1581412            return query;
 1581413        }
 414
 415        private static string EnsureUniqueLinkedEntityAlias(IDictionary<string, int> linkedEntities, string entityName)
 368416        {
 368417             if (linkedEntities.ContainsKey(entityName))
 30418            {
 30419                linkedEntities[entityName]++;
 30420            }
 421            else
 338422            {
 338423                linkedEntities[entityName] = 1;
 338424            }
 425
 368426            return $"{entityName}{linkedEntities[entityName]}";
 368427        }
 428
 429
 430        protected static XElement RetrieveFetchXmlNode(XDocument xlDoc, string sName)
 1556431        {
 4926432            return xlDoc.Descendants().Where(e => e.Name.LocalName.Equals(sName)).FirstOrDefault();
 1556433        }
 434
 435        public static XDocument ParseFetchXml(string fetchXml)
 1316436        {
 437            try
 1316438            {
 1316439                return XDocument.Parse(fetchXml);
 440            }
 6441            catch (Exception ex)
 6442            {
 6443                throw new Exception(string.Format("FetchXml must be a valid XML document: {0}", ex.ToString()));
 444            }
 1310445        }
 446
 447        public static QueryExpression TranslateFetchXmlToQueryExpression(XrmFakedContext context, string fetchXml)
 643448        {
 643449            return TranslateFetchXmlDocumentToQueryExpression(context, ParseFetchXml(fetchXml));
 577450        }
 451
 452        public static QueryExpression TranslateFetchXmlDocumentToQueryExpression(XrmFakedContext context, XDocument xlDo
 1310453        {
 454            //Validate nodes
 9629455             if (!xlDoc.Descendants().All(el => el.IsFetchXmlNodeValid()))
 36456                throw new Exception("At least some node is not valid");
 457
 458            //Root node
 1268459             if (!xlDoc.Root.Name.LocalName.Equals("fetch"))
 0460            {
 0461                throw new Exception("Root node must be fetch");
 462            }
 463
 1268464            var entityNode = RetrieveFetchXmlNode(xlDoc, "entity");
 1268465            var query = new QueryExpression(entityNode.GetAttribute("name").Value);
 466
 1268467            query.ColumnSet = xlDoc.ToColumnSet();
 468
 469            // Ordering is done after grouping/aggregation
 1268470             if (!xlDoc.IsAggregateFetchXml())
 1124471            {
 1124472                var orders = xlDoc.ToOrderExpressionList();
 3992473                foreach (var order in orders)
 310474                {
 310475                    query.AddOrder(order.AttributeName, order.OrderType);
 310476                }
 1124477            }
 478
 1268479            query.Distinct = xlDoc.IsDistincFetchXml();
 480
 1268481            query.Criteria = xlDoc.ToCriteria(context);
 482
 1238483            query.TopCount = xlDoc.ToTopCount();
 484
 1238485             query.PageInfo.Count = xlDoc.ToCount() ?? 0;
 1232486             query.PageInfo.PageNumber = xlDoc.ToPageNumber() ?? 1;
 1232487            query.PageInfo.ReturnTotalRecordCount = xlDoc.ToReturnTotalRecordCount();
 488
 1232489            var linkedEntities = xlDoc.ToLinkEntities(context);
 4024490            foreach (var le in linkedEntities)
 164491            {
 164492                query.LinkEntities.Add(le);
 164493            }
 494
 1232495            return query;
 1232496        }
 497
 498        public static IQueryable<Entity> TranslateQueryExpressionToLinq(XrmFakedContext context, QueryExpression qe)
 2939499        {
 2945500             if (qe == null) return null;
 501
 502            //Start form the root entity and build a LINQ query to execute the query against the In-Memory context:
 2933503            context.EnsureEntityNameExistsInMetadata(qe.EntityName);
 504
 2933505            IQueryable<Entity> query = null;
 506
 2933507            query = context.CreateQuery<Entity>(qe.EntityName);
 508
 2933509            var linkedEntities = new Dictionary<string, int>();
 510
 511#if  !FAKE_XRM_EASY
 2473512            ValidateAliases(qe, context);
 513#endif
 514
 515            // Add as many Joins as linked entities
 11745516            foreach (var le in qe.LinkEntities)
 1479517            {
 1479518                 if (string.IsNullOrWhiteSpace(le.EntityAlias))
 314519                {
 314520                    le.EntityAlias = EnsureUniqueLinkedEntityAlias(linkedEntities, le.LinkToEntityName);
 314521                }
 522
 1479523                query = TranslateLinkedEntityToLinq(context, le, query, qe.ColumnSet, linkedEntities);
 1467524            }
 525
 526            // Compose the expression tree that represents the parameter to the predicate.
 2921527            ParameterExpression entity = Expression.Parameter(typeof(Entity));
 2921528            var expTreeBody = TranslateQueryExpressionFiltersToExpression(context, qe, entity);
 2888529            Expression<Func<Entity, bool>> lambda = Expression.Lambda<Func<Entity, bool>>(expTreeBody, entity);
 2888530            query = query.Where(lambda);
 531
 532            //Sort results
 2888533             if (qe.Orders != null)
 2888534            {
 2888535                 if (qe.Orders.Count > 0)
 500536                {
 500537                    IOrderedQueryable<Entity> orderedQuery = null;
 538
 500539                    var order = qe.Orders[0];
 500540                     if (order.OrderType == OrderType.Ascending)
 464541                        orderedQuery = query.OrderBy(e => e.Attributes.ContainsKey(order.AttributeName) ? e[order.Attrib
 542                    else
 36543                        orderedQuery = query.OrderByDescending(e => e.Attributes.ContainsKey(order.AttributeName) ? e[or
 544
 545                    //Subsequent orders should use ThenBy and ThenByDescending
 1048546                     for (var i = 1; i < qe.Orders.Count; i++)
 24547                    {
 24548                        var thenOrder = qe.Orders[i];
 24549                         if (thenOrder.OrderType == OrderType.Ascending)
 12550                            orderedQuery = orderedQuery.ThenBy(e => e.Attributes.ContainsKey(thenOrder.AttributeName) ? 
 551                        else
 12552                            orderedQuery = orderedQuery.ThenByDescending(e => e[thenOrder.AttributeName], new XrmOrderBy
 24553                    }
 554
 500555                    query = orderedQuery;
 500556                }
 2888557            }
 558
 559            //Project the attributes in the root column set  (must be applied after the where and order clauses, not bef
 2888560            query = query.Select(x => x.Clone(x.GetType(), context).ProjectAttributes(qe, context));
 561
 2888562            return query;
 2894563        }
 564
 565#if !FAKE_XRM_EASY
 566        protected static void ValidateAliases(QueryExpression qe, XrmFakedContext context)
 2473567        {
 2473568             if (qe.Criteria != null)
 2303569                ValidateAliases(qe, context, qe.Criteria);
 2473570             if (qe.LinkEntities != null)
 9899571                foreach (var link in qe.LinkEntities)
 1240572                {
 1240573                    ValidateAliases(qe, context, link);
 1240574                }
 2473575        }
 576
 577        protected static void ValidateAliases(QueryExpression qe, XrmFakedContext context, LinkEntity link)
 1335578        {
 1335579             if (link.LinkCriteria != null)
 1250580                ValidateAliases(qe, context, link.LinkCriteria);
 1335581             if (link.LinkEntities != null)
 4195582                foreach (var innerLink in link.LinkEntities)
 95583                {
 95584                    ValidateAliases(qe, context, innerLink);
 95585                }
 1335586        }
 587
 588        protected static void ValidateAliases(QueryExpression qe, XrmFakedContext context, FilterExpression filter)
 3783589        {
 3783590             if (filter.Filters != null)
 11809591                foreach (var innerFilter in filter.Filters)
 230592                {
 230593                    ValidateAliases(qe, context, innerFilter);
 230594                }
 595
 3783596             if (filter.Conditions != null)
 16459597                foreach (var condition in filter.Conditions)
 2555598                {
 2555599                     if (!string.IsNullOrEmpty(condition.EntityName))
 100600                    {
 100601                        ValidateAliases(qe, context, condition);
 100602                    }
 2555603                }
 3783604        }
 605
 606        protected static void ValidateAliases(QueryExpression qe, XrmFakedContext context, ConditionExpression condition
 100607        {
 100608             var matches = qe.LinkEntities != null ? MatchByAlias(qe, context, condition, qe.LinkEntities) : 0;
 100609             if (matches > 1)
 0610            {
 0611                throw new FaultException<OrganizationServiceFault>(new OrganizationServiceFault(), $"Table {condition.En
 612            }
 100613             else if (matches == 0)
 15614            {
 30615                 if (qe.LinkEntities != null) matches = MatchByEntity(qe, context, condition, qe.LinkEntities);
 15616                 if (matches > 1)
 0617                {
 0618                    throw new FaultException<OrganizationServiceFault>(new OrganizationServiceFault(), $"There's more th
 619                }
 15620                 else if (matches == 0)
 5621                {
 10622                     if (condition.EntityName == qe.EntityName) return;
 0623                    throw new FaultException<OrganizationServiceFault>(new OrganizationServiceFault(), $"LinkEntity with
 624                }
 10625                condition.EntityName += "1";
 10626            }
 100627        }
 628
 629        protected static int MatchByEntity(QueryExpression qe, XrmFakedContext context, ConditionExpression condition, D
 25630        {
 25631            var matches = 0;
 95632            foreach (var link in linkEntities)
 10633            {
 10634                 if (string.IsNullOrEmpty(link.EntityAlias) && condition.EntityName == link.LinkToEntityName)
 10635                {
 10636                    matches += 1;
 10637                }
 20638                 if (link.LinkEntities != null) matches += MatchByEntity(qe, context, condition, link.LinkEntities);
 10639            }
 25640            return matches;
 25641        }
 642
 643        protected static int MatchByAlias(QueryExpression qe, XrmFakedContext context, ConditionExpression condition, Da
 235644        {
 235645            var matches = 0;
 975646            foreach (var link in linkEntities)
 135647            {
 135648                 if (link.EntityAlias == condition.EntityName)
 85649                {
 85650                    matches += 1;
 85651                }
 270652                 if (link.LinkEntities != null) matches += MatchByAlias(qe, context, condition, link.LinkEntities);
 135653            }
 235654            return matches;
 235655        }
 656#endif
 657
 658        protected static Expression TranslateConditionExpression(QueryExpression qe, XrmFakedContext context, TypedCondi
 3020659        {
 3020660            Expression attributesProperty = Expression.Property(
 3020661                entity,
 3020662                "Attributes"
 3020663                );
 664
 665
 666            //If the attribute comes from a joined entity, then we need to access the attribute from the join
 667            //But the entity name attribute only exists >= 2013
 668#if FAKE_XRM_EASY_2013 || FAKE_XRM_EASY_2015 || FAKE_XRM_EASY_2016 || FAKE_XRM_EASY_365 || FAKE_XRM_EASY_9
 2549669            string attributeName = "";
 670
 671            //Do not prepend the entity name if the EntityLogicalName is the same as the QueryExpression main logical na
 672
 2549673             if (!string.IsNullOrWhiteSpace(c.CondExpression.EntityName) && !c.CondExpression.EntityName.Equals(qe.Entity
 95674            {
 95675                attributeName = c.CondExpression.EntityName + "." + c.CondExpression.AttributeName;
 95676            }
 677            else
 2454678                attributeName = c.CondExpression.AttributeName;
 679
 2549680            Expression containsAttributeExpression = Expression.Call(
 2549681                attributesProperty,
 2549682                typeof(AttributeCollection).GetMethod("ContainsKey", new Type[] { typeof(string) }),
 2549683                Expression.Constant(attributeName)
 2549684                );
 685
 2549686            Expression getAttributeValueExpr = Expression.Property(
 2549687                            attributesProperty, "Item",
 2549688                            Expression.Constant(attributeName, typeof(string))
 2549689                            );
 690#else
 471691            Expression containsAttributeExpression = Expression.Call(
 471692                attributesProperty,
 471693                typeof(AttributeCollection).GetMethod("ContainsKey", new Type[] { typeof(string) }),
 471694                Expression.Constant(c.CondExpression.AttributeName)
 471695                );
 696
 471697            Expression getAttributeValueExpr = Expression.Property(
 471698                            attributesProperty, "Item",
 471699                            Expression.Constant(c.CondExpression.AttributeName, typeof(string))
 471700                            );
 701#endif
 702
 703
 704
 3020705            Expression getNonBasicValueExpr = getAttributeValueExpr;
 706
 3020707            Expression operatorExpression = null;
 708
 3020709             switch (c.CondExpression.Operator)
 710            {
 711                case ConditionOperator.Equal:
 712                case ConditionOperator.Today:
 713                case ConditionOperator.Yesterday:
 714                case ConditionOperator.Tomorrow:
 715                case ConditionOperator.EqualUserId:
 2269716                    operatorExpression = TranslateConditionExpressionEqual(context, c, getNonBasicValueExpr, containsAtt
 2265717                    break;
 718
 719                case ConditionOperator.NotEqualUserId:
 6720                    operatorExpression = Expression.Not(TranslateConditionExpressionEqual(context, c, getNonBasicValueEx
 6721                    break;
 722
 723                case ConditionOperator.EqualBusinessId:
 6724                    operatorExpression = TranslateConditionExpressionEqual(context, c, getNonBasicValueExpr, containsAtt
 6725                    break;
 726
 727                case ConditionOperator.NotEqualBusinessId:
 0728                    operatorExpression = Expression.Not(TranslateConditionExpressionEqual(context, c, getNonBasicValueEx
 0729                    break;
 730
 731                case ConditionOperator.BeginsWith:
 732                case ConditionOperator.Like:
 66733                    operatorExpression = TranslateConditionExpressionLike(c, getNonBasicValueExpr, containsAttributeExpr
 66734                    break;
 735
 736                case ConditionOperator.EndsWith:
 18737                    operatorExpression = TranslateConditionExpressionEndsWith(c, getNonBasicValueExpr, containsAttribute
 18738                    break;
 739
 740                case ConditionOperator.Contains:
 24741                    operatorExpression = TranslateConditionExpressionContains(c, getNonBasicValueExpr, containsAttribute
 24742                    break;
 743
 744                case ConditionOperator.NotEqual:
 20745                    operatorExpression = Expression.Not(TranslateConditionExpressionEqual(context, c, getNonBasicValueEx
 20746                    break;
 747
 748                case ConditionOperator.DoesNotBeginWith:
 749                case ConditionOperator.DoesNotEndWith:
 750                case ConditionOperator.NotLike:
 751                case ConditionOperator.DoesNotContain:
 12752                    operatorExpression = Expression.Not(TranslateConditionExpressionLike(c, getNonBasicValueExpr, contai
 12753                    break;
 754
 755                case ConditionOperator.Null:
 71756                    operatorExpression = TranslateConditionExpressionNull(c, getNonBasicValueExpr, containsAttributeExpr
 71757                    break;
 758
 759                case ConditionOperator.NotNull:
 108760                    operatorExpression = Expression.Not(TranslateConditionExpressionNull(c, getNonBasicValueExpr, contai
 108761                    break;
 762
 763                case ConditionOperator.GreaterThan:
 24764                    operatorExpression = TranslateConditionExpressionGreaterThan(c, getNonBasicValueExpr, containsAttrib
 24765                    break;
 766
 767                case ConditionOperator.GreaterEqual:
 38768                    operatorExpression = TranslateConditionExpressionGreaterThanOrEqual(context, c, getNonBasicValueExpr
 38769                    break;
 770
 771                case ConditionOperator.LessThan:
 36772                    operatorExpression = TranslateConditionExpressionLessThan(c, getNonBasicValueExpr, containsAttribute
 36773                    break;
 774
 775                case ConditionOperator.LessEqual:
 28776                    operatorExpression = TranslateConditionExpressionLessThanOrEqual(context, c, getNonBasicValueExpr, c
 28777                    break;
 778
 779                case ConditionOperator.In:
 36780                    operatorExpression = TranslateConditionExpressionIn(c, getNonBasicValueExpr, containsAttributeExpres
 34781                    break;
 782
 783                case ConditionOperator.NotIn:
 2784                    operatorExpression = Expression.Not(TranslateConditionExpressionIn(c, getNonBasicValueExpr, contains
 2785                    break;
 786
 787                case ConditionOperator.On:
 12788                    operatorExpression = TranslateConditionExpressionEqual(context, c, getNonBasicValueExpr, containsAtt
 12789                    break;
 790
 791                case ConditionOperator.NotOn:
 0792                    operatorExpression = Expression.Not(TranslateConditionExpressionEqual(context, c, getNonBasicValueEx
 0793                    break;
 794
 795                case ConditionOperator.OnOrAfter:
 12796                    operatorExpression = Expression.Or(
 12797                               TranslateConditionExpressionEqual(context, c, getNonBasicValueExpr, containsAttributeExpr
 12798                               TranslateConditionExpressionGreaterThan(c, getNonBasicValueExpr, containsAttributeExpress
 12799                    break;
 800                case ConditionOperator.LastXHours:
 801                case ConditionOperator.LastXDays:
 802                case ConditionOperator.Last7Days:
 803                case ConditionOperator.LastXWeeks:
 804                case ConditionOperator.LastXMonths:
 805                case ConditionOperator.LastXYears:
 36806                    operatorExpression = TranslateConditionExpressionLast(c, getNonBasicValueExpr, containsAttributeExpr
 36807                    break;
 808
 809                case ConditionOperator.OnOrBefore:
 18810                    operatorExpression = Expression.Or(
 18811                                TranslateConditionExpressionEqual(context, c, getNonBasicValueExpr, containsAttributeExp
 18812                                TranslateConditionExpressionLessThan(c, getNonBasicValueExpr, containsAttributeExpressio
 18813                    break;
 814
 815                case ConditionOperator.Between:
 12816                     if (c.CondExpression.Values.Count != 2)
 6817                    {
 6818                        throw new Exception("Between operator requires exactly 2 values.");
 819                    }
 6820                    operatorExpression = TranslateConditionExpressionBetween(c, getNonBasicValueExpr, containsAttributeE
 6821                    break;
 822
 823                case ConditionOperator.NotBetween:
 12824                     if (c.CondExpression.Values.Count != 2)
 6825                    {
 6826                        throw new Exception("Not-Between operator requires exactly 2 values.");
 827                    }
 6828                    operatorExpression = Expression.Not(TranslateConditionExpressionBetween(c, getNonBasicValueExpr, con
 6829                    break;
 830#if !FAKE_XRM_EASY && !FAKE_XRM_EASY_2013
 831                case ConditionOperator.OlderThanXMinutes:
 832                case ConditionOperator.OlderThanXHours:
 833                case ConditionOperator.OlderThanXDays:
 834                case ConditionOperator.OlderThanXWeeks:
 835                case ConditionOperator.OlderThanXYears:
 836#endif
 837                case ConditionOperator.OlderThanXMonths:
 38838                    operatorExpression = TranslateConditionExpressionOlderThan(c, getNonBasicValueExpr, containsAttribut
 38839                    break;
 840
 841                case ConditionOperator.NextXHours:
 842                case ConditionOperator.NextXDays:
 843                case ConditionOperator.Next7Days:
 844                case ConditionOperator.NextXWeeks:
 845                case ConditionOperator.NextXMonths:
 846                case ConditionOperator.NextXYears:
 36847                    operatorExpression = TranslateConditionExpressionNext(c, getNonBasicValueExpr, containsAttributeExpr
 36848                    break;
 849                case ConditionOperator.ThisYear:
 850                case ConditionOperator.LastYear:
 851                case ConditionOperator.NextYear:
 852                case ConditionOperator.ThisMonth:
 853                case ConditionOperator.LastMonth:
 854                case ConditionOperator.NextMonth:
 855                case ConditionOperator.LastWeek:
 856                case ConditionOperator.ThisWeek:
 857                case ConditionOperator.NextWeek:
 858                case ConditionOperator.InFiscalYear:
 60859                    operatorExpression = TranslateConditionExpressionBetweenDates(c, getNonBasicValueExpr, containsAttri
 60860                    break;
 861#if FAKE_XRM_EASY_9
 862                case ConditionOperator.ContainValues:
 11863                    operatorExpression = TranslateConditionExpressionContainValues(c, getNonBasicValueExpr, containsAttr
 9864                    break;
 865
 866                case ConditionOperator.DoesNotContainValues:
 3867                    operatorExpression = Expression.Not(TranslateConditionExpressionContainValues(c, getNonBasicValueExp
 3868                    break;
 869#endif
 870
 871                default:
 6872                    throw new PullRequestException(string.Format("Operator {0} not yet implemented for condition express
 873
 874
 875            }
 876
 2994877             if (c.IsOuter)
 44878            {
 879                //If outer join, filter is optional, only if there was a value
 44880                return Expression.Constant(true);
 881            }
 882            else
 2950883                return operatorExpression;
 884
 2994885        }
 886
 887        private static void ValidateSupportedTypedExpression(TypedConditionExpression typedExpression)
 3021888        {
 3021889            Expression validateOperatorTypeExpression = Expression.Empty();
 3021890            ConditionOperator[] supportedOperators = (ConditionOperator[])Enum.GetValues(typeof(ConditionOperator));
 891
 892#if FAKE_XRM_EASY_9
 543893             if (typedExpression.AttributeType == typeof(OptionSetValueCollection))
 39894            {
 39895                supportedOperators = new[]
 39896                {
 39897                    ConditionOperator.ContainValues,
 39898                    ConditionOperator.DoesNotContainValues,
 39899                    ConditionOperator.Equal,
 39900                    ConditionOperator.NotEqual,
 39901                    ConditionOperator.NotNull,
 39902                    ConditionOperator.Null,
 39903                    ConditionOperator.In,
 39904                    ConditionOperator.NotIn,
 39905                };
 39906            }
 907#endif
 908
 3021909             if (!supportedOperators.Contains(typedExpression.CondExpression.Operator))
 1910            {
 1911                FakeOrganizationServiceFault.Throw(ErrorCodes.InvalidOperatorCode, "The operator is not valid or it is n
 0912            }
 3020913        }
 914
 915        protected static Expression GetAppropiateTypedValue(object value)
 591916        {
 917            //Basic types conversions
 918            //Special case => datetime is sent as a string
 591919             if (value is string)
 168920            {
 921                DateTime dtDateTimeConversion;
 168922                 if (DateTime.TryParse(value.ToString(), CultureInfo.InvariantCulture, DateTimeStyles.AdjustToUniversal, 
 0923                {
 0924                    return Expression.Constant(dtDateTimeConversion, typeof(DateTime));
 925                }
 926                else
 168927                {
 168928                    return GetCaseInsensitiveExpression(Expression.Constant(value, typeof(string)));
 929                }
 930            }
 423931             else if (value is EntityReference)
 12932            {
 12933                var cast = (value as EntityReference).Id;
 12934                return Expression.Constant(cast);
 935            }
 411936             else if (value is OptionSetValue)
 0937            {
 0938                var cast = (value as OptionSetValue).Value;
 0939                return Expression.Constant(cast);
 940            }
 411941             else if (value is Money)
 0942            {
 0943                var cast = (value as Money).Value;
 0944                return Expression.Constant(cast);
 945            }
 411946            return Expression.Constant(value);
 591947        }
 948
 949        protected static Type GetAppropiateTypeForValue(object value)
 114950        {
 951            //Basic types conversions
 952            //Special case => datetime is sent as a string
 114953             if (value is string)
 6954            {
 955                DateTime dtDateTimeConversion;
 6956                 if (DateTime.TryParse(value.ToString(), CultureInfo.InvariantCulture, DateTimeStyles.AdjustToUniversal, 
 0957                {
 0958                    return typeof(DateTime);
 959                }
 960                else
 6961                {
 6962                    return typeof(string);
 963                }
 964            }
 965            else
 108966                return value.GetType();
 114967        }
 968
 969        protected static Expression GetAppropiateTypedValueAndType(object value, Type attributeType)
 2942970        {
 2942971             if (attributeType == null)
 591972                return GetAppropiateTypedValue(value);
 973
 2351974             if (Nullable.GetUnderlyingType(attributeType) != null)
 312975            {
 312976                attributeType = Nullable.GetUnderlyingType(attributeType);
 312977            }
 978
 979            //Basic types conversions
 980            //Special case => datetime is sent as a string
 2351981             if (value is string)
 635982            {
 983                int iValue;
 984
 985                DateTime dtDateTimeConversion;
 986                Guid id;
 635987                 if (attributeType.IsDateTime()  //Only convert to DateTime if the attribute's type was DateTime
 635988                    && DateTime.TryParse(value.ToString(), CultureInfo.InvariantCulture, DateTimeStyles.AdjustToUniversa
 6989                {
 6990                    return Expression.Constant(dtDateTimeConversion, typeof(DateTime));
 991                }
 629992                 else if (attributeType.IsOptionSet() && int.TryParse(value.ToString(), out iValue))
 6993                {
 6994                    return Expression.Constant(iValue, typeof(int));
 995                }
 623996                 else if ((attributeType == typeof(EntityReference) || attributeType == typeof(Guid)) && Guid.TryParse((s
 18997                {
 18998                    return Expression.Constant(id);
 999                }
 1000                else
 6051001                {
 6051002                    return GetCaseInsensitiveExpression(Expression.Constant(value, typeof(string)));
 1003                }
 1004            }
 17161005             else if (value is EntityReference)
 231006            {
 231007                var cast = (value as EntityReference).Id;
 231008                return Expression.Constant(cast);
 1009            }
 16931010             else if (value is OptionSetValue)
 461011            {
 461012                var cast = (value as OptionSetValue).Value;
 461013                return Expression.Constant(cast);
 1014            }
 16471015             else if (value is Money)
 61016            {
 61017                var cast = (value as Money).Value;
 61018                return Expression.Constant(cast);
 1019            }
 16411020            return Expression.Constant(value);
 29421021        }
 1022
 1023        protected static Expression GetAppropiateCastExpressionBasedOnType(Type t, Expression input, object value)
 30961024        {
 30961025            var typedExpression = GetAppropiateCastExpressionBasedOnAttributeTypeOrValue(input, value, t);
 1026
 1027            //Now, any value (entity reference, string, int, etc,... could be wrapped in an AliasedValue object
 1028            //So let's add this
 30961029            var getValueFromAliasedValueExp = Expression.Call(Expression.Convert(input, typeof(AliasedValue)),
 30961030                                            typeof(AliasedValue).GetMethod("get_Value"));
 1031
 30961032            var exp = Expression.Condition(Expression.TypeIs(input, typeof(AliasedValue)),
 30961033                    GetAppropiateCastExpressionBasedOnAttributeTypeOrValue(getValueFromAliasedValueExp, value, t),
 30961034                    typedExpression //Not an aliased value
 30961035                );
 1036
 30961037            return exp;
 30961038        }
 1039
 1040        //protected static Expression GetAppropiateCastExpressionBasedOnValue(XrmFakedContext context, Expression input,
 1041        //{
 1042        //    var typedExpression = GetAppropiateCastExpressionBasedOnAttributeTypeOrValue(context, input, value, sEntit
 1043
 1044        //    //Now, any value (entity reference, string, int, etc,... could be wrapped in an AliasedValue object
 1045        //    //So let's add this
 1046        //    var getValueFromAliasedValueExp = Expression.Call(Expression.Convert(input, typeof(AliasedValue)),
 1047        //                                    typeof(AliasedValue).GetMethod("get_Value"));
 1048
 1049        //    var  exp = Expression.Condition(Expression.TypeIs(input, typeof(AliasedValue)),
 1050        //            GetAppropiateCastExpressionBasedOnAttributeTypeOrValue(context, getValueFromAliasedValueExp, value
 1051        //            typedExpression //Not an aliased value
 1052        //        );
 1053
 1054        //    return exp;
 1055        //}
 1056
 1057        protected static Expression GetAppropiateCastExpressionBasedOnValueInherentType(Expression input, object value)
 12301058        {
 12301059             if (value is Guid || value is EntityReference)
 4541060                return GetAppropiateCastExpressionBasedGuid(input); //Could be compared against an EntityReference
 7761061             if (value is int || value is OptionSetValue)
 1921062                return GetAppropiateCastExpressionBasedOnInt(input); //Could be compared against an OptionSet
 5841063             if (value is decimal || value is Money)
 01064                return GetAppropiateCastExpressionBasedOnDecimal(input); //Could be compared against a Money
 5841065             if (value is bool)
 1041066                return GetAppropiateCastExpressionBasedOnBoolean(input); //Could be a BooleanManagedProperty
 4801067             if (value is string)
 3841068            {
 3841069                return GetAppropiateCastExpressionBasedOnString(input, value);
 1070            }
 961071            return GetAppropiateCastExpressionDefault(input, value); //any other type
 12301072        }
 1073
 1074        protected static Expression GetAppropiateCastExpressionBasedOnAttributeTypeOrValue(Expression input, object valu
 61921075        {
 61921076             if (attributeType != null)
 49621077            {
 49621078                 if (Nullable.GetUnderlyingType(attributeType) != null)
 6241079                {
 6241080                    attributeType = Nullable.GetUnderlyingType(attributeType);
 6241081                }
 1082#if FAKE_XRM_EASY || FAKE_XRM_EASY_2013 || FAKE_XRM_EASY_2015
 24201083                 if (attributeType == typeof(Microsoft.Xrm.Client.CrmEntityReference))
 01084                    return GetAppropiateCastExpressionBasedGuid(input);
 1085#endif
 49621086                 if (attributeType == typeof(Guid))
 7801087                    return GetAppropiateCastExpressionBasedGuid(input);
 41821088                 if (attributeType == typeof(EntityReference))
 4541089                    return GetAppropiateCastExpressionBasedOnEntityReference(input, value);
 37281090                 if (attributeType == typeof(int) || attributeType == typeof(Nullable<int>) || attributeType.IsOptionSet(
 11501091                    return GetAppropiateCastExpressionBasedOnInt(input);
 25781092                 if (attributeType == typeof(decimal) || attributeType == typeof(Money))
 361093                    return GetAppropiateCastExpressionBasedOnDecimal(input);
 25421094                 if (attributeType == typeof(bool) || attributeType == typeof(BooleanManagedProperty))
 721095                    return GetAppropiateCastExpressionBasedOnBoolean(input);
 24701096                 if (attributeType == typeof(string))
 13901097                    return GetAppropiateCastExpressionBasedOnStringAndType(input, value, attributeType);
 10801098                 if (attributeType.IsDateTime())
 9161099                    return GetAppropiateCastExpressionBasedOnDateTime(input, value);
 1100#if FAKE_XRM_EASY_9
 841101                 if (attributeType.IsOptionSetValueCollection())
 681102                    return GetAppropiateCastExpressionBasedOnOptionSetValueCollection(input);
 1103#endif
 1104
 961105                return GetAppropiateCastExpressionDefault(input, value); //any other type
 1106            }
 1107
 12301108            return GetAppropiateCastExpressionBasedOnValueInherentType(input, value); //Dynamic entities
 61921109        }
 1110        protected static Expression GetAppropiateCastExpressionBasedOnString(Expression input, object value)
 3841111        {
 3841112            var defaultStringExpression = GetCaseInsensitiveExpression(GetAppropiateCastExpressionDefault(input, value))
 1113
 1114            DateTime dtDateTimeConversion;
 3841115             if (DateTime.TryParse(value.ToString(), out dtDateTimeConversion))
 01116            {
 01117                return Expression.Convert(input, typeof(DateTime));
 1118            }
 1119
 1120            int iValue;
 3841121             if (int.TryParse(value.ToString(), out iValue))
 521122            {
 521123                return Expression.Condition(Expression.TypeIs(input, typeof(OptionSetValue)),
 521124                    GetToStringExpression<Int32>(GetAppropiateCastExpressionBasedOnInt(input)),
 521125                    defaultStringExpression
 521126                );
 1127            }
 1128
 3321129            return defaultStringExpression;
 3841130        }
 1131
 1132        protected static Expression GetAppropiateCastExpressionBasedOnStringAndType(Expression input, object value, Type
 13901133        {
 13901134            var defaultStringExpression = GetCaseInsensitiveExpression(GetAppropiateCastExpressionDefault(input, value))
 1135
 1136            int iValue;
 13901137             if (attributeType.IsOptionSet() && int.TryParse(value.ToString(), out iValue))
 01138            {
 01139                return Expression.Condition(Expression.TypeIs(input, typeof(OptionSetValue)),
 01140                    GetToStringExpression<Int32>(GetAppropiateCastExpressionBasedOnInt(input)),
 01141                    defaultStringExpression
 01142                );
 1143            }
 1144
 13901145            return defaultStringExpression;
 13901146        }
 1147
 1148        protected static Expression GetAppropiateCastExpressionBasedOnDateTime(Expression input, object value)
 9161149        {
 1150            // Convert to DateTime if string
 1151            DateTime _;
 9161152             if (value is DateTime || value is string && DateTime.TryParse(value.ToString(), out _))
 9161153            {
 9161154                return Expression.Convert(input, typeof(DateTime));
 1155            }
 1156
 01157            return input; // return directly
 9161158        }
 1159
 1160        protected static Expression GetAppropiateCastExpressionDefault(Expression input, object value)
 19661161        {
 19661162            return Expression.Convert(input, value.GetType());  //Default type conversion
 19661163        }
 1164        protected static Expression GetAppropiateCastExpressionBasedGuid(Expression input)
 12341165        {
 12341166            var getIdFromEntityReferenceExpr = Expression.Call(Expression.TypeAs(input, typeof(EntityReference)),
 12341167                typeof(EntityReference).GetMethod("get_Id"));
 1168
 12341169            return Expression.Condition(
 12341170                Expression.TypeIs(input, typeof(EntityReference)),  //If input is an entity reference, compare the Guid 
 12341171                Expression.Convert(
 12341172                    getIdFromEntityReferenceExpr,
 12341173                    typeof(Guid)),
 12341174                Expression.Condition(Expression.TypeIs(input, typeof(Guid)),  //If any other case, then just compare it 
 12341175                    Expression.Convert(input, typeof(Guid)),
 12341176                    Expression.Constant(Guid.Empty, typeof(Guid))));
 12341177        }
 1178
 1179        protected static Expression GetAppropiateCastExpressionBasedOnEntityReference(Expression input, object value)
 4541180        {
 1181            Guid guid;
 4541182             if (value is string && !Guid.TryParse((string)value, out guid))
 121183            {
 121184                var getNameFromEntityReferenceExpr = Expression.Call(Expression.TypeAs(input, typeof(EntityReference)),
 121185                    typeof(EntityReference).GetMethod("get_Name"));
 1186
 121187                return GetCaseInsensitiveExpression(Expression.Condition(Expression.TypeIs(input, typeof(EntityReference
 121188                    Expression.Convert(getNameFromEntityReferenceExpr, typeof(string)),
 121189                    Expression.Constant(string.Empty, typeof(string))));
 1190            }
 1191
 4421192            var getIdFromEntityReferenceExpr = Expression.Call(Expression.TypeAs(input, typeof(EntityReference)),
 4421193                typeof(EntityReference).GetMethod("get_Id"));
 1194
 4421195            return Expression.Condition(
 4421196                Expression.TypeIs(input, typeof(EntityReference)),  //If input is an entity reference, compare the Guid 
 4421197                Expression.Convert(
 4421198                    getIdFromEntityReferenceExpr,
 4421199                    typeof(Guid)),
 4421200                Expression.Condition(Expression.TypeIs(input, typeof(Guid)),  //If any other case, then just compare it 
 4421201                    Expression.Convert(input, typeof(Guid)),
 4421202                    Expression.Constant(Guid.Empty, typeof(Guid))));
 1203
 4541204        }
 1205
 1206        protected static Expression GetAppropiateCastExpressionBasedOnDecimal(Expression input)
 361207        {
 361208            return Expression.Condition(
 361209                        Expression.TypeIs(input, typeof(Money)),
 361210                                Expression.Convert(
 361211                                    Expression.Call(Expression.TypeAs(input, typeof(Money)),
 361212                                            typeof(Money).GetMethod("get_Value")),
 361213                                            typeof(decimal)),
 361214                           Expression.Condition(Expression.TypeIs(input, typeof(decimal)),
 361215                                        Expression.Convert(input, typeof(decimal)),
 361216                                        Expression.Constant(0.0M)));
 1217
 361218        }
 1219
 1220        protected static Expression GetAppropiateCastExpressionBasedOnBoolean(Expression input)
 1761221        {
 1761222            return Expression.Condition(
 1761223                        Expression.TypeIs(input, typeof(BooleanManagedProperty)),
 1761224                                Expression.Convert(
 1761225                                    Expression.Call(Expression.TypeAs(input, typeof(BooleanManagedProperty)),
 1761226                                            typeof(BooleanManagedProperty).GetMethod("get_Value")),
 1761227                                            typeof(bool)),
 1761228                           Expression.Condition(Expression.TypeIs(input, typeof(bool)),
 1761229                                        Expression.Convert(input, typeof(bool)),
 1761230                                        Expression.Constant(false)));
 1231
 1761232        }
 1233
 1234        protected static Expression GetAppropiateCastExpressionBasedOnInt(Expression input)
 13941235        {
 13941236            return Expression.Condition(
 13941237                        Expression.TypeIs(input, typeof(OptionSetValue)),
 13941238                                            Expression.Convert(
 13941239                                                Expression.Call(Expression.TypeAs(input, typeof(OptionSetValue)),
 13941240                                                        typeof(OptionSetValue).GetMethod("get_Value")),
 13941241                                                        typeof(int)),
 13941242                                                    Expression.Convert(input, typeof(int)));
 13941243        }
 1244
 1245        protected static Expression GetAppropiateCastExpressionBasedOnOptionSetValueCollection(Expression input)
 681246        {
 681247            return Expression.Call(typeof(XrmFakedContext).GetMethod("ConvertToHashSetOfInt"), input, Expression.Constan
 681248        }
 1249
 1250#if FAKE_XRM_EASY_9
 1251        public static HashSet<int> ConvertToHashSetOfInt(object input, bool isOptionSetValueCollectionAccepted)
 1441252        {
 1441253            var set = new HashSet<int>();
 1254
 1441255             var faultReason = $"The formatter threw an exception while trying to deserialize the message: There was an e
 1441256                $" http://schemas.microsoft.com/xrm/2011/Contracts/Services:query. The InnerException message was 'Error
 1441257                $"'http://schemas.microsoft.com/2003/10/Serialization/Arrays:anyType' contains data from a type that map
 1441258                $"'http://schemas.microsoft.com/xrm/2011/Contracts:{input?.GetType()}'. The deserializer has no knowledg
 1441259                $"Consider changing the implementation of the ResolveName method on your DataContractResolver to return 
 1441260                $"'{input?.GetType()}' and namespace 'http://schemas.microsoft.com/xrm/2011/Contracts'.'.  Please see In
 1261
 1441262             if (input is int)
 41263            {
 41264                set.Add((int)input);
 41265            }
 1401266             else if (input is string)
 11267            {
 11268                set.Add(int.Parse(input as string));
 11269            }
 1391270             else if (input is int[])
 01271            {
 01272                set.UnionWith(input as int[]);
 01273            }
 1391274             else if (input is string[])
 01275            {
 01276                set.UnionWith((input as string[]).Select(s => int.Parse(s)));
 01277            }
 1391278             else if (input is DataCollection<object>)
 281279            {
 281280                var collection = input as DataCollection<object>;
 1281
 621282                 if (collection.All(o => o is int))
 71283                {
 71284                    set.UnionWith(collection.Cast<int>());
 71285                }
 491286                 else if (collection.All(o => o is string))
 91287                {
 251288                    set.UnionWith(collection.Select(o => int.Parse(o as string)));
 91289                }
 121290                 else if (collection.Count == 1 && collection[0] is int[])
 81291                {
 81292                    set.UnionWith(collection[0] as int[]);
 81293                }
 41294                 else if (collection.Count == 1 && collection[0] is string[])
 01295                {
 01296                    set.UnionWith((collection[0] as string[]).Select(s => int.Parse(s)));
 01297                }
 1298                else
 41299                {
 41300                    throw new FaultException(new FaultReason(faultReason));
 1301                }
 241302            }
 1111303             else if (isOptionSetValueCollectionAccepted && input is OptionSetValueCollection)
 1101304            {
 3261305                set.UnionWith((input as OptionSetValueCollection).Select(osv => osv.Value));
 1101306            }
 1307            else
 11308            {
 11309                throw new FaultException(new FaultReason(faultReason));
 1310            }
 1311
 1391312            return set;
 1391313        }
 1314#endif
 1315
 1316        protected static Expression TransformExpressionGetDateOnlyPart(Expression input)
 1801317        {
 1801318            return Expression.Call(input, typeof(DateTime).GetMethod("get_Date"));
 1801319        }
 1320
 1321        protected static Expression TransformExpressionValueBasedOnOperator(ConditionOperator op, Expression input)
 50581322        {
 50581323             switch (op)
 1324            {
 1325                case ConditionOperator.Today:
 1326                case ConditionOperator.Yesterday:
 1327                case ConditionOperator.Tomorrow:
 1328                case ConditionOperator.On:
 1329                case ConditionOperator.OnOrAfter:
 1330                case ConditionOperator.OnOrBefore:
 1801331                    return TransformExpressionGetDateOnlyPart(input);
 1332
 1333                default:
 48781334                    return input; //No transformation
 1335            }
 50581336        }
 1337
 1338        protected static Expression TranslateConditionExpressionEqual(XrmFakedContext context, TypedConditionExpression 
 24091339        {
 1340
 24091341            BinaryExpression expOrValues = Expression.Or(Expression.Constant(false), Expression.Constant(false));
 1342
 24091343            object unaryOperatorValue = null;
 1344
 24091345             switch (c.CondExpression.Operator)
 1346            {
 1347                case ConditionOperator.Today:
 121348                    unaryOperatorValue = DateTime.Today;
 121349                    break;
 1350                case ConditionOperator.Yesterday:
 121351                    unaryOperatorValue = DateTime.Today.AddDays(-1);
 121352                    break;
 1353                case ConditionOperator.Tomorrow:
 121354                    unaryOperatorValue = DateTime.Today.AddDays(1);
 121355                    break;
 1356                case ConditionOperator.EqualUserId:
 1357                case ConditionOperator.NotEqualUserId:
 121358                    unaryOperatorValue = context.CallerId.Id;
 121359                    break;
 1360
 1361                case ConditionOperator.EqualBusinessId:
 1362                case ConditionOperator.NotEqualBusinessId:
 61363                    unaryOperatorValue = context.BusinessUnitId.Id;
 61364                    break;
 1365            }
 1366
 24091367             if (unaryOperatorValue != null)
 541368            {
 1369                //c.Values empty in this case
 541370                var leftHandSideExpression = GetAppropiateCastExpressionBasedOnType(c.AttributeType, getAttributeValueEx
 541371                var transformedExpression = TransformExpressionValueBasedOnOperator(c.CondExpression.Operator, leftHandS
 1372
 541373                expOrValues = Expression.Equal(transformedExpression,
 541374                                GetAppropiateTypedValueAndType(unaryOperatorValue, c.AttributeType));
 541375            }
 1376#if FAKE_XRM_EASY_9
 4051377             else if (c.AttributeType == typeof(OptionSetValueCollection))
 91378            {
 91379                var conditionValue = GetSingleConditionValue(c);
 1380
 61381                var leftHandSideExpression = GetAppropiateCastExpressionBasedOnType(c.AttributeType, getAttributeValueEx
 61382                var rightHandSideExpression = Expression.Constant(ConvertToHashSetOfInt(conditionValue, isOptionSetValue
 1383
 51384                expOrValues = Expression.Equal(
 51385                    Expression.Call(leftHandSideExpression, typeof(HashSet<int>).GetMethod("SetEquals"), rightHandSideEx
 51386                    Expression.Constant(true));
 51387            }
 1388#endif
 1389            else
 23461390            {
 117301391                foreach (object value in c.CondExpression.Values)
 23461392                {
 23461393                    var leftHandSideExpression = GetAppropiateCastExpressionBasedOnType(c.AttributeType, getAttributeVal
 23461394                    var transformedExpression = TransformExpressionValueBasedOnOperator(c.CondExpression.Operator, leftH
 1395
 23461396                    expOrValues = Expression.Or(expOrValues, Expression.Equal(
 23461397                                transformedExpression,
 23461398                                TransformExpressionValueBasedOnOperator(c.CondExpression.Operator, GetAppropiateTypedVal
 1399
 1400
 23461401                }
 23461402            }
 1403
 24051404            return Expression.AndAlso(
 24051405                            containsAttributeExpr,
 24051406                            Expression.AndAlso(Expression.NotEqual(getAttributeValueExpr, Expression.Constant(null)),
 24051407                                expOrValues));
 24051408        }
 1409
 1410        private static object GetSingleConditionValue(TypedConditionExpression c)
 91411        {
 91412             if (c.CondExpression.Values.Count != 1)
 11413            {
 11414                FakeOrganizationServiceFault.Throw(ErrorCodes.InvalidArgument, $"The {c.CondExpression.Operator} require
 01415            }
 1416
 81417            var conditionValue = c.CondExpression.Values.Single();
 1418
 81419             if (!(conditionValue is string) && conditionValue is IEnumerable)
 41420            {
 41421                var conditionValueEnumerable = conditionValue as IEnumerable;
 41422                var count = 0;
 1423
 261424                foreach (var obj in conditionValueEnumerable)
 71425                {
 71426                    count++;
 71427                    conditionValue = obj;
 71428                }
 1429
 41430                 if (count != 1)
 21431                {
 21432                    FakeOrganizationServiceFault.Throw(ErrorCodes.InvalidArgument, $"The {c.CondExpression.Operator} req
 01433                }
 21434            }
 1435
 61436            return conditionValue;
 61437        }
 1438
 1439        protected static Expression TranslateConditionExpressionIn(TypedConditionExpression tc, Expression getAttributeV
 381440        {
 381441            var c = tc.CondExpression;
 1442
 381443            BinaryExpression expOrValues = Expression.Or(Expression.Constant(false), Expression.Constant(false));
 1444
 1445#if FAKE_XRM_EASY_9
 181446             if (tc.AttributeType == typeof(OptionSetValueCollection))
 141447            {
 141448                var leftHandSideExpression = GetAppropiateCastExpressionBasedOnType(tc.AttributeType, getAttributeValueE
 141449                var rightHandSideExpression = Expression.Constant(ConvertToHashSetOfInt(c.Values, isOptionSetValueCollec
 1450
 121451                expOrValues = Expression.Equal(
 121452                    Expression.Call(leftHandSideExpression, typeof(HashSet<int>).GetMethod("SetEquals"), rightHandSideEx
 121453                    Expression.Constant(true));
 121454            }
 1455            else
 1456#endif
 241457            {
 1441458                foreach (object value in c.Values)
 361459                {
 361460                     if (value is Array)
 121461                    {
 1081462                        foreach (var a in ((Array)value))
 361463                        {
 361464                            expOrValues = Expression.Or(expOrValues, Expression.Equal(
 361465                                GetAppropiateCastExpressionBasedOnType(tc.AttributeType, getAttributeValueExpr, a),
 361466                                GetAppropiateTypedValueAndType(a, tc.AttributeType)));
 361467                        }
 121468                    }
 1469                    else
 241470                    {
 241471                        expOrValues = Expression.Or(expOrValues, Expression.Equal(
 241472                                    GetAppropiateCastExpressionBasedOnType(tc.AttributeType, getAttributeValueExpr, valu
 241473                                    GetAppropiateTypedValueAndType(value, tc.AttributeType)));
 241474                    }
 361475                }
 241476            }
 1477
 361478            return Expression.AndAlso(
 361479                            containsAttributeExpr,
 361480                            Expression.AndAlso(Expression.NotEqual(getAttributeValueExpr, Expression.Constant(null)),
 361481                                expOrValues));
 361482        }
 1483
 1484        //protected static Expression TranslateConditionExpressionOn(ConditionExpression c, Expression getAttributeValue
 1485        //{
 1486        //    BinaryExpression expOrValues = Expression.Or(Expression.Constant(false), Expression.Constant(false));
 1487        //    foreach (object value in c.Values)
 1488        //    {
 1489
 1490        //        expOrValues = Expression.Or(expOrValues, Expression.Equal(
 1491        //                    GetAppropiateCastExpressionBasedOnValue(getAttributeValueExpr, value),
 1492        //                    GetAppropiateTypedValue(value)));
 1493
 1494
 1495        //    }
 1496        //    return Expression.AndAlso(
 1497        //                    containsAttributeExpr,
 1498        //                    Expression.AndAlso(Expression.NotEqual(getAttributeValueExpr, Expression.Constant(null)),
 1499        //                        expOrValues));
 1500        //}
 1501
 1502        protected static Expression TranslateConditionExpressionGreaterThanOrEqual(XrmFakedContext context, TypedConditi
 381503        {
 1504            //var c = tc.CondExpression;
 1505
 381506            return Expression.Or(
 381507                                TranslateConditionExpressionEqual(context, tc, getAttributeValueExpr, containsAttributeE
 381508                                TranslateConditionExpressionGreaterThan(tc, getAttributeValueExpr, containsAttributeExpr
 1509
 381510        }
 1511        protected static Expression TranslateConditionExpressionGreaterThan(TypedConditionExpression tc, Expression getA
 741512        {
 741513            var c = tc.CondExpression;
 1514
 1481515             if (c.Values.Count(v => v != null) != 1)
 01516            {
 01517                throw new FaultException(new FaultReason($"The ConditonOperator.{c.Operator} requires 1 value/s, not {c.
 1518            }
 1519
 741520             if (tc.AttributeType == typeof(string))
 181521            {
 181522                return TranslateConditionExpressionGreaterThanString(tc, getAttributeValueExpr, containsAttributeExpr);
 1523            }
 561524             else if (GetAppropiateTypeForValue(c.Values[0]) == typeof(string))
 01525            {
 01526                return TranslateConditionExpressionGreaterThanString(tc, getAttributeValueExpr, containsAttributeExpr);
 1527            }
 1528            else
 561529            {
 561530                BinaryExpression expOrValues = Expression.Or(Expression.Constant(false), Expression.Constant(false));
 2801531                foreach (object value in c.Values)
 561532                {
 561533                    var leftHandSideExpression = GetAppropiateCastExpressionBasedOnType(tc.AttributeType, getAttributeVa
 561534                    var transformedExpression = TransformExpressionValueBasedOnOperator(tc.CondExpression.Operator, left
 1535
 561536                    expOrValues = Expression.Or(expOrValues,
 561537                            Expression.GreaterThan(
 561538                                transformedExpression,
 561539                                TransformExpressionValueBasedOnOperator(tc.CondExpression.Operator, GetAppropiateTypedVa
 561540                }
 561541                return Expression.AndAlso(
 561542                                containsAttributeExpr,
 561543                                Expression.AndAlso(Expression.NotEqual(getAttributeValueExpr, Expression.Constant(null))
 561544                                    expOrValues));
 1545            }
 1546
 741547        }
 1548
 1549        protected static Expression TranslateConditionExpressionGreaterThanString(TypedConditionExpression tc, Expressio
 181550        {
 181551            var c = tc.CondExpression;
 1552
 181553            BinaryExpression expOrValues = Expression.Or(Expression.Constant(false), Expression.Constant(false));
 901554            foreach (object value in c.Values)
 181555            {
 181556                var leftHandSideExpression = GetAppropiateCastExpressionBasedOnType(tc.AttributeType, getAttributeValueE
 181557                var transformedExpression = TransformExpressionValueBasedOnOperator(tc.CondExpression.Operator, leftHand
 1558
 181559                var left = transformedExpression;
 181560                var right = TransformExpressionValueBasedOnOperator(tc.CondExpression.Operator, GetAppropiateTypedValueA
 1561
 181562                var methodCallExpr = GetCompareToExpression<string>(left, right);
 1563
 181564                expOrValues = Expression.Or(expOrValues,
 181565                        Expression.GreaterThan(
 181566                            methodCallExpr,
 181567                            Expression.Constant(0)));
 181568            }
 181569            return Expression.AndAlso(
 181570                            containsAttributeExpr,
 181571                            Expression.AndAlso(Expression.NotEqual(getAttributeValueExpr, Expression.Constant(null)),
 181572                                expOrValues));
 181573        }
 1574
 1575        protected static Expression TranslateConditionExpressionLessThanOrEqual(XrmFakedContext context, TypedConditionE
 281576        {
 1577            //var c = tc.CondExpression;
 1578
 281579            return Expression.Or(
 281580                                TranslateConditionExpressionEqual(context, tc, getAttributeValueExpr, containsAttributeE
 281581                                TranslateConditionExpressionLessThan(tc, getAttributeValueExpr, containsAttributeExpr));
 1582
 281583        }
 1584
 1585        protected static Expression GetCompareToExpression<T>(Expression left, Expression right)
 481586        {
 481587            return Expression.Call(left, typeof(T).GetMethod("CompareTo", new Type[] { typeof(string) }), new[] { right 
 481588        }
 1589
 1590        protected static Expression TranslateConditionExpressionLessThanString(TypedConditionExpression tc, Expression g
 301591        {
 301592            var c = tc.CondExpression;
 1593
 301594            BinaryExpression expOrValues = Expression.Or(Expression.Constant(false), Expression.Constant(false));
 1501595            foreach (object value in c.Values)
 301596            {
 301597                var leftHandSideExpression = GetAppropiateCastExpressionBasedOnType(tc.AttributeType, getAttributeValueE
 301598                var transformedLeftHandSideExpression = TransformExpressionValueBasedOnOperator(tc.CondExpression.Operat
 1599
 301600                var rightHandSideExpression = TransformExpressionValueBasedOnOperator(tc.CondExpression.Operator, GetApp
 1601
 1602                //var compareToMethodCall = Expression.Call(transformedLeftHandSideExpression, typeof(string).GetMethod(
 301603                var compareToMethodCall = GetCompareToExpression<string>(transformedLeftHandSideExpression, rightHandSid
 1604
 301605                expOrValues = Expression.Or(expOrValues,
 301606                        Expression.LessThan(compareToMethodCall, Expression.Constant(0)));
 301607            }
 301608            return Expression.AndAlso(
 301609                            containsAttributeExpr,
 301610                            Expression.AndAlso(Expression.NotEqual(getAttributeValueExpr, Expression.Constant(null)),
 301611                                expOrValues));
 301612        }
 1613
 1614        protected static Expression TranslateConditionExpressionLessThan(TypedConditionExpression tc, Expression getAttr
 821615        {
 821616            var c = tc.CondExpression;
 1617
 1641618             if (c.Values.Count(v => v != null) != 1)
 01619            {
 01620                throw new FaultException(new FaultReason($"The ConditonOperator.{c.Operator} requires 1 value/s, not {c.
 1621            }
 1622
 821623             if (tc.AttributeType == typeof(string))
 241624            {
 241625                return TranslateConditionExpressionLessThanString(tc, getAttributeValueExpr, containsAttributeExpr);
 1626            }
 581627             else if (GetAppropiateTypeForValue(c.Values[0]) == typeof(string))
 61628            {
 61629                return TranslateConditionExpressionLessThanString(tc, getAttributeValueExpr, containsAttributeExpr);
 1630            }
 1631            else
 521632            {
 521633                BinaryExpression expOrValues = Expression.Or(Expression.Constant(false), Expression.Constant(false));
 2601634                foreach (object value in c.Values)
 521635                {
 521636                    var leftHandSideExpression = GetAppropiateCastExpressionBasedOnType(tc.AttributeType, getAttributeVa
 521637                    var transformedExpression = TransformExpressionValueBasedOnOperator(tc.CondExpression.Operator, left
 1638
 521639                    expOrValues = Expression.Or(expOrValues,
 521640                            Expression.LessThan(
 521641                                transformedExpression,
 521642                                TransformExpressionValueBasedOnOperator(tc.CondExpression.Operator, GetAppropiateTypedVa
 521643                }
 521644                return Expression.AndAlso(
 521645                                containsAttributeExpr,
 521646                                Expression.AndAlso(Expression.NotEqual(getAttributeValueExpr, Expression.Constant(null))
 521647                                    expOrValues));
 1648            }
 1649
 821650        }
 1651
 1652        protected static Expression TranslateConditionExpressionLast(TypedConditionExpression tc, Expression getAttribut
 361653        {
 361654            var c = tc.CondExpression;
 1655
 361656            var beforeDateTime = default(DateTime);
 361657            var currentDateTime = DateTime.UtcNow;
 361658             switch (c.Operator)
 1659            {
 1660                case ConditionOperator.LastXHours:
 61661                    beforeDateTime = currentDateTime.AddHours(-(int)c.Values[0]);
 61662                    break;
 1663                case ConditionOperator.LastXDays:
 61664                    beforeDateTime = currentDateTime.AddDays(-(int)c.Values[0]);
 61665                    break;
 1666                case ConditionOperator.Last7Days:
 61667                    beforeDateTime = currentDateTime.AddDays(-7);
 61668                    break;
 1669                case ConditionOperator.LastXWeeks:
 61670                    beforeDateTime = currentDateTime.AddDays(-7 * (int)c.Values[0]);
 61671                    break;
 1672                case ConditionOperator.LastXMonths:
 61673                    beforeDateTime = currentDateTime.AddMonths(-(int)c.Values[0]);
 61674                    break;
 1675                case ConditionOperator.LastXYears:
 61676                    beforeDateTime = currentDateTime.AddYears(-(int)c.Values[0]);
 61677                    break;
 1678            }
 1679
 361680            c.Values.Clear();
 361681            c.Values.Add(beforeDateTime);
 361682            c.Values.Add(currentDateTime);
 1683
 361684            return TranslateConditionExpressionBetween(tc, getAttributeValueExpr, containsAttributeExpr);
 361685        }
 1686
 1687        /// <summary>
 1688        /// Takes a condition expression which needs translating into a 'between two dates' expression and works out the
 1689        /// </summary>
 1690        protected static Expression TranslateConditionExpressionBetweenDates(TypedConditionExpression tc, Expression get
 601691        {
 601692            var c = tc.CondExpression;
 1693
 601694            DateTime? fromDate = null;
 601695            DateTime? toDate = null;
 1696
 601697            var today = DateTime.Today;
 601698            var thisYear = today.Year;
 601699            var thisMonth = today.Month;
 1700
 1701
 601702             switch (c.Operator)
 1703            {
 1704                case ConditionOperator.ThisYear: // From first day of this year to last day of this year
 61705                    fromDate = new DateTime(thisYear, 1, 1);
 61706                    toDate = new DateTime(thisYear, 12, 31);
 61707                    break;
 1708                case ConditionOperator.LastYear: // From first day of last year to last day of last year
 61709                    fromDate = new DateTime(thisYear - 1, 1, 1);
 61710                    toDate = new DateTime(thisYear - 1, 12, 31);
 61711                    break;
 1712                case ConditionOperator.NextYear: // From first day of next year to last day of next year
 61713                    fromDate = new DateTime(thisYear + 1, 1, 1);
 61714                    toDate = new DateTime(thisYear + 1, 12, 31);
 61715                    break;
 1716                case ConditionOperator.ThisMonth: // From first day of this month to last day of this month
 61717                    fromDate = new DateTime(thisYear, thisMonth, 1);
 1718                    // Last day of this month: Add one month to the first of this month, and then remove one day
 61719                    toDate = new DateTime(thisYear, thisMonth, 1).AddMonths(1).AddDays(-1);
 61720                    break;
 1721                case ConditionOperator.LastMonth: // From first day of last month to last day of last month
 61722                    fromDate = new DateTime(thisYear, thisMonth, 1).AddMonths(-1);
 1723                    // Last day of last month: One day before the first of this month
 61724                    toDate = new DateTime(thisYear, thisMonth, 1).AddDays(-1);
 61725                    break;
 1726                case ConditionOperator.NextMonth: // From first day of next month to last day of next month
 61727                    fromDate = new DateTime(thisYear, thisMonth, 1).AddMonths(1);
 1728                    // LAst day of Next Month: Add two months to the first of this month, and then go back one day
 61729                    toDate = new DateTime(thisYear, thisMonth, 1).AddMonths(2).AddDays(-1);
 61730                    break;
 1731                case ConditionOperator.ThisWeek:
 61732                    fromDate = today.ToFirstDayOfDeltaWeek();
 61733                    toDate = today.ToLastDayOfDeltaWeek().AddDays(1);
 61734                    break;
 1735                case ConditionOperator.LastWeek:
 61736                    fromDate = today.ToFirstDayOfDeltaWeek(-1);
 61737                    toDate = today.ToLastDayOfDeltaWeek(-1).AddDays(1);
 61738                    break;
 1739                case ConditionOperator.NextWeek:
 61740                    fromDate = today.ToFirstDayOfDeltaWeek(1);
 61741                    toDate = today.ToLastDayOfDeltaWeek(1).AddDays(1);
 61742                    break;
 1743                case ConditionOperator.InFiscalYear:
 61744                    var fiscalYear = (int)c.Values[0];
 61745                    c.Values.Clear();
 61746                    var fiscalYearDate = context.FiscalYearSettings?.StartDate ?? new DateTime(fiscalYear, 4, 1);
 61747                    fromDate = fiscalYearDate;
 61748                    toDate = fiscalYearDate.AddYears(1).AddDays(-1);
 61749                    break;
 1750            }
 1751
 601752            c.Values.Add(fromDate);
 601753            c.Values.Add(toDate);
 1754
 601755            return TranslateConditionExpressionBetween(tc, getAttributeValueExpr, containsAttributeExpr);
 601756        }
 1757
 1758
 1759        protected static Expression TranslateConditionExpressionOlderThan(TypedConditionExpression tc, Expression getAtt
 381760        {
 381761            var c = tc.CondExpression;
 1762
 381763            var valueToAdd = 0;
 1764
 381765             if (!int.TryParse(c.Values[0].ToString(), out valueToAdd))
 01766            {
 01767                throw new Exception(c.Operator + " requires an integer value in the ConditionExpression.");
 1768            }
 1769
 381770             if (valueToAdd <= 0)
 01771            {
 01772                throw new Exception(c.Operator + " requires a value greater than 0.");
 1773            }
 1774
 381775            DateTime toDate = default(DateTime);
 1776
 381777             switch (c.Operator)
 1778            {
 1779                case ConditionOperator.OlderThanXMonths:
 181780                    toDate = DateTime.UtcNow.AddMonths(-valueToAdd);
 181781                    break;
 1782#if !FAKE_XRM_EASY && !FAKE_XRM_EASY_2013
 1783                case ConditionOperator.OlderThanXMinutes:
 41784                    toDate = DateTime.UtcNow.AddMinutes(-valueToAdd);
 41785                    break;
 1786                case ConditionOperator.OlderThanXHours:
 41787                    toDate = DateTime.UtcNow.AddHours(-valueToAdd);
 41788                    break;
 1789                case ConditionOperator.OlderThanXDays:
 41790                    toDate = DateTime.UtcNow.AddDays(-valueToAdd);
 41791                    break;
 1792                case ConditionOperator.OlderThanXWeeks:
 41793                    toDate = DateTime.UtcNow.AddDays(-7 * valueToAdd);
 41794                    break;
 1795                case ConditionOperator.OlderThanXYears:
 41796                    toDate = DateTime.UtcNow.AddYears(-valueToAdd);
 41797                    break;
 1798#endif
 1799            }
 1800
 381801            return TranslateConditionExpressionOlderThan(tc, getAttributeValueExpr, containsAttributeExpr, toDate);
 381802        }
 1803
 1804
 1805        protected static Expression TranslateConditionExpressionBetween(TypedConditionExpression tc, Expression getAttri
 1441806        {
 1441807            var c = tc.CondExpression;
 1808
 1809            object value1, value2;
 1441810            value1 = c.Values[0];
 1441811            value2 = c.Values[1];
 1812
 1813            //Between the range...
 1441814            var exp = Expression.And(
 1441815                Expression.GreaterThanOrEqual(
 1441816                            GetAppropiateCastExpressionBasedOnType(tc.AttributeType, getAttributeValueExpr, value1),
 1441817                            GetAppropiateTypedValueAndType(value1, tc.AttributeType)),
 1441818
 1441819                Expression.LessThanOrEqual(
 1441820                            GetAppropiateCastExpressionBasedOnType(tc.AttributeType, getAttributeValueExpr, value2),
 1441821                            GetAppropiateTypedValueAndType(value2, tc.AttributeType)));
 1822
 1823
 1824            //and... attribute exists too
 1441825            return Expression.AndAlso(
 1441826                            containsAttributeExpr,
 1441827                            Expression.AndAlso(Expression.NotEqual(getAttributeValueExpr, Expression.Constant(null)),
 1441828                                exp));
 1441829        }
 1830
 1831        protected static Expression TranslateConditionExpressionNull(TypedConditionExpression tc, Expression getAttribut
 1791832        {
 1791833            var c = tc.CondExpression;
 1834
 1791835            return Expression.Or(Expression.AndAlso(
 1791836                                    containsAttributeExpr,
 1791837                                    Expression.Equal(
 1791838                                    getAttributeValueExpr,
 1791839                                    Expression.Constant(null))),   //Attribute is null
 1791840                                 Expression.AndAlso(
 1791841                                    Expression.Not(containsAttributeExpr),
 1791842                                    Expression.Constant(true)));   //Or attribute is not defined (null)
 1791843        }
 1844
 1845        protected static Expression TranslateConditionExpressionOlderThan(TypedConditionExpression tc, Expression getAtt
 381846        {
 381847            var lessThanExpression = Expression.LessThan(
 381848                            GetAppropiateCastExpressionBasedOnType(tc.AttributeType, getAttributeValueExpr, olderThanDat
 381849                            GetAppropiateTypedValueAndType(olderThanDate, tc.AttributeType));
 1850
 381851            return Expression.AndAlso(containsAttributeExpr,
 381852                            Expression.AndAlso(Expression.NotEqual(getAttributeValueExpr, Expression.Constant(null)),
 381853                                lessThanExpression));
 381854        }
 1855
 1856        protected static Expression TranslateConditionExpressionEndsWith(TypedConditionExpression tc, Expression getAttr
 181857        {
 181858            var c = tc.CondExpression;
 1859
 1860            //Append a ´%´at the end of each condition value
 361861            var computedCondition = new ConditionExpression(c.AttributeName, c.Operator, c.Values.Select(x => "%" + x.To
 181862            var typedComputedCondition = new TypedConditionExpression(computedCondition);
 181863            typedComputedCondition.AttributeType = tc.AttributeType;
 1864
 181865            return TranslateConditionExpressionLike(typedComputedCondition, getAttributeValueExpr, containsAttributeExpr
 181866        }
 1867
 1868        protected static Expression GetToStringExpression<T>(Expression e)
 521869        {
 521870            return Expression.Call(e, typeof(T).GetMethod("ToString", new Type[] { }));
 521871        }
 1872        protected static Expression GetCaseInsensitiveExpression(Expression e)
 26791873        {
 26791874            return Expression.Call(e,
 26791875                                typeof(string).GetMethod("ToLowerInvariant", new Type[] { }));
 26791876        }
 1877
 1878        protected static Expression TranslateConditionExpressionLike(TypedConditionExpression tc, Expression getAttribut
 1201879        {
 1201880            var c = tc.CondExpression;
 1881
 1201882            BinaryExpression expOrValues = Expression.Or(Expression.Constant(false), Expression.Constant(false));
 1201883            Expression convertedValueToStr = Expression.Convert(GetAppropiateCastExpressionBasedOnType(tc.AttributeType,
 1884
 1201885            Expression convertedValueToStrAndToLower = GetCaseInsensitiveExpression(convertedValueToStr);
 1886
 1201887            string sLikeOperator = "%";
 6001888            foreach (object value in c.Values)
 1201889            {
 1201890                var strValue = value.ToString();
 1201891                string sMethod = "";
 1892
 1201893                 if (strValue.EndsWith(sLikeOperator) && strValue.StartsWith(sLikeOperator))
 361894                    sMethod = "Contains";
 1895
 841896                 else if (strValue.StartsWith(sLikeOperator))
 181897                    sMethod = "EndsWith";
 1898
 1899                else
 661900                    sMethod = "StartsWith";
 1901
 1201902                expOrValues = Expression.Or(expOrValues, Expression.Call(
 1201903                    convertedValueToStrAndToLower,
 1201904                    typeof(string).GetMethod(sMethod, new Type[] { typeof(string) }),
 1201905                    Expression.Constant(value.ToString().ToLowerInvariant().Replace("%", "")) //Linq2CRM adds the percen
 1201906                ));
 1201907            }
 1908
 1201909            return Expression.AndAlso(
 1201910                            containsAttributeExpr,
 1201911                            expOrValues);
 1201912        }
 1913
 1914        protected static Expression TranslateConditionExpressionContains(TypedConditionExpression tc, Expression getAttr
 241915        {
 241916            var c = tc.CondExpression;
 1917
 1918            //Append a ´%´at the end of each condition value
 481919            var computedCondition = new ConditionExpression(c.AttributeName, c.Operator, c.Values.Select(x => "%" + x.To
 241920            var computedTypedCondition = new TypedConditionExpression(computedCondition);
 241921            computedTypedCondition.AttributeType = tc.AttributeType;
 1922
 241923            return TranslateConditionExpressionLike(computedTypedCondition, getAttributeValueExpr, containsAttributeExpr
 1924
 241925        }
 1926
 1927        protected static BinaryExpression TranslateMultipleConditionExpressions(QueryExpression qe, XrmFakedContext cont
 23221928        {
 23221929            BinaryExpression binaryExpression = null;  //Default initialisation depending on logical operator
 23221930             if (op == LogicalOperator.And)
 22861931                binaryExpression = Expression.And(Expression.Constant(true), Expression.Constant(true));
 1932            else
 361933                binaryExpression = Expression.Or(Expression.Constant(false), Expression.Constant(false));
 1934
 129871935            foreach (var c in conditions)
 30271936            {
 30271937                var cEntityName = sEntityName;
 1938                //Create a new typed expression
 30271939                var typedExpression = new TypedConditionExpression(c);
 30271940                typedExpression.IsOuter = bIsOuter;
 1941
 30271942                string sAttributeName = c.AttributeName;
 1943
 1944                //Find the attribute type if using early bound entities
 30271945                 if (context.ProxyTypesAssembly != null)
 23711946                {
 1947
 1948#if FAKE_XRM_EASY_2013 || FAKE_XRM_EASY_2015 || FAKE_XRM_EASY_2016 || FAKE_XRM_EASY_365 || FAKE_XRM_EASY_9
 19951949                     if (c.EntityName != null)
 751950                        cEntityName = qe.GetEntityNameFromAlias(c.EntityName);
 1951                    else
 19201952                    {
 19201953                         if (c.AttributeName.IndexOf(".") >= 0)
 3051954                        {
 3051955                            var alias = c.AttributeName.Split('.')[0];
 3051956                            cEntityName = qe.GetEntityNameFromAlias(alias);
 3051957                            sAttributeName = c.AttributeName.Split('.')[1];
 3051958                        }
 19201959                    }
 1960
 1961#else
 1962                    //CRM 2011
 4481963                     if (c.AttributeName.IndexOf(".") >= 0) {
 721964                        var alias = c.AttributeName.Split('.')[0];
 721965                        cEntityName = qe.GetEntityNameFromAlias(alias);
 721966                        sAttributeName = c.AttributeName.Split('.')[1];
 721967                    }
 1968#endif
 1969
 23711970                    var earlyBoundType = context.FindReflectedType(cEntityName);
 23711971                     if (earlyBoundType != null)
 23651972                    {
 23651973                        typedExpression.AttributeType = context.FindReflectedAttributeType(earlyBoundType, cEntityName, 
 1974
 1975                        // Special case when filtering on the name of a Lookup
 23591976                         if (typedExpression.AttributeType == typeof(EntityReference) && sAttributeName.EndsWith("name"))
 61977                        {
 61978                            var realAttributeName = c.AttributeName.Substring(0, c.AttributeName.Length - 4);
 1979
 61980                             if (GetEarlyBoundTypeAttribute(earlyBoundType, sAttributeName) == null && GetEarlyBoundTypeA
 61981                            {
 1982                                // Need to make Lookups work against the real attribute, not the "name" suffixed attribu
 61983                                c.AttributeName = realAttributeName;
 61984                            }
 61985                        }
 23591986                    }
 23651987                }
 1988
 30211989                ValidateSupportedTypedExpression(typedExpression);
 1990
 1991                //Build a binary expression
 30201992                 if (op == LogicalOperator.And)
 29541993                {
 29541994                    binaryExpression = Expression.And(binaryExpression, TranslateConditionExpression(qe, context, typedE
 29281995                }
 1996                else
 661997                    binaryExpression = Expression.Or(binaryExpression, TranslateConditionExpression(qe, context, typedEx
 29941998            }
 1999
 22892000            return binaryExpression;
 22892001        }
 2002
 2003        protected static BinaryExpression TranslateMultipleFilterExpressions(QueryExpression qe, XrmFakedContext context
 2402004        {
 2402005            BinaryExpression binaryExpression = null;
 2402006             if (op == LogicalOperator.And)
 2162007                binaryExpression = Expression.And(Expression.Constant(true), Expression.Constant(true));
 2008            else
 242009                binaryExpression = Expression.Or(Expression.Constant(false), Expression.Constant(false));
 2010
 12602011            foreach (var f in filters)
 2702012            {
 2702013                var thisFilterLambda = TranslateFilterExpressionToExpression(qe, context, sEntityName, f, entity, bIsOut
 2014
 2015                //Build a binary expression
 2702016                 if (op == LogicalOperator.And)
 2282017                {
 2282018                    binaryExpression = Expression.And(binaryExpression, thisFilterLambda);
 2282019                }
 2020                else
 422021                    binaryExpression = Expression.Or(binaryExpression, thisFilterLambda);
 2702022            }
 2023
 2402024            return binaryExpression;
 2402025        }
 2026
 2027        protected static List<Expression> TranslateLinkedEntityFilterExpressionToExpression(QueryExpression qe, XrmFaked
 15812028        {
 2029            //In CRM 2011, condition expressions are at the LinkEntity level without an entity name
 2030            //From CRM 2013, condition expressions were moved to outside the LinkEntity object at the QueryExpression le
 2031            //with an EntityName alias attribute
 2032
 2033            //If we reach this point, it means we are translating filters at the Link Entity level (2011),
 2034            //Therefore we need to prepend the alias attribute because the code to generate attributes for Joins (JoinAt
 15812035            var linkedEntitiesQueryExpressions = new List<Expression>();
 2036
 15812037             if (le.LinkCriteria != null)
 14822038            {
 14822039                var earlyBoundType = context.FindReflectedType(le.LinkToEntityName);
 14822040                 var attributeMetadata = context.AttributeMetadataNames.ContainsKey(le.LinkToEntityName) ? context.Attrib
 2041
 54042042                foreach (var ce in le.LinkCriteria.Conditions)
 4792043                {
 4792044                     if (earlyBoundType != null)
 3672045                    {
 3672046                        var attributeInfo = GetEarlyBoundTypeAttribute(earlyBoundType, ce.AttributeName);
 3672047                         if (attributeInfo == null && ce.AttributeName.EndsWith("name"))
 02048                        {
 2049                            // Special case for referencing the name of a EntityReference
 02050                            var sAttributeName = ce.AttributeName.Substring(0, ce.AttributeName.Length - 4);
 02051                            attributeInfo = GetEarlyBoundTypeAttribute(earlyBoundType, sAttributeName);
 2052
 02053                             if (attributeInfo.PropertyType == typeof(EntityReference))
 02054                            {
 2055                                // Don't mess up if other attributes follow this naming pattern
 02056                                ce.AttributeName = sAttributeName;
 02057                            }
 02058                        }
 3672059                    }
 1122060                     else if (attributeMetadata != null && !attributeMetadata.ContainsKey(ce.AttributeName) && ce.Attribu
 02061                    {
 2062                        // Special case for referencing the name of a EntityReference
 02063                        var sAttributeName = ce.AttributeName.Substring(0, ce.AttributeName.Length - 4);
 02064                         if (attributeMetadata.ContainsKey(sAttributeName))
 02065                        {
 02066                            ce.AttributeName = sAttributeName;
 02067                        }
 02068                    }
 2069
 4792070                     var entityAlias = !string.IsNullOrEmpty(le.EntityAlias) ? le.EntityAlias : le.LinkToEntityName;
 4792071                    ce.AttributeName = entityAlias + "." + ce.AttributeName;
 4792072                }
 2073
 44902074                foreach (var fe in le.LinkCriteria.Filters)
 222075                {
 862076                    foreach (var ce in fe.Conditions)
 102077                    {
 102078                         var entityAlias = !string.IsNullOrEmpty(le.EntityAlias) ? le.EntityAlias : le.LinkToEntityName;
 102079                        ce.AttributeName = entityAlias + "." + ce.AttributeName;
 102080                    }
 222081                }
 14822082            }
 2083
 2084            //Translate this specific Link Criteria
 15812085            linkedEntitiesQueryExpressions.Add(TranslateFilterExpressionToExpression(qe, context, le.LinkToEntityName, l
 2086
 2087            //Processed nested linked entities
 49712088            foreach (var nestedLinkedEntity in le.LinkEntities)
 1142089            {
 1142090                var listOfExpressions = TranslateLinkedEntityFilterExpressionToExpression(qe, context, nestedLinkedEntit
 1142091                linkedEntitiesQueryExpressions.AddRange(listOfExpressions);
 1142092            }
 2093
 15812094            return linkedEntitiesQueryExpressions;
 15812095        }
 2096
 2097        protected static Expression TranslateQueryExpressionFiltersToExpression(XrmFakedContext context, QueryExpression
 29212098        {
 29212099            var linkedEntitiesQueryExpressions = new List<Expression>();
 116972100            foreach (var le in qe.LinkEntities)
 14672101            {
 14672102                var listOfExpressions = TranslateLinkedEntityFilterExpressionToExpression(qe, context, le, entity);
 14672103                linkedEntitiesQueryExpressions.AddRange(listOfExpressions);
 14672104            }
 2105
 29212106             if (linkedEntitiesQueryExpressions.Count > 0 && qe.Criteria != null)
 8492107            {
 2108                //Return the and of the two
 8492109                Expression andExpression = Expression.Constant(true);
 56252110                foreach (var e in linkedEntitiesQueryExpressions)
 15392111                {
 15392112                    andExpression = Expression.And(e, andExpression);
 2113
 15392114                }
 8492115                var feExpression = TranslateFilterExpressionToExpression(qe, context, qe.EntityName, qe.Criteria, entity
 8492116                return Expression.And(andExpression, feExpression);
 2117            }
 20722118             else if (linkedEntitiesQueryExpressions.Count > 0)
 242119            {
 2120                //Linked entity expressions only
 242121                Expression andExpression = Expression.Constant(true);
 1562122                foreach (var e in linkedEntitiesQueryExpressions)
 422123                {
 422124                    andExpression = Expression.And(e, andExpression);
 2125
 422126                }
 242127                return andExpression;
 2128            }
 2129            else
 20482130            {
 2131                //Criteria only
 20482132                return TranslateFilterExpressionToExpression(qe, context, qe.EntityName, qe.Criteria, entity, false);
 2133            }
 28882134        }
 2135        protected static Expression TranslateFilterExpressionToExpression(QueryExpression qe, XrmFakedContext context, s
 47482136        {
 50272137             if (fe == null) return Expression.Constant(true);
 2138
 44692139            BinaryExpression conditionsLambda = null;
 44692140            BinaryExpression filtersLambda = null;
 44692141             if (fe.Conditions != null && fe.Conditions.Count > 0)
 23222142            {
 23222143                conditionsLambda = TranslateMultipleConditionExpressions(qe, context, sEntityName, fe.Conditions.ToList(
 22892144            }
 2145
 2146            //Process nested filters recursively
 44362147             if (fe.Filters != null && fe.Filters.Count > 0)
 2402148            {
 2402149                filtersLambda = TranslateMultipleFilterExpressions(qe, context, sEntityName, fe.Filters.ToList(), fe.Fil
 2402150            }
 2151
 44362152             if (conditionsLambda != null && filtersLambda != null)
 422153            {
 2154                //Satisfy both
 422155                 if (fe.FilterOperator == LogicalOperator.And)
 362156                {
 362157                    return Expression.And(conditionsLambda, filtersLambda);
 2158                }
 2159                else
 62160                {
 62161                    return Expression.Or(conditionsLambda, filtersLambda);
 2162                }
 2163            }
 43942164             else if (conditionsLambda != null)
 22472165                return conditionsLambda;
 21472166             else if (filtersLambda != null)
 1982167                return filtersLambda;
 2168
 19492169            return Expression.Constant(true); //Satisfy filter if there are no conditions nor filters
 47152170        }
 2171        protected static Expression TranslateConditionExpressionNext(TypedConditionExpression tc, Expression getAttribut
 362172        {
 362173            var c = tc.CondExpression;
 2174
 362175            var nextDateTime = default(DateTime);
 362176            var currentDateTime = DateTime.UtcNow;
 362177             switch (c.Operator)
 2178            {
 2179                case ConditionOperator.NextXHours:
 62180                    nextDateTime = currentDateTime.AddHours((int)c.Values[0]);
 62181                    break;
 2182                case ConditionOperator.NextXDays:
 62183                    nextDateTime = currentDateTime.AddDays((int)c.Values[0]);
 62184                    break;
 2185                case ConditionOperator.Next7Days:
 62186                    nextDateTime = currentDateTime.AddDays(7);
 62187                    break;
 2188                case ConditionOperator.NextXWeeks:
 62189                    nextDateTime = currentDateTime.AddDays(7 * (int)c.Values[0]);
 62190                    break;
 2191                case ConditionOperator.NextXMonths:
 62192                    nextDateTime = currentDateTime.AddMonths((int)c.Values[0]);
 62193                    break;
 2194                case ConditionOperator.NextXYears:
 62195                    nextDateTime = currentDateTime.AddYears((int)c.Values[0]);
 62196                    break;
 2197            }
 2198
 362199            c.Values.Clear();
 362200            c.Values.Add(currentDateTime);
 362201            c.Values.Add(nextDateTime);
 2202
 2203
 362204            return TranslateConditionExpressionBetween(tc, getAttributeValueExpr, containsAttributeExpr);
 362205        }
 2206
 2207#if FAKE_XRM_EASY_9
 2208        protected static Expression TranslateConditionExpressionContainValues(TypedConditionExpression tc, Expression ge
 142209        {
 142210            var leftHandSideExpression = GetAppropiateCastExpressionBasedOnType(tc.AttributeType, getAttributeValueExpr,
 142211            var rightHandSideExpression = Expression.Constant(ConvertToHashSetOfInt(tc.CondExpression.Values, isOptionSe
 2212
 122213            return Expression.AndAlso(
 122214                       containsAttributeExpr,
 122215                       Expression.AndAlso(
 122216                           Expression.NotEqual(getAttributeValueExpr, Expression.Constant(null)),
 122217                           Expression.Equal(
 122218                               Expression.Call(leftHandSideExpression, typeof(HashSet<int>).GetMethod("Overlaps"), right
 122219                               Expression.Constant(true))));
 122220        }
 2221#endif
 2222    }
 2223}