private async Task <Type> BuildNestedTypeAsync(EntityPropertyDto propertyDto, DynamicDtoTypeBuildingContext context)
        {
            if (propertyDto.DataType != DataTypes.Object)
            {
                throw new NotSupportedException($"{nameof(BuildNestedTypeAsync)}: unsupported type of property (expected '{DataTypes.Object}', actual: '{propertyDto.DataType}')");
            }

            // todo: build name of the class according ot the level of the property
            using (context.OpenNamePrefix(propertyDto.Name))
            {
                var className = context.CurrentPrefix.Replace('.', '_');

                var tb = GetTypeBuilder(typeof(object), className, new List <Type> {
                    typeof(IDynamicNestedObject)
                });
                var constructor = tb.DefineDefaultConstructor(MethodAttributes.Public | MethodAttributes.SpecialName | MethodAttributes.RTSpecialName);

                foreach (var property in propertyDto.Properties)
                {
                    //if (propertyFilter == null || propertyFilter.Invoke(property.PropertyName))
                    var propertyType = await GetDtoPropertyTypeAsync(property, context);

                    CreateProperty(tb, property.Name, propertyType);
                }

                var objectType = tb.CreateType();

                context.ClassCreated(objectType);

                return(objectType);
            }
        }
Example #2
0
        // todo: get IsVersioned flag from the EntityPropertyDto
        public async Task SetValueAsync <TId>(IEntity <TId> entity, EntityPropertyDto property, string value, bool createNewVersion)
        {
            var config = entity.GetType().GetEntityConfiguration();

            var prop = _entityPropertyValueRepository.GetAll()
                       .Where(x => x.EntityProperty.Id == property.Id && x.OwnerId == entity.Id.ToString() &&
                              x.OwnerType == config.TypeShortAlias)
                       .OrderByDescending(x => x.CreationTime).FirstOrDefault();

            if (prop?.Value == value)
            {
                return;
            }

            if (createNewVersion || prop == null)
            {
                prop = new EntityPropertyValue()
                {
                    Value = value, EntityProperty = _entityPropertyRepository.Get(property.Id)
                };
                prop.SetOwner(entity);
            }
            else
            {
                prop.Value = value;
            }

            await _entityPropertyValueRepository.InsertOrUpdateAsync(prop);
        }
Example #3
0
        public async Task <string> GetValueAsync <TId>(IEntity <TId> entity, EntityPropertyDto property)
        {
            var config = entity.GetType().GetEntityConfiguration();

            var result = await _entityPropertyValueRepository.GetAll()
                         .Where(x => x.EntityProperty.Id == property.Id && x.OwnerId == entity.Id.ToString() && x.OwnerType == config.TypeShortAlias)
                         .OrderByDescending(x => x.CreationTime).FirstOrDefaultAsync();

            return(result?.Value);
        }
Example #4
0
        /// inheritedDoc
        public string SerializeProperty(EntityPropertyDto propertyDto, object value)
        {
            switch (propertyDto.DataType)
            {
            case DataTypes.Date:
            {
                var settings = new JsonSerializerSettings()
                {
                    Converters = new List <JsonConverter>()
                    {
                        new IsoDateTimeConverter()
                        {
                            DateTimeFormat = "yyyy'-'MM'-'dd"
                        }
                    },
                };
                return(JsonConvert.SerializeObject(value, settings));
            }

            default:
                return(JsonConvert.SerializeObject(value));
            }
        }
        private Type GetEntityReferenceType(EntityPropertyDto propertyDto, DynamicDtoTypeBuildingContext context)
        {
            if (propertyDto.DataType != DataTypes.EntityReference)
            {
                throw new NotSupportedException($"DataType {propertyDto.DataType} is not supported. Expected {DataTypes.EntityReference}");
            }

            if (string.IsNullOrWhiteSpace(propertyDto.EntityType))
            {
                return(null);
            }

            var entityConfig = _entityConfigurationStore.Get(propertyDto.EntityType);

            if (entityConfig == null)
            {
                return(null);
            }

            return(context.UseDtoForEntityReferences
                ? typeof(EntityWithDisplayNameDto <>).MakeGenericType(entityConfig.IdType)
                : entityConfig?.IdType);
        }
        public async Task BuildDynamicDto_Test()
        {
            var entityConfigCacheMock = new Mock <IEntityConfigCache>();

            const string supervisorPropName          = "Supervisor";
            const string supervisorFirstNamePropName = "FirstName";
            const string supervisorLastNamePropName  = "LastName";

            entityConfigCacheMock.Setup(x => x.GetEntityPropertiesAsync(It.IsAny <Type>()))
            .Returns(() => {
                var result = new EntityPropertyDtoList();
                result.AddString("Name", "Name...");
                result.AddString("Description", "Description...");

                var nested = new EntityPropertyDto {
                    Name       = supervisorPropName,
                    DataType   = DataTypes.Object,
                    Properties = new List <EntityPropertyDto>(),
                };
                nested.Properties.Add(new EntityPropertyDto {
                    Name = supervisorFirstNamePropName, DataType = DataTypes.String
                });
                nested.Properties.Add(new EntityPropertyDto {
                    Name = supervisorLastNamePropName, DataType = DataTypes.String
                });

                result.Add(nested);


                var r = result as List <EntityPropertyDto>;
                return(Task.FromResult(r));
            });

            var entityConfigStore = LocalIocManager.Resolve <IEntityConfigurationStore>();
            var cacheManager      = LocalIocManager.Resolve <ICacheManager>();
            var builder           = new DynamicDtoTypeBuilder(entityConfigCacheMock.Object, entityConfigStore, cacheManager);

            var baseDtoType = typeof(DynamicDto <Person, Guid>);

            var context = new DynamicDtoTypeBuildingContext()
            {
                ModelType = baseDtoType,
                // AddFormFieldsProperty = false // note: false is a default value for AddFormFieldsProperty. Leave it blank to test default value
            };
            var proxyType = await builder.BuildDtoFullProxyTypeAsync(baseDtoType, context);

            proxyType.Assembly.IsDynamic.ShouldBeTrue();

            var properties = proxyType.GetProperties();

            properties.ShouldContain(p => p.Name == "Name");
            properties.ShouldContain(p => p.Name == "Description");
            properties.ShouldNotContain(p => p.Name == nameof(IHasFormFieldsList._formFields));
            proxyType.ShouldNotBeAssignableTo(typeof(IHasFormFieldsList));

            var supervisorProp = properties.FirstOrDefault(p => p.Name == supervisorPropName);

            supervisorProp.ShouldNotBeNull($"{supervisorPropName} property is missing in the created DTO");

            // nested object should be dynamic
            supervisorProp.PropertyType.Assembly.IsDynamic.ShouldBeTrue();
            typeof(IDynamicNestedObject).IsAssignableFrom(supervisorProp.PropertyType).ShouldBeTrue($"Dynamic nested object must implement {nameof(IDynamicNestedObject)}");

            var nestedProperties = supervisorProp.PropertyType.GetProperties();

            nestedProperties.ShouldContain(p => p.Name == supervisorFirstNamePropName);
            nestedProperties.ShouldContain(p => p.Name == supervisorLastNamePropName);
        }
        /// <summary>
        /// Returns .Net type that is used to store data for the specified DTO property (according to the property settings)
        /// </summary>
        public async Task <Type> GetDtoPropertyTypeAsync(EntityPropertyDto propertyDto, DynamicDtoTypeBuildingContext context)
        {
            var dataType   = propertyDto.DataType;
            var dataFormat = propertyDto.DataFormat;

            switch (dataType)
            {
            case DataTypes.Guid:
                return(typeof(Guid?));

            case DataTypes.String:
                return(typeof(string));

            case DataTypes.Date:
            case DataTypes.DateTime:
                return(typeof(DateTime?));

            case DataTypes.Time:
                return(typeof(TimeSpan?));

            case DataTypes.Boolean:
                return(typeof(bool?));

            case DataTypes.ReferenceListItem:
                // todo: find a way to check an entity property
                // if it's declared as an enum - get base type of this enum
                // if it's declared as int/Int64 - use this type
                return(typeof(Int64?));

            case DataTypes.Number:
            {
                switch (dataFormat)
                {
                case NumberFormats.Int32:
                    return(typeof(int?));

                case NumberFormats.Int64:
                    return(typeof(Int64?));

                case NumberFormats.Float:
                    return(typeof(float?));

                case NumberFormats.Double:
                    return(typeof(decimal?));

                default:
                    return(typeof(decimal?));
                }
            }

            case DataTypes.EntityReference:
                return(GetEntityReferenceType(propertyDto, context));

            case DataTypes.Array:
            {
                if (propertyDto.ItemsType == null)
                {
                    return(null);
                }

                var nestedType = await GetDtoPropertyTypeAsync(propertyDto.ItemsType, context);

                var arrayType = typeof(List <>).MakeGenericType(nestedType);
                return(arrayType);
            }

            case DataTypes.Object:
                return(await BuildNestedTypeAsync(propertyDto, context));    // JSON content

            default:
                throw new NotSupportedException($"Data type not supported: {dataType}");
            }
        }