Summary

Class:FakeXrmEasy.XrmFakedContext
Assembly:FakeXrmEasy
File(s):F:\Git\fake-xrm-easy\FakeXrmEasy.Shared\XrmFakedContext.Aggregations.cs
F:\Git\fake-xrm-easy\FakeXrmEasy.Shared\XrmFakedContext.CodeActivities.cs
F:\Git\fake-xrm-easy\FakeXrmEasy.Shared\XrmFakedContext.Crud.cs
F:\Git\fake-xrm-easy\FakeXrmEasy.Shared\XrmFakedContext.cs
F:\Git\fake-xrm-easy\FakeXrmEasy.Shared\XrmFakedContext.DateTime.cs
F:\Git\fake-xrm-easy\FakeXrmEasy.Shared\XrmFakedContext.Metadata.cs
F:\Git\fake-xrm-easy\FakeXrmEasy.Shared\XrmFakedContext.Pipeline.cs
F:\Git\fake-xrm-easy\FakeXrmEasy.Shared\XrmFakedContext.Plugins.cs
F:\Git\fake-xrm-easy\FakeXrmEasy.Shared\XrmFakedContext.Queries.cs
Covered lines:2575
Uncovered lines:315
Coverable lines:2890
Total lines:4733
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(...)573.0857.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)

F:\Git\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}

F:\Git\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                });
 85
 3686                debugText += "Adding extensions...ok." + Environment.NewLine;
 3687                debugText += "Invoking activity..." + Environment.NewLine;
 88
 3689                 if (inputs == null)
 090                {
 091                    inputs = new Dictionary<string, object>();
 092                }
 93
 3694                return invoker.Invoke(inputs);
 95            }
 096            catch (TypeLoadException exception)
 097            {
 098                 var typeName = exception.TypeName != null ? exception.TypeName : "(null)";
 099                throw new TypeLoadException($"When loading type: {typeName}.{exception.Message}in domain directory: {App
 100            }
 36101        }
 102    }
 103}

F:\Git\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
 9722619        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)
 319792        {
 319793            A.CallTo(() => fakedService.Retrieve(A<string>._, A<Guid>._, A<ColumnSet>._))
 319794                .ReturnsLazily((string entityName, Guid id, ColumnSet columnSet) =>
 387995                {
 387996                    RetrieveRequest retrieveRequest = new RetrieveRequest()
 387997                    {
 387998                        Target = new EntityReference() { LogicalName = entityName, Id = id },
 387999                        ColumnSet = columnSet
 3879100                    };
 3879101                    var executor = context.FakeMessageExecutors[typeof(RetrieveRequest)];
 3197102
 3879103                    RetrieveResponse retrieveResponse = (RetrieveResponse)executor.Execute(retrieveRequest, context);
 3197104
 3813105                    return retrieveResponse.Entity;
 3813106                });
 3197107        }
 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)
 3197114        {
 3197115            A.CallTo(() => fakedService.Create(A<Entity>._))
 3197116                .ReturnsLazily((Entity e) =>
 4461117                {
 4461118                    return context.CreateEntity(e);
 4401119                });
 3197120        }
 121
 122        protected static void FakeUpdate(XrmFakedContext context, IOrganizationService fakedService)
 3197123        {
 3197124            A.CallTo(() => fakedService.Update(A<Entity>._))
 3197125                .Invokes((Entity e) =>
 3533126                {
 3533127                    context.UpdateEntity(e);
 3497128                });
 3197129        }
 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)
 3197225        {
 3197226            A.CallTo(() => fakedService.Delete(A<string>._, A<Guid>._))
 3197227                .Invokes((string entityName, Guid id) =>
 3341228                {
 3341229                     if (string.IsNullOrWhiteSpace(entityName))
 3215230                    {
 3215231                        throw new InvalidOperationException("The entity logical name must not be null or empty.");
 3197232                    }
 3197233
 3323234                     if (id == Guid.Empty)
 3203235                    {
 3203236                        throw new InvalidOperationException("The id must not be empty.");
 3197237                    }
 3197238
 3317239                    var entityReference = new EntityReference(entityName, id);
 3197240
 3317241                    context.DeleteEntity(entityReference);
 3299242                });
 3197243        }
 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)
 6113290        {
 6593291             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
 5657297             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            //};
 6113310        }
 311
 312        protected void AddEntityDefaultAttributes(Entity e)
 28482313        {
 314            // Add createdon, modifiedon, createdby, modifiedby properties
 28482315             if (CallerId == null)
 2974316            {
 2974317                CallerId = new EntityReference("systemuser", Guid.NewGuid()); // Create a new instance by default
 2974318                 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
 2974330            }
 331
 28482332            var isManyToManyRelationshipEntity = e.LogicalName != null && this.Relationships.ContainsKey(e.LogicalName);
 333
 28482334            EntityInitializerService.Initialize(e, CallerId.Id, this, isManyToManyRelationshipEntity);
 28482335        }
 336
 337        protected void ValidateEntity(Entity e)
 29770338        {
 29770339             if (e == null)
 0340            {
 0341                throw new InvalidOperationException("The entity must not be null");
 342            }
 343
 344            // Validate the entity
 29770345             if (string.IsNullOrWhiteSpace(e.LogicalName))
 12346            {
 12347                throw new InvalidOperationException("The LogicalName property must not be empty");
 348            }
 349
 29758350             if (e.Id == Guid.Empty)
 6351            {
 6352                throw new InvalidOperationException("The Id property must not be empty");
 353            }
 29752354        }
 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)
 28482429        {
 430            // Create the entity with defaults
 28482431            AddEntityDefaultAttributes(e);
 432
 28482433             if (usePluginPipeline)
 42434            {
 42435                ExecutePipelineStage("Create", ProcessingStepStage.Preoperation, ProcessingStepMode.Synchronous, e);
 42436            }
 437
 438            // Store
 28482439             AddEntity(clone ? e.Clone(e.GetType()) : e);
 440
 28458441             if (usePluginPipeline)
 42442            {
 42443                ExecutePipelineStage("Create", ProcessingStepStage.Postoperation, ProcessingStepMode.Synchronous, e);
 42444                ExecutePipelineStage("Create", ProcessingStepStage.Postoperation, ProcessingStepMode.Asynchronous, e);
 42445            }
 28458446        }
 447
 448        protected internal void AddEntity(Entity e)
 28482449        {
 450            //Automatically detect proxy types assembly if an early bound type was used.
 28482451             if (ProxyTypesAssembly == null &&
 28482452                e.GetType().IsSubclassOf(typeof(Entity)))
 1438453            {
 1438454                ProxyTypesAssembly = Assembly.GetAssembly(e.GetType());
 1438455            }
 456
 28482457            ValidateEntity(e); //Entity must have a logical name and an Id
 458
 531990459            foreach (var sAttributeName in e.Attributes.Keys.ToList())
 223293460            {
 223293461                var attribute = e[sAttributeName];
 223293462                 if (attribute is DateTime)
 57940463                {
 57940464                    e[sAttributeName] = ConvertToUtc((DateTime)e[sAttributeName]);
 57940465                }
 223293466                 if (attribute is EntityReference && ValidateReferences)
 174467                {
 174468                    var target = (EntityReference)e[sAttributeName];
 174469                    e[sAttributeName] = ResolveEntityReference(target);
 168470                }
 223287471            }
 472
 473            //Add the entity collection
 28464474             if (!Data.ContainsKey(e.LogicalName))
 4909475            {
 4909476                Data.Add(e.LogicalName, new Dictionary<Guid, Entity>());
 4909477            }
 478
 28464479             if (Data[e.LogicalName].ContainsKey(e.Id))
 6480            {
 6481                Data[e.LogicalName][e.Id] = e;
 6482            }
 483            else
 28458484            {
 28458485                Data[e.LogicalName].Add(e.Id, e);
 28458486            }
 487
 488            //Update metadata for that entity
 28464489             if (!AttributeMetadataNames.ContainsKey(e.LogicalName))
 4909490                AttributeMetadataNames.Add(e.LogicalName, new Dictionary<string, string>());
 491
 492            //Update attribute metadata
 28464493             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
 22060511            {
 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
 407914514                foreach (var attKey in e.Attributes.Keys)
 170867515                {
 170867516                     if (!AttributeMetadataNames[e.LogicalName].ContainsKey(attKey))
 15741517                        AttributeMetadataNames[e.LogicalName].Add(attKey, attKey);
 170867518                }
 22060519            }
 520
 28458521        }
 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()
 488516541                        .Where(pi => pi.GetCustomAttributes(typeof(AttributeLogicalNameAttribute), true).Length > 0)
 485384542                        .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)
 58263579        {
 58263580            return DateTime.SpecifyKind(attribute, DateTimeKind.Utc);
 58263581        }
 582        #endregion
 583    }
 584}

F:\Git\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    {
 1179523        protected internal IOrganizationService Service { get; set; }
 24
 25        private IServiceEndpointNotificationService _serviceEndpointNotificationService;
 26
 445727        private readonly Lazy<XrmFakedTracingService> _tracingService = new Lazy<XrmFakedTracingService>(() => new XrmFa
 28
 29        /// <summary>
 30        /// All proxy type assemblies available on mocked database.
 31        /// </summary>
 14131732        private List<Assembly> ProxyTypesAssemblies { get; set; }
 33
 27134        protected internal XrmFakedTracingService TracingService => _tracingService.Value;
 35
 532036        protected internal bool Initialised { get; set; }
 37
 11446638        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
 10274951            {
 52                // TODO What we should do when ProxyTypesAssemblies contains multiple assemblies? One shouldn't throw ex
 10274953                return ProxyTypesAssemblies.FirstOrDefault();
 10274954            }
 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>
 6072269        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>
 627478        private Dictionary<Type, ServiceRequestExecution> ExecutionMocks { get; set; }
 79
 1109680        private Dictionary<Type, IFakeMessageExecutor> FakeMessageExecutors { get; set; }
 81
 433082        private Dictionary<string, IFakeMessageExecutor> GenericFakeMessageExecutors { get; set; }
 83
 4601184        private Dictionary<string, XrmFakedRelationship> Relationships { get; set; }
 85
 86
 3275287        public IEntityInitializerService EntityInitializerService { get; set; }
 455388        public IAccessRightsRepository AccessRightsRepository { get; set; }
 89
 2029990        public int MaxRetrieveCount { get; set; }
 91
 3275292        public EntityInitializationLevel InitializationLevel { get; set; }
 93
 426494        public XrmFakedContext()
 426495        {
 426496            MaxRetrieveCount = 5000;
 97
 426498            AttributeMetadataNames = new Dictionary<string, Dictionary<string, string>>();
 426499            Data = new Dictionary<string, Dictionary<Guid, Entity>>();
 4264100            ExecutionMocks = new Dictionary<Type, ServiceRequestExecution>();
 4264101            OptionSetValuesMetadata = new Dictionary<string, OptionSetMetadata>();
 4264102            StatusAttributeMetadata = new Dictionary<string, StatusAttributeMetadata>();
 103
 4264104            FakeMessageExecutors = Assembly.GetExecutingAssembly()
 4264105                .GetTypes()
 872584106                .Where(t => t.GetInterfaces().Contains(typeof(IFakeMessageExecutor)))
 202116107                .Select(t => Activator.CreateInstance(t) as IFakeMessageExecutor)
 399968108                .ToDictionary(t => t.GetResponsibleRequestType(), t => t);
 109
 4264110            GenericFakeMessageExecutors = new Dictionary<string, IFakeMessageExecutor>();
 111
 4264112            Relationships = new Dictionary<string, XrmFakedRelationship>();
 113
 4264114            EntityInitializerService = new DefaultEntityInitializerService();
 115
 4264116            AccessRightsRepository = new AccessRightsRepository();
 117
 4264118            SystemTimeZone = TimeZoneInfo.Local;
 4264119            DateBehaviour = DefaultDateBehaviour();
 120
 4264121            EntityMetadata = new Dictionary<string, EntityMetadata>();
 122
 4264123            UsePipelineSimulation = false;
 124
 4264125            InitializationLevel = EntityInitializationLevel.Default;
 126
 4264127            ProxyTypesAssemblies = new List<Assembly>();
 4264128        }
 129
 130        /// <summary>
 131        /// Initializes the context with the provided entities
 132        /// </summary>
 133        /// <param name="entities"></param>
 134        public virtual void Initialize(IEnumerable<Entity> entities)
 2672135        {
 2672136             if (Initialised)
 6137            {
 6138                throw new Exception("Initialize should be called only once per unit test execution and XrmFakedContext i
 139            }
 140
 2666141             if (entities == null)
 6142            {
 6143                throw new InvalidOperationException("The entities parameter must be not null");
 144            }
 145
 61756146            foreach (var e in entities)
 26894147            {
 26894148                AddEntityWithDefaults(e, true);
 26882149            }
 150
 2648151            Initialised = true;
 2648152        }
 153
 154        public void Initialize(Entity e)
 111155        {
 111156            this.Initialize(new List<Entity>() { e });
 111157        }
 158
 159        /// <summary>
 160        /// Enables support for the early-cound types exposed in a specified assembly.
 161        /// </summary>
 162        /// <param name="assembly">
 163        /// An assembly containing early-bound entity types.
 164        /// </param>
 165        /// <remarks>
 166        /// See issue #334 on GitHub. This has quite similar idea as is on SDK method
 167        /// https://docs.microsoft.com/en-us/dotnet/api/microsoft.xrm.sdk.client.organizationserviceproxy.enableproxytyp
 168        /// </remarks>
 169        public void EnableProxyTypes(Assembly assembly)
 24170        {
 24171             if (assembly == null)
 0172            {
 0173                throw new ArgumentNullException(nameof(assembly));
 174            }
 175
 24176             if (ProxyTypesAssemblies.Contains(assembly))
 6177            {
 6178                throw new InvalidOperationException($"Proxy types assembly { assembly.GetName().Name } is already enable
 179            }
 180
 18181            ProxyTypesAssemblies.Add(assembly);
 18182        }
 183
 184        public void AddExecutionMock<T>(ServiceRequestExecution mock) where T : OrganizationRequest
 18185        {
 18186             if (!ExecutionMocks.ContainsKey(typeof(T)))
 12187                ExecutionMocks.Add(typeof(T), mock);
 188            else
 6189                ExecutionMocks[typeof(T)] = mock;
 18190        }
 191
 192        public void RemoveExecutionMock<T>() where T : OrganizationRequest
 12193        {
 12194            ExecutionMocks.Remove(typeof(T));
 12195        }
 196
 197        public void AddFakeMessageExecutor<T>(IFakeMessageExecutor executor) where T : OrganizationRequest
 30198        {
 30199             if (!FakeMessageExecutors.ContainsKey(typeof(T)))
 0200                FakeMessageExecutors.Add(typeof(T), executor);
 201            else
 30202                FakeMessageExecutors[typeof(T)] = executor;
 30203        }
 204
 205        public void RemoveFakeMessageExecutor<T>() where T : OrganizationRequest
 6206        {
 6207            FakeMessageExecutors.Remove(typeof(T));
 6208        }
 209
 210        public void AddGenericFakeMessageExecutor(string message, IFakeMessageExecutor executor)
 12211        {
 12212             if (!GenericFakeMessageExecutors.ContainsKey(message))
 12213                GenericFakeMessageExecutors.Add(message, executor);
 214            else
 0215                GenericFakeMessageExecutors[message] = executor;
 12216        }
 217
 218        public void RemoveGenericFakeMessageExecutor(string message)
 6219        {
 6220             if (GenericFakeMessageExecutors.ContainsKey(message))
 6221                GenericFakeMessageExecutors.Remove(message);
 6222        }
 223
 224        public void AddRelationship(string schemaname, XrmFakedRelationship relationship)
 198225        {
 198226            Relationships.Add(schemaname, relationship);
 198227        }
 228
 229        public void RemoveRelationship(string schemaname)
 0230        {
 0231            Relationships.Remove(schemaname);
 0232        }
 233
 234        public XrmFakedRelationship GetRelationship(string schemaName)
 294235        {
 294236             if (Relationships.ContainsKey(schemaName))
 282237            {
 282238                return Relationships[schemaName];
 239            }
 240
 12241            return null;
 294242        }
 243
 244        public void AddAttributeMapping(string sourceEntityName, string sourceAttributeName, string targetEntityName, st
 18245        {
 18246             if (string.IsNullOrWhiteSpace(sourceEntityName))
 0247                throw new ArgumentNullException("sourceEntityName");
 18248             if (string.IsNullOrWhiteSpace(sourceAttributeName))
 0249                throw new ArgumentNullException("sourceAttributeName");
 18250             if (string.IsNullOrWhiteSpace(targetEntityName))
 0251                throw new ArgumentNullException("targetEntityName");
 18252             if (string.IsNullOrWhiteSpace(targetAttributeName))
 0253                throw new ArgumentNullException("targetAttributeName");
 254
 18255            var entityMap = new Entity
 18256            {
 18257                LogicalName = "entitymap",
 18258                Id = Guid.NewGuid(),
 18259                ["targetentityname"] = targetEntityName,
 18260                ["sourceentityname"] = sourceEntityName
 18261            };
 262
 18263            var attributeMap = new Entity
 18264            {
 18265                LogicalName = "attributemap",
 18266                Id = Guid.NewGuid(),
 18267                ["entitymapid"] = new EntityReference("entitymap", entityMap.Id),
 18268                ["targetattributename"] = targetAttributeName,
 18269                ["sourceattributename"] = sourceAttributeName
 18270            };
 271
 18272            AddEntityWithDefaults(entityMap);
 18273            AddEntityWithDefaults(attributeMap);
 18274        }
 275
 276        public virtual IOrganizationService GetOrganizationService()
 2338277        {
 2338278             if (this is XrmRealContext)
 0279            {
 0280                Service = GetOrganizationService();
 0281                return Service;
 282            }
 2338283            return GetFakedOrganizationService(this);
 2338284        }
 285
 286        /// <summary>
 287        /// Deprecated. Use GetOrganizationService instead
 288        /// </summary>
 289        /// <returns></returns>
 290        [Obsolete("Use GetOrganizationService instead")]
 291        public IOrganizationService GetFakedOrganizationService()
 1691292        {
 1691293            return GetFakedOrganizationService(this);
 1691294        }
 295
 296        protected IOrganizationService GetFakedOrganizationService(XrmFakedContext context)
 4029297        {
 4029298             if (context.Service != null)
 832299            {
 832300                return context.Service;
 301            }
 302
 3197303            var fakedService = A.Fake<IOrganizationService>();
 304
 305            //Fake CRUD methods
 3197306            FakeRetrieve(context, fakedService);
 3197307            FakeCreate(context, fakedService);
 3197308            FakeUpdate(context, fakedService);
 3197309            FakeDelete(context, fakedService);
 310
 311            //Fake / Intercept Retrieve Multiple Requests
 3197312            FakeRetrieveMultiple(context, fakedService);
 313
 314            //Fake / Intercept other requests
 3197315            FakeExecute(context, fakedService);
 3197316            FakeAssociate(context, fakedService);
 3197317            FakeDisassociate(context, fakedService);
 3197318            context.Service = fakedService;
 319
 3197320            return context.Service;
 4029321        }
 322
 323        /// <summary>
 324        /// Fakes the Execute method of the organization service.
 325        /// Not all the OrganizationRequest are going to be implemented, so stay tunned on updates!
 326        /// </summary>
 327        /// <param name="context"></param>
 328        /// <param name="fakedService"></param>
 329        public static void FakeExecute(XrmFakedContext context, IOrganizationService fakedService)
 3197330        {
 3197331            OrganizationResponse response = null;
 3197332            Func<OrganizationRequest, OrganizationResponse> execute = (req) =>
 5147333            {
 5147334                 if (context.ExecutionMocks.ContainsKey(req.GetType()))
 3209335                    return context.ExecutionMocks[req.GetType()].Invoke(req);
 3197336
 5135337                 if (context.FakeMessageExecutors.ContainsKey(req.GetType())
 5135338                    && context.FakeMessageExecutors[req.GetType()].CanExecute(req))
 5117339                    return context.FakeMessageExecutors[req.GetType()].Execute(req, context);
 3197340
 3215341                 if (req.GetType() == typeof(OrganizationRequest)
 3215342                    && context.GenericFakeMessageExecutors.ContainsKey(req.RequestName))
 3209343                    return context.GenericFakeMessageExecutors[req.RequestName].Execute(req, context);
 3197344
 3203345                throw PullRequestException.NotImplementedOrganizationRequest(req.GetType());
 4879346            };
 347
 3197348            A.CallTo(() => fakedService.Execute(A<OrganizationRequest>._))
 5147349                .Invokes((OrganizationRequest req) => response = execute(req))
 4879350                .ReturnsLazily((OrganizationRequest req) => response);
 3197351        }
 352
 353        public static void FakeAssociate(XrmFakedContext context, IOrganizationService fakedService)
 3197354        {
 3197355            A.CallTo(() => fakedService.Associate(A<string>._, A<Guid>._, A<Relationship>._, A<EntityReferenceCollection
 3197356                .Invokes((string entityName, Guid entityId, Relationship relationship, EntityReferenceCollection entityC
 3305357                {
 3305358                     if (context.FakeMessageExecutors.ContainsKey(typeof(AssociateRequest)))
 3305359                    {
 3305360                        var request = new AssociateRequest()
 3305361                        {
 3305362                            Target = new EntityReference() { Id = entityId, LogicalName = entityName },
 3305363                            Relationship = relationship,
 3305364                            RelatedEntities = entityCollection
 3305365                        };
 3305366                        context.FakeMessageExecutors[typeof(AssociateRequest)].Execute(request, context);
 3305367                    }
 3197368                    else
 3197369                        throw PullRequestException.NotImplementedOrganizationRequest(typeof(AssociateRequest));
 3305370                });
 3197371        }
 372
 373        public static void FakeDisassociate(XrmFakedContext context, IOrganizationService fakedService)
 3197374        {
 3197375            A.CallTo(() => fakedService.Disassociate(A<string>._, A<Guid>._, A<Relationship>._, A<EntityReferenceCollect
 3197376                .Invokes((string entityName, Guid entityId, Relationship relationship, EntityReferenceCollection entityC
 3215377                {
 3215378                     if (context.FakeMessageExecutors.ContainsKey(typeof(DisassociateRequest)))
 3215379                    {
 3215380                        var request = new DisassociateRequest()
 3215381                        {
 3215382                            Target = new EntityReference() { Id = entityId, LogicalName = entityName },
 3215383                            Relationship = relationship,
 3215384                            RelatedEntities = entityCollection
 3215385                        };
 3215386                        context.FakeMessageExecutors[typeof(DisassociateRequest)].Execute(request, context);
 3215387                    }
 3197388                    else
 3197389                        throw PullRequestException.NotImplementedOrganizationRequest(typeof(DisassociateRequest));
 3215390                });
 3197391        }
 392
 393        public static void FakeRetrieveMultiple(XrmFakedContext context, IOrganizationService fakedService)
 3197394        {
 3197395            EntityCollection entities = null;
 3197396            Func<QueryBase, EntityCollection> retriveMultiple = (QueryBase req) =>
 4763397            {
 4763398                var request = new RetrieveMultipleRequest { Query = req };
 3197399
 4763400                var executor = new RetrieveMultipleRequestExecutor();
 4763401                var response = executor.Execute(request, context) as RetrieveMultipleResponse;
 3197402
 4731403                return response.EntityCollection;
 4731404            };
 405
 406            //refactored from RetrieveMultipleExecutor
 3197407            A.CallTo(() => fakedService.RetrieveMultiple(A<QueryBase>._))
 4763408                .Invokes((QueryBase req) => entities = retriveMultiple(req))
 4731409                .ReturnsLazily((QueryBase req) => entities);
 3197410        }
 411
 412        public IServiceEndpointNotificationService GetFakedServiceEndpointNotificationService()
 12413        {
 12414            return _serviceEndpointNotificationService ??
 12415                   (_serviceEndpointNotificationService = A.Fake<IServiceEndpointNotificationService>());
 12416        }
 417#if FAKE_XRM_EASY_9
 418        public IEntityDataSourceRetrieverService GetFakedEntityDataSourceRetrieverService()
 1419        {
 1420            var service = A.Fake<IEntityDataSourceRetrieverService>();
 1421            A.CallTo(() => service.RetrieveEntityDataSource())
 2422                .ReturnsLazily(() => EntityDataSourceRetriever);
 1423            return service;
 1424        }
 425#endif
 426    }
 427}

F:\Git\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    {
 42649        public TimeZoneInfo SystemTimeZone { get; set; }
 10
 1211        public FiscalYearSettings FiscalYearSettings { get; set; }
 12
 2769013        public Dictionary<string, Dictionary<string, DateTimeAttributeBehavior>> DateBehaviour { get; set; }
 14
 15        private static Dictionary<string, Dictionary<string, DateTimeAttributeBehavior>> DefaultDateBehaviour()
 426416        {
 17#if FAKE_XRM_EASY || FAKE_XRM_EASY_2013
 136218            return new Dictionary<string, Dictionary<string, DateTimeAttributeBehavior>>();
 19#else
 290220            return new Dictionary<string, Dictionary<string, DateTimeAttributeBehavior>>
 290221            {
 290222                {
 290223                    "contact", new Dictionary<string, DateTimeAttributeBehavior>
 290224                    {
 290225                        { "anniversary", DateTimeAttributeBehavior.DateOnly },
 290226                        { "birthdate", DateTimeAttributeBehavior.DateOnly }
 290227                    }
 290228                },
 290229                {
 290230                    "invoice", new Dictionary<string, DateTimeAttributeBehavior>
 290231                    {
 290232                        { "duedate", DateTimeAttributeBehavior.DateOnly }
 290233                    }
 290234                },
 290235                {
 290236                    "lead", new Dictionary<string, DateTimeAttributeBehavior>
 290237                    {
 290238                        { "estimatedclosedate", DateTimeAttributeBehavior.DateOnly }
 290239                    }
 290240                },
 290241                {
 290242                    "opportunity", new Dictionary<string, DateTimeAttributeBehavior>
 290243                    {
 290244                        { "actualclosedate", DateTimeAttributeBehavior.DateOnly },
 290245                        { "estimatedclosedate", DateTimeAttributeBehavior.DateOnly },
 290246                        { "finaldecisiondate", DateTimeAttributeBehavior.DateOnly }
 290247                    }
 290248                },
 290249                {
 290250                    "product", new Dictionary<string, DateTimeAttributeBehavior>
 290251                    {
 290252                        { "validfromdate", DateTimeAttributeBehavior.DateOnly },
 290253                        { "validtodate", DateTimeAttributeBehavior.DateOnly }
 290254                    }
 290255                },
 290256                {
 290257                    "quote", new Dictionary<string, DateTimeAttributeBehavior>
 290258                    {
 290259                        { "closedon", DateTimeAttributeBehavior.DateOnly },
 290260                        { "dueby", DateTimeAttributeBehavior.DateOnly }
 290261                    }
 290262                }
 290263            };
 64#endif
 426465        }
 66    }
 67}

F:\Git\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>
 259834218        protected internal Dictionary<string, Dictionary<string, string>> AttributeMetadataNames { get; set; }
 19
 20        /// <summary>
 21        /// Stores fake global option set metadata
 22        /// </summary>
 443823        public Dictionary<string, OptionSetMetadata> OptionSetValuesMetadata { get; set; }
 24
 25        /// <summary>
 26        /// Stores fake global status values metadata
 27        /// </summary>
 429428        public Dictionary<string, StatusAttributeMetadata> StatusAttributeMetadata { get; set; }
 29
 30        /// <summary>
 31        /// Stores fake entity metadata
 32        /// </summary>
 18005333        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}

F:\Git\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    {
 641012        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}

F:\Git\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);
 25962            A.CallTo(() => context.InitiatingUserId).ReturnsLazily(() => ctx.InitiatingUserId == Guid.Empty ? newUserId 
 32063            A.CallTo(() => context.UserId).ReturnsLazily(() => ctx.UserId == Guid.Empty ? newUserId : ctx.UserId);
 24164            A.CallTo(() => context.PrimaryEntityName).ReturnsLazily(() => ctx.PrimaryEntityName);
 23565            A.CallTo(() => context.SecondaryEntityName).ReturnsLazily(() => ctx.SecondaryEntityName);
 24166            A.CallTo(() => context.SharedVariables).ReturnsLazily(() => ctx.SharedVariables);
 23567            A.CallTo(() => context.BusinessUnitId).ReturnsLazily(() => ctx.BusinessUnitId);
 23568            A.CallTo(() => context.CorrelationId).ReturnsLazily(() => ctx.CorrelationId);
 23569            A.CallTo(() => context.OperationCreatedOn).ReturnsLazily(() => ctx.OperationCreatedOn);
 23570            A.CallTo(() => context.IsolationMode).ReturnsLazily(() => ctx.IsolationMode);
 23571            A.CallTo(() => context.IsInTransaction).ReturnsLazily(() => ctx.IsInTransaction);
 72
 73
 74            // Create message will pass an Entity as the target but this is not always true
 75            // For instance, a Delete request will receive an EntityReference
 23576             if (ctx.InputParameters != null && ctx.InputParameters.ContainsKey("Target"))
 18677            {
 18678                 if (ctx.InputParameters["Target"] is Entity)
 16879                {
 16880                    var target = (Entity)ctx.InputParameters["Target"];
 16881                    A.CallTo(() => context.PrimaryEntityId).ReturnsLazily(() => target.Id);
 19882                    A.CallTo(() => context.PrimaryEntityName).ReturnsLazily(() => target.LogicalName);
 16883                }
 1884                 else if (ctx.InputParameters["Target"] is EntityReference)
 1885                {
 1886                    var target = (EntityReference)ctx.InputParameters["Target"];
 1887                    A.CallTo(() => context.PrimaryEntityId).ReturnsLazily(() => target.Id);
 1888                    A.CallTo(() => context.PrimaryEntityName).ReturnsLazily(() => target.LogicalName);
 1889                }
 18690            }
 23591        }
 92
 93        protected IExecutionContext GetFakedExecutionContext(XrmFakedPluginExecutionContext ctx)
 094        {
 095            var context = A.Fake<IExecutionContext>();
 96
 097            PopulateExecutionContextPropertiesFromFakedContext(context, ctx);
 98
 099            return context;
 0100        }
 101
 102        /// <summary>
 103        /// Executes a plugin passing a custom context. This is useful whenever we need to mock more complex plugin cont
 104        /// </summary>
 105        /// <typeparam name="T">Must be a plugin</typeparam>
 106        /// <param name="ctx"></param>
 107        /// <returns></returns>
 108        public IPlugin ExecutePluginWith<T>(XrmFakedPluginExecutionContext ctx = null)
 109            where T : IPlugin, new()
 150110        {
 150111             if (ctx == null)
 24112            {
 24113                ctx = GetDefaultPluginContext();
 24114            }
 115
 150116            return this.ExecutePluginWith(ctx, new T());
 150117        }
 118
 119        /// <summary>
 120        /// Executes a plugin passing a custom context. This is useful whenever we need to mock more complex plugin cont
 121        /// </summary>
 122        /// <param name="ctx"></param>
 123        /// <param name="instance"></param>
 124        /// <returns></returns>
 125        public IPlugin ExecutePluginWith(XrmFakedPluginExecutionContext ctx, IPlugin instance)
 211126        {
 211127            var fakedServiceProvider = GetFakedServiceProvider(ctx);
 128
 211129            var fakedPlugin = A.Fake<IPlugin>();
 211130            A.CallTo(() => fakedPlugin.Execute(A<IServiceProvider>._))
 211131                .Invokes((IServiceProvider provider) =>
 422132                {
 422133                    var plugin = instance;
 422134                    plugin.Execute(fakedServiceProvider);
 416135                });
 136
 211137            fakedPlugin.Execute(fakedServiceProvider); //Execute the plugin
 205138            return fakedPlugin;
 205139        }
 140
 141        public IPlugin ExecutePluginWith<T>(ParameterCollection inputParameters, ParameterCollection outputParameters, E
 142            where T : IPlugin, new()
 24143        {
 24144            var ctx = GetDefaultPluginContext();
 24145            ctx.InputParameters.AddRange(inputParameters);
 24146            ctx.OutputParameters.AddRange(outputParameters);
 24147            ctx.PreEntityImages.AddRange(preEntityImages);
 24148            ctx.PostEntityImages.AddRange(postEntityImages);
 149
 24150            var fakedServiceProvider = GetFakedServiceProvider(ctx);
 151
 24152            var fakedPlugin = A.Fake<IPlugin>();
 24153            A.CallTo(() => fakedPlugin.Execute(A<IServiceProvider>._))
 24154                .Invokes((IServiceProvider provider) =>
 48155                {
 48156                    var plugin = new T();
 48157                    plugin.Execute(fakedServiceProvider);
 48158                });
 159
 24160            fakedPlugin.Execute(fakedServiceProvider); //Execute the plugin
 24161            return fakedPlugin;
 24162        }
 163
 164        public IPlugin ExecutePluginWithConfigurations<T>(XrmFakedPluginExecutionContext plugCtx, string unsecureConfigu
 165            where T : class, IPlugin
 31166        {
 31167            var pluginType = typeof(T);
 31168            var constructors = pluginType.GetConstructors().ToList();
 169
 106170             if (!constructors.Any(c => c.GetParameters().Length == 2 && c.GetParameters().All(param => param.ParameterTy
 12171            {
 12172                throw new ArgumentException("The plugin you are trying to execute does not specify a constructor for pas
 173            }
 174
 19175            var pluginInstance = (T)Activator.CreateInstance(typeof(T), unsecureConfiguration, secureConfiguration);
 176
 19177            return this.ExecutePluginWith(plugCtx, pluginInstance);
 19178        }
 179
 180        [Obsolete("Use ExecutePluginWith(XrmFakedPluginExecutionContext ctx, IPlugin instance).")]
 181        public IPlugin ExecutePluginWithConfigurations<T>(XrmFakedPluginExecutionContext plugCtx, T instance, string uns
 182            where T : class, IPlugin
 0183        {
 0184            var fakedServiceProvider = GetFakedServiceProvider(plugCtx);
 185
 0186            var fakedPlugin = A.Fake<IPlugin>();
 187
 0188            A.CallTo(() => fakedPlugin.Execute(A<IServiceProvider>._))
 0189                .Invokes((IServiceProvider provider) =>
 0190                {
 0191                    var pluginType = typeof(T);
 0192                    var constructors = pluginType.GetConstructors();
 0193
 0194                     if (!constructors.Any(c => c.GetParameters().Length == 2 && c.GetParameters().All(param => param.Par
 0195                    {
 0196                        throw new ArgumentException("The plugin you are trying to execute does not specify a constructor
 0197                    }
 0198
 0199                    var plugin = instance;
 0200                    plugin.Execute(fakedServiceProvider);
 0201                });
 202
 0203            fakedPlugin.Execute(fakedServiceProvider); //Execute the plugin
 0204            return fakedPlugin;
 0205        }
 206
 207        public IPlugin ExecutePluginWithTarget<T>(XrmFakedPluginExecutionContext ctx, Entity target, string messageName 
 208          where T : IPlugin, new()
 0209        {
 0210            ctx.InputParameters.Add("Target", target);
 0211            ctx.MessageName = messageName;
 0212            ctx.Stage = stage;
 213
 0214            return this.ExecutePluginWith<T>(ctx);
 0215        }
 216
 217        /// <summary>
 218        /// Executes the plugin of type T against the faked context for an entity target
 219        /// and returns the faked plugin
 220        /// </summary>
 221        /// <typeparam name="T"></typeparam>
 222        /// <param name="target">The entity to execute the plug-in for.</param>
 223        /// <param name="messageName">Sets the message name.</param>
 224        /// <param name="stage">Sets the stage.</param>
 225        /// <returns></returns>
 226        public IPlugin ExecutePluginWithTarget<T>(Entity target, string messageName = "Create", int stage = 40)
 227            where T : IPlugin, new()
 36228        {
 36229            return this.ExecutePluginWithTarget(new T(), target, messageName, stage);
 30230        }
 231
 232        /// <summary>
 233        /// Executes the plugin of type T against the faked context for an entity target
 234        /// and returns the faked plugin
 235        /// </summary>
 236        /// <param name="instance"></param>
 237        /// <param name="target">The entity to execute the plug-in for.</param>
 238        /// <param name="messageName">Sets the message name.</param>
 239        /// <param name="stage">Sets the stage.</param>
 240        /// <returns></returns>
 241        public IPlugin ExecutePluginWithTarget(IPlugin instance, Entity target, string messageName = "Create", int stage
 36242        {
 36243            var ctx = GetDefaultPluginContext();
 244
 245            // Add the target entity to the InputParameters
 36246            ctx.InputParameters.Add("Target", target);
 36247            ctx.MessageName = messageName;
 36248            ctx.Stage = stage;
 249
 36250            return this.ExecutePluginWith(ctx, instance);
 30251        }
 252
 253        /// <summary>
 254        /// Executes the plugin of type T against the faked context for an entity reference target
 255        /// and returns the faked plugin
 256        /// </summary>
 257        /// <typeparam name="T"></typeparam>
 258        /// <param name="target">The entity reference to execute the plug-in for.</param>
 259        /// <param name="messageName">Sets the message name.</param>
 260        /// <param name="stage">Sets the stage.</param>
 261        /// <returns></returns>
 262        public IPlugin ExecutePluginWithTargetReference<T>(EntityReference target, string messageName = "Delete", int st
 263            where T : IPlugin, new()
 0264        {
 0265            return this.ExecutePluginWithTargetReference(new T(), target, messageName, stage);
 0266        }
 267
 268        /// <summary>
 269        /// Executes the plugin of type T against the faked context for an entity reference target
 270        /// and returns the faked plugin
 271        /// </summary>
 272        /// <param name="instance"></param>
 273        /// <param name="target">The entity reference to execute the plug-in for.</param>
 274        /// <param name="messageName">Sets the message name.</param>
 275        /// <param name="stage">Sets the stage.</param>
 276        /// <returns></returns>
 277        public IPlugin ExecutePluginWithTargetReference(IPlugin instance, EntityReference target, string messageName = "
 0278        {
 0279            var ctx = GetDefaultPluginContext();
 280            // Add the target entity to the InputParameters
 0281            ctx.InputParameters.Add("Target", target);
 0282            ctx.MessageName = messageName;
 0283            ctx.Stage = stage;
 284
 0285            return this.ExecutePluginWith(ctx, instance);
 0286        }
 287
 288        /// <summary>
 289        /// Returns a faked plugin with a target and the specified pre entity images
 290        /// </summary>
 291        /// <typeparam name="T"></typeparam>
 292        /// <returns></returns>
 293        [Obsolete]
 294        public IPlugin ExecutePluginWithTargetAndPreEntityImages<T>(object target, EntityImageCollection preEntityImages
 295            where T : IPlugin, new()
 6296        {
 6297            var ctx = GetDefaultPluginContext();
 298            // Add the target entity to the InputParameters
 6299            ctx.InputParameters.Add("Target", target);
 6300            ctx.PreEntityImages.AddRange(preEntityImages);
 6301            ctx.MessageName = messageName;
 6302            ctx.Stage = stage;
 303
 6304            return this.ExecutePluginWith<T>(ctx);
 6305        }
 306
 307        /// <summary>
 308        /// Returns a faked plugin with a target and the specified post entity images
 309        /// </summary>
 310        /// <typeparam name="T"></typeparam>
 311        /// <returns></returns>
 312        [Obsolete]
 313        public IPlugin ExecutePluginWithTargetAndPostEntityImages<T>(object target, EntityImageCollection postEntityImag
 314            where T : IPlugin, new()
 6315        {
 6316            var ctx = GetDefaultPluginContext();
 317            // Add the target entity to the InputParameters
 6318            ctx.InputParameters.Add("Target", target);
 6319            ctx.PostEntityImages.AddRange(postEntityImages);
 6320            ctx.MessageName = messageName;
 6321            ctx.Stage = stage;
 322
 6323            return this.ExecutePluginWith<T>(ctx);
 6324        }
 325
 326        [Obsolete]
 327        public IPlugin ExecutePluginWithTargetAndInputParameters<T>(Entity target, ParameterCollection inputParameters, 
 328            where T : IPlugin, new()
 0329        {
 0330            var ctx = GetDefaultPluginContext();
 331
 0332            ctx.InputParameters.AddRange(inputParameters);
 333
 0334            return this.ExecutePluginWithTarget<T>(ctx, target, messageName, stage);
 0335        }
 336
 337        protected IServiceProvider GetFakedServiceProvider(XrmFakedPluginExecutionContext plugCtx)
 235338        {
 235339            var fakedServiceProvider = A.Fake<IServiceProvider>();
 340
 235341            A.CallTo(() => fakedServiceProvider.GetService(A<Type>._))
 235342               .ReturnsLazily((Type t) =>
 707343               {
 707344                    if (t == typeof(IOrganizationService))
 247345                   {
 235346                       //Return faked or real organization service
 247347                       return GetOrganizationService();
 235348                   }
 235349
 695350                    if (t == typeof(ITracingService))
 386351                   {
 386352                       return TracingService;
 235353                   }
 235354
 544355                    if (t == typeof(IPluginExecutionContext))
 470356                   {
 470357                       return GetFakedPluginContext(plugCtx);
 235358                   }
 235359
 309360                    if (t == typeof(IExecutionContext))
 235361                   {
 235362                       return GetFakedExecutionContext(plugCtx);
 235363                   }
 235364
 309365                    if (t == typeof(IOrganizationServiceFactory))
 302366                   {
 302367                       var fakedServiceFactory = A.Fake<IOrganizationServiceFactory>();
 369368                       A.CallTo(() => fakedServiceFactory.CreateOrganizationService(A<Guid?>._)).ReturnsLazily((Guid? g)
 302369                       return fakedServiceFactory;
 235370                   }
 235371
 242372                    if (t == typeof(IServiceEndpointNotificationService))
 241373                   {
 241374                       return GetFakedServiceEndpointNotificationService();
 235375                   }
 235376#if FAKE_XRM_EASY_9
 236377                    if (t == typeof(IEntityDataSourceRetrieverService))
 236378                   {
 236379                       return GetFakedEntityDataSourceRetrieverService();
 235380                   }
 235381#endif
 235382                   throw new PullRequestException("The specified service type is not supported");
 707383               });
 384
 235385            return fakedServiceProvider;
 235386        }
 387
 388#if FAKE_XRM_EASY_9
 2389        public Entity EntityDataSourceRetriever { get; set; }
 390#endif
 391
 392        public XrmFakedTracingService GetFakeTracingService()
 84393        {
 84394            return TracingService;
 84395        }
 396    }
 397}

F:\Git\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)
 2962023        {
 2962024            var types =
 8449225                ProxyTypesAssemblies.Select(a => FindReflectedType(logicalName, a))
 8449226                                    .Where(t => t != null);
 27
 2962028             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
 2962042            return types.SingleOrDefault();
 2962043        }
 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()
 714909078                        .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"))
 906231            {
 906232                return attributeInfo.PropertyType.GenericTypeArguments[0];
 233            }
 234#endif
 235
 2278236            return attributeInfo.PropertyType;
 3192237        }
 238
 239        private static PropertyInfo GetEarlyBoundTypeAttribute(Type earlyBoundType, string attributeName)
 3589240        {
 3589241            var attributeInfo = earlyBoundType.GetProperties()
 230359242                .Where(pi => pi.GetCustomAttributes(typeof(AttributeLogicalNameAttribute), true).Length > 0)
 224611243                .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