コード例 #1
0
        public async Task <ActionResult> Create(string name, int projectId, string priority, string description)
        {
            var result = await _taskRepository.Create(name, projectId, priority, description);

            return(ControllerUtilities.CheckResult(result));
        }
コード例 #2
0
ファイル: Controller.Core.cs プロジェクト: mehedi09/GridWork
 public static string GetSelectView(string controller)
 {
     ControllerUtilities c = new ControllerUtilities();
     return c.GetActionView(controller, "editForm1", "Select");
 }
コード例 #3
0
ファイル: FactControllerBase.cs プロジェクト: lulzzz/tellma
 /// <summary>
 /// Takes a list of <see cref="Entity"/>s, and for every entity it inspects the navigation properties, if a navigation property
 /// contains an <see cref="Entity"/> with a strong type, it sets that property to null, and moves the strong entity into a separate
 /// "relatedEntities" hash set, this has several advantages:
 /// 1 - JSON.NET will not have to deal with circular references
 /// 2 - Every strong entity is mentioned once in the JSON response (smaller response size)
 /// 3 - It makes it easier for clients to store and track entities in a central workspace
 /// </summary>
 /// <returns>A hash set of strong related entity in the original result entities (excluding the result entities)</returns>
 protected Dictionary <string, IEnumerable <Entity> > FlattenAndTrim <T>(IEnumerable <T> resultEntities, CancellationToken cancellation)
     where T : Entity
 {
     return(ControllerUtilities.FlattenAndTrim(resultEntities, cancellation));
 }
コード例 #4
0
ファイル: Controller.Core.cs プロジェクト: Ashrafnet/XIOT
        public static string GetDeleteView(string controller)
        {
            ControllerUtilities c = new ControllerUtilities();

            return(c.GetActionView(controller, "editForm1", "Delete"));
        }
コード例 #5
0
        protected override async Task SaveValidateAsync(List <LineDefinitionForSave> entities)
        {
            var defs     = _defCache.GetCurrentDefinitionsIfCached().Data;
            var settings = _settingsCache.GetCurrentSettingsIfCached().Data;

            // C# validation
            int lineDefinitionIndex = 0;

            entities.ForEach(lineDefinition =>
            {
                // Columns
                int columnIndex = 0;
                lineDefinition.Columns.ForEach(column =>
                {
                    int index = column.EntryIndex.Value;
                    if (index < 0)
                    {
                        string path = $"[{lineDefinitionIndex}].{nameof(LineDefinition.Columns)}[{columnIndex}].{nameof(LineDefinitionColumn.EntryIndex)}";
                        string msg  = _localizer["Error_IndexMustBeGreaterOrEqualZero"];

                        ModelState.AddModelError(path, msg);
                    }
                    else if (index > (lineDefinition.Entries?.Count ?? 0))
                    {
                        string path = $"[{lineDefinitionIndex}].{nameof(LineDefinition.Columns)}[{columnIndex}].{nameof(LineDefinitionColumn.EntryIndex)}";
                        string msg  = _localizer["Error_NoEntryCorrespondsToIndex0", index];

                        ModelState.AddModelError(path, msg);
                    }

                    // Required state should always be <= ReadOnlyState
                    if (column.RequiredState > column.ReadOnlyState)
                    {
                        string path = $"[{lineDefinitionIndex}].{nameof(LineDefinition.Columns)}[{columnIndex}].{nameof(LineDefinitionColumn.ReadOnlyState)}";
                        string msg  = _localizer["Error_ReadOnlyStateCannotBeBeforeRequiredState"];;

                        ModelState.AddModelError(path, msg);
                    }

                    columnIndex++;
                });

                // GenerateScript
                if (!string.IsNullOrWhiteSpace(lineDefinition.GenerateScript))
                {
                    // If auto-generate script is specified, DefaultsToForm must be false
                    if (lineDefinition.ViewDefaultsToForm.Value)
                    {
                        string path = $"[{lineDefinitionIndex}].{nameof(LineDefinition.GenerateScript)}";
                        string msg  = _localizer["Error_CannotHaveGenerateScriptWithDefaultsToForm"];

                        ModelState.AddModelError(path, msg);
                    }
                }

                // Generate parameters
                int paramIndex = 0;
                lineDefinition.GenerateParameters.ForEach(parameter =>
                {
                    var errors = ControllerUtilities.ValidateControlOptions(parameter.Control, parameter.ControlOptions, _localizer, settings, defs);
                    foreach (var msg in errors)
                    {
                        ModelState.AddModelError($"[{lineDefinitionIndex}].{nameof(lineDefinition.GenerateParameters)}[{paramIndex}].{nameof(parameter.ControlOptions)}", msg);
                    }

                    paramIndex++;
                });

                // Workflows
                int workflowIndex = 0;
                lineDefinition.Workflows.ForEach(workflow =>
                {
                    int signatureIndex = 0;
                    workflow.Signatures?.ForEach(signature =>
                    {
                        // Role is required
                        if (signature.RuleType == RuleTypes.ByRole && signature.RoleId == null)
                        {
                            string path = $"[{lineDefinitionIndex}].{nameof(LineDefinition.Workflows)}[{workflowIndex}].{nameof(Workflow.Signatures)}[{signatureIndex}].{nameof(WorkflowSignature.RoleId)}";
                            string msg  = _localizer[Constants.Error_Field0IsRequired, _localizer["WorkflowSignature_Role"]];

                            ModelState.AddModelError(path, msg);
                        }

                        // User is required
                        if (signature.RuleType == RuleTypes.ByUser && signature.UserId == null)
                        {
                            string path = $"[{lineDefinitionIndex}].{nameof(LineDefinition.Workflows)}[{workflowIndex}].{nameof(Workflow.Signatures)}[{signatureIndex}].{nameof(WorkflowSignature.UserId)}";
                            string msg  = _localizer[Constants.Error_Field0IsRequired, _localizer["WorkflowSignature_User"]];

                            ModelState.AddModelError(path, msg);
                        }

                        if (signature.RuleType == RuleTypes.ByCustodian && signature.RuleTypeEntryIndex == null)
                        {
                            // Entry index is required
                            if (signature.RuleTypeEntryIndex == null)
                            {
                                string path = $"[{lineDefinitionIndex}].{nameof(LineDefinition.Workflows)}[{workflowIndex}].{nameof(Workflow.Signatures)}[{signatureIndex}].{nameof(WorkflowSignature.RuleTypeEntryIndex)}";
                                string msg  = _localizer[Constants.Error_Field0IsRequired, _localizer["WorkflowSignature_RuleTypeEntryIndex"]];

                                ModelState.AddModelError(path, msg);
                            }
                            else
                            {
                                // Make sure Entry index is not out of bounds
                                int index = signature.RuleTypeEntryIndex.Value;
                                if (index < 0)
                                {
                                    string path = $"[{lineDefinitionIndex}].{nameof(LineDefinition.Workflows)}[{workflowIndex}].{nameof(Workflow.Signatures)}[{signatureIndex}].{nameof(WorkflowSignature.RuleTypeEntryIndex)}";
                                    string msg  = _localizer["Error_IndexMustBeGreaterOrEqualZero"];

                                    ModelState.AddModelError(path, msg);
                                }
                                else if (index > (lineDefinition.Entries?.Count ?? 0))
                                {
                                    string path = $"[{lineDefinitionIndex}].{nameof(LineDefinition.Workflows)}[{workflowIndex}].{nameof(Workflow.Signatures)}[{signatureIndex}].{nameof(WorkflowSignature.RuleTypeEntryIndex)}";
                                    string msg  = _localizer["Error_NoEntryCorrespondsToIndex0", index];

                                    ModelState.AddModelError(path, msg);
                                }
                            }
                        }

                        if (signature.PredicateType == PredicateTypes.ValueGreaterOrEqual)
                        {
                            // Value is required
                            if (signature.Value == null)
                            {
                                string path = $"[{lineDefinitionIndex}].{nameof(LineDefinition.Workflows)}[{workflowIndex}].{nameof(Workflow.Signatures)}[{signatureIndex}].{nameof(WorkflowSignature.Value)}";
                                string msg  = _localizer[Constants.Error_Field0IsRequired, _localizer["WorkflowSignature_Value"]];

                                ModelState.AddModelError(path, msg);
                            }

                            // Entry Index is required
                            if (signature.PredicateTypeEntryIndex == null)
                            {
                                string path = $"[{lineDefinitionIndex}].{nameof(LineDefinition.Workflows)}[{workflowIndex}].{nameof(Workflow.Signatures)}[{signatureIndex}].{nameof(WorkflowSignature.PredicateTypeEntryIndex)}";
                                string msg  = _localizer[Constants.Error_Field0IsRequired, _localizer["WorkflowSignature_PredicateTypeEntryIndex"]];

                                ModelState.AddModelError(path, msg);
                            }
                            else
                            {
                                // Make sure Entry index is not out of bounds
                                int index = signature.PredicateTypeEntryIndex.Value;
                                if (index < 0)
                                {
                                    string path = $"[{lineDefinitionIndex}].{nameof(LineDefinition.Workflows)}[{workflowIndex}].{nameof(Workflow.Signatures)}[{signatureIndex}].{nameof(WorkflowSignature.PredicateTypeEntryIndex)}";
                                    string msg  = _localizer["Error_IndexMustBeGreaterOrEqualZero"];

                                    ModelState.AddModelError(path, msg);
                                }
                                else if (index > (lineDefinition.Entries?.Count ?? 0))
                                {
                                    string path = $"[{lineDefinitionIndex}].{nameof(LineDefinition.Workflows)}[{workflowIndex}].{nameof(Workflow.Signatures)}[{signatureIndex}].{nameof(WorkflowSignature.PredicateTypeEntryIndex)}";
                                    string msg  = _localizer["Error_NoEntryCorrespondsToIndex0", index];

                                    ModelState.AddModelError(path, msg);
                                }
                            }
                        }

                        signatureIndex++;
                    });

                    workflowIndex++;
                });

                // Barcode
                if (lineDefinition.BarcodeColumnIndex != null)
                {
                    // If barcode is enabled, BarcodeProperty must be specified
                    if (string.IsNullOrWhiteSpace(lineDefinition.BarcodeProperty))
                    {
                        string path = $"[{lineDefinitionIndex}].{nameof(LineDefinition.BarcodeProperty)}";
                        string msg  = _localizer[Constants.Error_Field0IsRequired, _localizer["LineDefinition_BarcodeProperty"]];

                        ModelState.AddModelError(path, msg);
                    }

                    // If barcode is enabled, BarcodeExistingItemHandling must be specified
                    if (string.IsNullOrWhiteSpace(lineDefinition.BarcodeExistingItemHandling))
                    {
                        string path = $"[{lineDefinitionIndex}].{nameof(LineDefinition.BarcodeExistingItemHandling)}";
                        string msg  = _localizer[Constants.Error_Field0IsRequired, _localizer["LineDefinition_BarcodeExistingItemHandling"]];

                        ModelState.AddModelError(path, msg);
                    }

                    // If barcode is enabled, DefaultsToForm must be false
                    if (lineDefinition.ViewDefaultsToForm.Value)
                    {
                        string path = $"[{lineDefinitionIndex}].{nameof(LineDefinition.BarcodeColumnIndex)}";
                        string msg  = _localizer["Error_CannotHaveBarcodeWithDefaultsToForm"];

                        ModelState.AddModelError(path, msg);
                    }

                    // BarcodeColumnIndex must be within Columns range
                    var colIndex = lineDefinition.BarcodeColumnIndex.Value;
                    if (colIndex >= lineDefinition.Columns.Count)
                    {
                        string path = $"[{lineDefinitionIndex}].{nameof(LineDefinition.BarcodeColumnIndex)}";
                        string msg  = _localizer["Error_BarcodeColumnIndexOutOfRange"];

                        ModelState.AddModelError(path, msg);
                    }
                    else
                    {
                        // Barcode Column cannot inherit from headers
                        var colDef = lineDefinition.Columns[colIndex];
                        if (colDef.InheritsFromHeader > 0)
                        {
                            string path = $"[{lineDefinitionIndex}].{nameof(LineDefinition.BarcodeColumnIndex)}";
                            string msg  = _localizer["Error_BarcodeColumnCannotInheritFromHeaders"];

                            ModelState.AddModelError(path, msg);
                        }

                        // Barcode Column must be visible from DRAFT
                        if (colDef.VisibleState > 0)
                        {
                            string path = $"[{lineDefinitionIndex}].{nameof(LineDefinition.BarcodeColumnIndex)}";
                            string msg  = _localizer["Error_BarcodeColumnMustBeVisibleFromDraft"];

                            ModelState.AddModelError(path, msg);
                        }

                        // Barcode Column must be editable from DRAFT
                        if (colDef.ReadOnlyState == 0)
                        {
                            string path = $"[{lineDefinitionIndex}].{nameof(LineDefinition.BarcodeColumnIndex)}";
                            string msg  = _localizer["Error_BarcodeColumnCannotBeReadOnlyFromDraft"];

                            ModelState.AddModelError(path, msg);
                        }

                        Dictionary <string, Type> acceptableColumnNames = new Dictionary <string, Type> {
                            { "CustodianId", typeof(Relation) },
                            { "CustodyId", typeof(Custody) },
                            { "ParticipantId", typeof(Relation) },
                            { "ResourceId", typeof(Resource) }
                        };

                        if (string.IsNullOrWhiteSpace(colDef.ColumnName))
                        {
                            // Error handled earlier
                        }
                        else if (!acceptableColumnNames.TryGetValue(colDef.ColumnName, out Type colType))
                        {
                            // Barcode Column must have on of the supported column names
                            string path  = $"[{lineDefinitionIndex}].{nameof(LineDefinition.BarcodeColumnIndex)}";
                            string names = string.Join(", ", acceptableColumnNames.Keys.Select(e => _localizer["Entry_" + e[0..^ 2]]));
コード例 #6
0
 public static void VerifyMvcPage <ControllerUnderTest>(Expression <Func <ControllerUnderTest, Func <ActionResult> > > actionName, Func <string, string> scrubber = null)
     where ControllerUnderTest : TestableControllerBase
 {
     VerifyMvcUrl(ReflectionUtility.GetControllerName <ControllerUnderTest>(), ControllerUtilities.GetMethodName(actionName.Body), GetFilePathasQueryString <ControllerUnderTest>(), scrubber);
 }
コード例 #7
0
        public async Task <ActionResult> Delete(int id)
        {
            var result = await _projectRepository.Delete(id);

            return(ControllerUtilities.CheckResult(result));
        }
コード例 #8
0
        public async Task <(List <DocumentDefinition>, Extras)> UpdateState(List <int> ids, UpdateStateArguments args)
        {
            // Make sure
            int jvDefId = _defCache.GetCurrentDefinitionsIfCached()?.Data?.ManualJournalVouchersDefinitionId ??
                          throw new BadRequestException("The Manual Journal Voucher Id is not defined");

            int index = 0;

            ids.ForEach(id =>
            {
                if (id == jvDefId)
                {
                    string path = $"[{index}]";
                    string msg  = _localizer["Error_CannotModifySystemItem"];

                    ModelState.AddModelError(path, msg);
                }

                index++;
            });

            // No point carrying on
            ModelState.ThrowIfInvalid();

            // Check user permissions
            var action       = "State";
            var actionFilter = await UserPermissionsFilter(action, cancellation : default);

            ids = await CheckActionPermissionsBefore(actionFilter, ids);

            // C# Validation
            if (string.IsNullOrWhiteSpace(args.State))
            {
                throw new BadRequestException(_localizer[Constants.Error_Field0IsRequired, _localizer["State"]]);
            }

            if (!DefStates.All.Contains(args.State))
            {
                string validStates = string.Join(", ", DefStates.All);
                throw new BadRequestException($"'{args.State}' is not a valid definition state, valid states are: {validStates}");
            }

            // Transaction
            using var trx = ControllerUtilities.CreateTransaction();

            // Validate
            int remainingErrorCount = ModelState.MaxAllowedErrors - ModelState.ErrorCount;
            var errors = await _repo.DocumentDefinitions_Validate__UpdateState(ids, args.State, top : remainingErrorCount);

            ControllerUtilities.AddLocalizedErrors(ModelState, errors, _localizer);
            ModelState.ThrowIfInvalid();

            // Execute
            await _repo.DocumentDefinitions__UpdateState(ids, args.State);

            // Prepare response
            List <DocumentDefinition> data = null;
            Extras extras = null;

            if (args.ReturnEntities ?? false)
            {
                (data, extras) = await GetByIds(ids, args, action, cancellation : default);
            }

            // Check user permissions again
            await CheckActionPermissionsAfter(actionFilter, ids, data);

            // Commit and return
            trx.Complete();
            return(data, extras);
        }
コード例 #9
0
        public async Task <ActionResult <IEnumerable <Project> > > Get()
        {
            var result = await _projectRepository.Get();

            return(ControllerUtilities.CheckResult(result));
        }
コード例 #10
0
        public async Task <ActionResult> Create(string name, string priority)
        {
            var result = await _projectRepository.Create(name, priority);

            return(ControllerUtilities.CheckResult(result));
        }
コード例 #11
0
        protected override async Task <(List <MeasurementUnitForQuery>, IQueryable <MeasurementUnitForQuery>)> PersistAsync(List <MeasurementUnitForSave> entities, SaveArguments args)
        {
            // Add created entities
            DataTable entitiesTable = ControllerUtilities.DataTable(entities, addIndex: true);
            var       entitiesTvp   = new SqlParameter("Entities", entitiesTable)
            {
                TypeName  = $"dbo.{nameof(MeasurementUnitForSave)}List",
                SqlDbType = SqlDbType.Structured
            };

            string saveSql = $@"
-- Procedure: MeasurementUnitsSave
SET NOCOUNT ON;
	DECLARE @IndexedIds [dbo].[IndexedIdList];
	DECLARE @TenantId int = CONVERT(INT, SESSION_CONTEXT(N'TenantId'));
	DECLARE @Now DATETIMEOFFSET(7) = SYSDATETIMEOFFSET();
	DECLARE @UserId INT = CONVERT(INT, SESSION_CONTEXT(N'UserId'));

	INSERT INTO @IndexedIds([Index], [Id])
	SELECT x.[Index], x.[Id]
	FROM
	(
		MERGE INTO [dbo].MeasurementUnits AS t
		USING (
			SELECT [Index], [Id], [Code], [UnitType], [Name], [Name2], [UnitAmount], [BaseAmount]
			FROM @Entities 
			WHERE [EntityState] IN (N'Inserted', N'Updated')
		) AS s ON (t.Id = s.Id)
		WHEN MATCHED 
		THEN
			UPDATE SET 
				t.[UnitType]	= s.[UnitType],
				t.[Name]		= s.[Name],
				t.[Name2]		= s.[Name2],
				t.[UnitAmount]	= s.[UnitAmount],
				t.[BaseAmount]	= s.[BaseAmount],
				t.[Code]		= s.[Code],
				t.[ModifiedAt]	= @Now,
				t.[ModifiedById]	= @UserId
		WHEN NOT MATCHED THEN
				INSERT ([TenantId], [UnitType], [Name], [Name2], [UnitAmount], [BaseAmount], [Code], [CreatedAt], [CreatedById], [ModifiedAt], [ModifiedById])
				VALUES (@TenantId, s.[UnitType], s.[Name], s.[Name2], s.[UnitAmount], s.[BaseAmount], s.[Code], @Now, @UserId, @Now, @UserId)
			OUTPUT s.[Index], inserted.[Id] 
	) As x
    OPTION(RECOMPILE)
";

            // Optimization
            if (!(args.ReturnEntities ?? false))
            {
                // IF no returned items are expected, simply execute a non-Query and return an empty list;
                await _db.Database.ExecuteSqlCommandAsync(saveSql, entitiesTvp);

                return(new List <MeasurementUnitForQuery>(), null);
            }
            else
            {
                // If returned items are expected, append a select statement to the SQL command
                saveSql = saveSql += "SELECT * FROM @IndexedIds;";

                // Retrieve the map from Indexes to Ids
                var indexedIds = await _db.Saving.FromSql(saveSql, entitiesTvp).ToListAsync();

                // Load the entities using their Ids
                DataTable idsTable = ControllerUtilities.DataTable(indexedIds.Select(e => new { e.Id }), addIndex: false);
                var       idsTvp   = new SqlParameter("Ids", idsTable)
                {
                    TypeName  = $"dbo.IdList",
                    SqlDbType = SqlDbType.Structured
                };

                var q = _db.VW_MeasurementUnits.FromSql("SELECT * FROM dbo.[MeasurementUnits] WHERE Id IN (SELECT Id FROM @Ids)", idsTvp);
                q = Expand(q, args.Expand);
                var savedEntities = await q.ToListAsync();


                // SQL Server does not guarantee order, so make sure the result is sorted according to the initial index
                Dictionary <int, int> indices = indexedIds.ToDictionary(e => e.Id, e => e.Index);
                var sortedSavedEntities       = new MeasurementUnitForQuery[savedEntities.Count];
                foreach (var item in savedEntities)
                {
                    int index = indices[item.Id.Value];
                    sortedSavedEntities[index] = item;
                }

                // Return the sorted collection
                return(sortedSavedEntities.ToList(), q);
            }
        }
コード例 #12
0
        protected override async Task ValidateAsync(List <MeasurementUnitForSave> entities)
        {
            // Hash the indices for performance
            var indices = entities.ToIndexDictionary();

            // Check that Ids make sense in relation to EntityState, and that no entity is DELETED
            // All these errors indicate a bug
            foreach (var entity in entities)
            {
                if (entity.EntityState == EntityStates.Deleted)
                {
                    // Won't be supported for this API
                    var index = indices[entity];
                    ModelState.AddModelError($"[{index}].{nameof(entity.EntityState)}", _localizer["Error_Deleting0IsNotSupportedFromThisAPI", _localizer["MeasurementUnits"]]);
                }
            }

            // Check that Ids are unique
            var duplicateIds = entities.Where(e => e.Id != null).GroupBy(e => e.Id.Value).Where(g => g.Count() > 1);

            foreach (var groupWithDuplicateIds in duplicateIds)
            {
                foreach (var entity in groupWithDuplicateIds)
                {
                    // This error indicates a bug
                    var index = indices[entity];
                    ModelState.AddModelError($"[{index}].{nameof(entity.Id)}", _localizer["Error_TheEntityWithId0IsSpecifiedMoreThanOnce", entity.Id]);
                }
            }

            // No need to invoke SQL if the model state is full of errors
            if (ModelState.HasReachedMaxErrors)
            {
                return;
            }

            // Perform SQL-side validation
            DataTable entitiesTable = ControllerUtilities.DataTable(entities, addIndex: true);
            var       entitiesTvp   = new SqlParameter("Entities", entitiesTable)
            {
                TypeName = $"dbo.{nameof(MeasurementUnitForSave)}List", SqlDbType = SqlDbType.Structured
            };
            int remainingErrorCount = ModelState.MaxAllowedErrors - ModelState.ErrorCount;

            // Code, Name and Name2 must be unique
            var sqlErrors = await _db.Validation.FromSql($@"
SET NOCOUNT ON;
DECLARE @ValidationErrors dbo.ValidationErrorList;

    -- Non Null Ids must exist
    INSERT INTO @ValidationErrors([Key], [ErrorName], [Argument1])
    SELECT '[' + CAST([Id] AS NVARCHAR(255)) + '].Id' As [Key], N'Error_TheId0WasNotFound' As [ErrorName], CAST([Id] As NVARCHAR(255)) As [Argument1]
    FROM @Entities
    WHERE Id Is NOT NULL AND Id NOT IN (SELECT Id from [dbo].[MeasurementUnits])
    
	-- Code must be unique
	INSERT INTO @ValidationErrors([Key], [ErrorName], [Argument1], [Argument2], [Argument3], [Argument4], [Argument5]) 
	SELECT '[' + CAST(FE.[Index] AS NVARCHAR(255)) + '].Code' As [Key], N'Error_TheCode0IsUsed' As [ErrorName],
		FE.Code AS Argument1, NULL AS Argument2, NULL AS Argument3, NULL AS Argument4, NULL AS Argument5
	FROM @Entities FE 
	JOIN [dbo].MeasurementUnits BE ON FE.Code = BE.Code
	WHERE FE.[Code] IS NOT NULL
	AND (FE.[EntityState] = N'Inserted') OR (FE.Id <> BE.Id) OPTION(HASH JOIN);

	-- Code must not be duplicated in the uploaded list
	INSERT INTO @ValidationErrors([Key], [ErrorName], [Argument1], [Argument2], [Argument3], [Argument4], [Argument5]) 
	SELECT '[' + CAST([Index] AS NVARCHAR(255)) + '].Code' As [Key], N'Error_TheCode0IsDuplicated' As [ErrorName],
		[Code] AS Argument1, NULL AS Argument2, NULL AS Argument3, NULL AS Argument4, NULL AS Argument5
	FROM @Entities
	WHERE [Code] IN (
		SELECT [Code]
		FROM @Entities
		WHERE [Code] IS NOT NULL
		GROUP BY [Code]
		HAVING COUNT(*) > 1
	) OPTION(HASH JOIN);

	-- Name must not exist already
	INSERT INTO @ValidationErrors([Key], [ErrorName], [Argument1], [Argument2], [Argument3], [Argument4], [Argument5]) 
	SELECT '[' + CAST(FE.[Index] AS NVARCHAR(255)) + '].Name' As [Key], N'Error_TheName0IsUsed' As [ErrorName],
		FE.[Name] AS Argument1, NULL AS Argument2, NULL AS Argument3, NULL AS Argument4, NULL AS Argument5
	FROM @Entities FE 
	JOIN [dbo].MeasurementUnits BE ON FE.[Name] = BE.[Name]
	WHERE (FE.[EntityState] = N'Inserted') OR (FE.Id <> BE.Id) OPTION(HASH JOIN);

	-- Name must be unique in the uploaded list
	INSERT INTO @ValidationErrors([Key], [ErrorName], [Argument1], [Argument2], [Argument3], [Argument4], [Argument5]) 
	SELECT '[' + CAST([Index] AS NVARCHAR(255)) + '].Name' As [Key], N'Error_TheName0IsDuplicated' As [ErrorName],
		[Name] AS Argument1, NULL AS Argument2, NULL AS Argument3, NULL AS Argument4, NULL AS Argument5
	FROM @Entities
	WHERE [Name] IN (
		SELECT [Name]
		FROM @Entities
		GROUP BY [Name]
		HAVING COUNT(*) > 1
	) OPTION(HASH JOIN);

	-- Name2 must not exist already
	INSERT INTO @ValidationErrors([Key], [ErrorName], [Argument1], [Argument2], [Argument3], [Argument4], [Argument5]) 
	SELECT '[' + CAST(FE.[Index] AS NVARCHAR(255)) + '].Name2' As [Key], N'Error_TheName0IsUsed' As [ErrorName],
		FE.[Name2] AS Argument1, NULL AS Argument2, NULL AS Argument3, NULL AS Argument4, NULL AS Argument5
	FROM @Entities FE 
	JOIN [dbo].MeasurementUnits BE ON FE.[Name2] = BE.[Name2]
	WHERE (FE.[EntityState] = N'Inserted') OR (FE.Id <> BE.Id) OPTION(HASH JOIN);

	-- Name2 must be unique in the uploaded list
	INSERT INTO @ValidationErrors([Key], [ErrorName], [Argument1], [Argument2], [Argument3], [Argument4], [Argument5]) 
	SELECT '[' + CAST([Index] AS NVARCHAR(255)) + '].Name2' As [Key], N'Error_TheName0IsDuplicated' As [ErrorName],
		[Name2] AS Argument1, NULL AS Argument2, NULL AS Argument3, NULL AS Argument4, NULL AS Argument5
	FROM @Entities
	WHERE [Name2] IN (
		SELECT [Name2]
		FROM @Entities
		GROUP BY [Name2]
		HAVING COUNT(*) > 1
	) OPTION(HASH JOIN);
    -- Add further logic

SELECT TOP {remainingErrorCount} * FROM @ValidationErrors;
", entitiesTvp).ToListAsync();

            // Loop over the errors returned from SQL and add them to ModelState
            foreach (var sqlError in sqlErrors)
            {
                var formatArguments = sqlError.ToFormatArguments();

                string key          = sqlError.Key;
                string errorMessage = _localizer[sqlError.ErrorName, formatArguments];

                ModelState.AddModelError(key: key, errorMessage: errorMessage);
            }
        }
コード例 #13
0
 protected override async Task <IEnumerable <AbstractPermission> > UserPermissions(PermissionLevel level)
 {
     return(await ControllerUtilities.GetPermissions(_db.AbstractPermissions, level, "measurement-units"));
 }
コード例 #14
0
        protected override async Task <List <int> > SaveExecuteAsync(List <UserForSave> entities, ExpandExpression expand, bool returnIds)
        {
            // NOTE: this method is not optimized for massive bulk (e.g. 1,000+ users), since it relies
            // on querying identity through UserManager one email at a time but it should be acceptable
            // with the usual workloads, customers with more than 200 users are rare anyways

            // Step (1) enlist the app repo
            _appRepo.EnlistTransaction(Transaction.Current); // So that it is not affected by admin trx scope later

            // Step (2): If Embedded Identity Server is enabled, create any emails that don't already exist there
            var usersToInvite = new List <(EmbeddedIdentityServerUser IdUser, UserForSave User)>();

            if (_options.EmbeddedIdentityServerEnabled)
            {
                _identityTrxScope = ControllerUtilities.CreateTransaction(TransactionScopeOption.RequiresNew);

                foreach (var entity in entities)
                {
                    var email = entity.Email;

                    // In case the user was added in a previous failed transaction
                    // or something, we always try to be forgiving in the code
                    var identityUser = await _userManager.FindByNameAsync(email) ??
                                       await _userManager.FindByEmailAsync(email);

                    // This is truly a new user, create it
                    if (identityUser == null)
                    {
                        // Create the identity user
                        identityUser = new EmbeddedIdentityServerUser
                        {
                            UserName       = email,
                            Email          = email,
                            EmailConfirmed = !_options.EmailEnabled

                                             // Note: If the system is integrated with an email service, user emails
                                             // are automatically confirmed, otherwise users must confirm their
                        };

                        var result = await _userManager.CreateAsync(identityUser);

                        if (!result.Succeeded)
                        {
                            string msg = string.Join(", ", result.Errors.Select(e => e.Description));
                            _logger.LogError(msg);

                            throw new BadRequestException($"An unexpected error occurred while creating an account for '{email}'");
                        }
                    }

                    // Mark for invitation later
                    if (!identityUser.EmailConfirmed)
                    {
                        usersToInvite.Add((identityUser, entity));
                    }
                }
            }

            // Step (3): Extract the images
            var(blobsToDelete, blobsToSave, imageIds) = await ImageUtilities.ExtractImages <User, UserForSave>(_appRepo, entities, BlobName);

            // Step (4): Save the users in the app database
            var ids = await _appRepo.Users__Save(entities, imageIds, returnIds);

            // Step (5): Delete old images from the blob storage
            if (blobsToDelete.Any())
            {
                await _blobService.DeleteBlobsAsync(blobsToDelete);
            }

            // Step (6): Save new images to the blob storage
            if (blobsToSave.Any())
            {
                await _blobService.SaveBlobsAsync(blobsToSave);
            }

            // Step (7) Same the emails in the admin database
            var tenantId = _tenantIdAccessor.GetTenantId();

            _adminTrxScope = ControllerUtilities.CreateTransaction(TransactionScopeOption.RequiresNew);
            _adminRepo.EnlistTransaction(Transaction.Current);
            var oldEmails = new List <string>(); // Emails are readonly after the first save
            var newEmails = entities.Where(e => e.Id == 0).Select(e => e.Email);
            await _adminRepo.GlobalUsers__Save(newEmails, oldEmails, tenantId);

            // Step (8): Send the invitation emails
            if (usersToInvite.Any()) // This will be empty if embedded identity is disabled or if email is disabled
            {
                var userIds       = usersToInvite.Select(e => e.User.Id).ToArray();
                var tos           = new List <string>();
                var subjects      = new List <string>();
                var substitutions = new List <Dictionary <string, string> >();
                foreach (var(idUser, user) in usersToInvite)
                {
                    // Add the email sender parameters
                    var(subject, body) = await MakeInvitationEmailAsync(idUser, user.Name, user.Name2, user.Name3, user.PreferredLanguage);

                    tos.Add(idUser.Email);
                    subjects.Add(subject);
                    substitutions.Add(new Dictionary <string, string> {
                        { "-message-", body }
                    });
                }

                await _emailSender.SendEmailBulkAsync(
                    tos : tos,
                    subjects : subjects,
                    htmlMessage : $"-message-",
                    substitutions : substitutions.ToList()
                    );
            }

            // Return the new Ids
            return(ids);
        }
コード例 #15
0
        public async Task <ActionResult> UpdateName(int id, string name)
        {
            var result = await _projectRepository.UpdateName(id, name);

            return(ControllerUtilities.CheckResult(result));
        }
コード例 #16
0
 /// <summary>
 /// Takes a list of <see cref="Entity"/>, and for every entity it inspects the navigation properties, if a navigation property
 /// contains an <see cref="Entity"/> with a strong type, it sets that property to null, and moves the strong entity into a separate
 /// "relatedEntities" hash set, this has several advantages: <br/>
 /// 1 - JSON.NET will not have to deal with circular references <br/>
 /// 2 - Every strong entity is mentioned once in the JSON response (smaller response size) <br/>
 /// 3 - It makes it easier for clients to store and track entities in a central workspace <br/>
 /// </summary>
 /// <returns>A hash set of strong related entity in the original result entities (excluding the result entities).</returns>
 protected RelatedEntities Flatten <T>(IEnumerable <T> resultEntities, CancellationToken cancellation)
     where T : Entity
 {
     return(ControllerUtilities.Flatten(resultEntities, cancellation));
 }
コード例 #17
0
        public async Task <ActionResult> UpdatePriority(int id, string priority)
        {
            var result = await _projectRepository.UpdatePriority(id, priority);

            return(ControllerUtilities.CheckResult(result));
        }
コード例 #18
0
            public async Task OnResourceExecutionAsync(ResourceExecutingContext context, ResourceExecutionDelegate next)
            {
                var cancellation = context.HttpContext.RequestAborted;

                // (1) Make sure the requester has an active user
                AdminUserInfo userInfo = await _adminRepo.GetAdminUserInfoAsync(cancellation);

                if (userInfo.UserId == null)
                {
                    // If there is no user cut the pipeline short and return a Forbidden 403
                    context.Result = new StatusCodeResult(StatusCodes.Status403Forbidden);

                    // This indicates to the client to discard all cached information about this
                    // company since the user is no longer a member of it
                    context.HttpContext.Response.Headers.Add("x-admin-settings-version", Constants.Unauthorized);
                    context.HttpContext.Response.Headers.Add("x-admin-permissions-version", Constants.Unauthorized);
                    context.HttpContext.Response.Headers.Add("x-admin-user-settings-version", Constants.Unauthorized);

                    return;
                }

                var userId        = userInfo.UserId.Value;
                var externalId    = _externalUserAccessor.GetUserId();
                var externalEmail = _externalUserAccessor.GetUserEmail();

                // (3) If the user exists but new, set the External Id
                if (userInfo.ExternalId == null)
                {
                    using var trx = ControllerUtilities.CreateTransaction();

                    await _adminRepo.AdminUsers__SetExternalIdByUserId(userId, externalId);

                    await _adminRepo.DirectoryUsers__SetExternalIdByEmail(externalEmail, externalId);

                    trx.Complete();
                }

                else if (userInfo.ExternalId != externalId)
                {
                    // Note: we will assume that no identity provider can provider the same email twice with
                    // two different external Ids, i.e. that no provider allows email recycling, so we won't handle this case now
                    // This can only happen if the application is re-configured to a new identity provider, or if someone messed with
                    // the database directly
                    context.Result = new BadRequestObjectResult("The sign-in email already exists but with a different external Id");
                    return;
                }

                // (4) If the user's email address has changed at the identity server, update it locally
                else if (userInfo.Email != externalEmail)
                {
                    using var trx = ControllerUtilities.CreateTransaction();
                    await _adminRepo.AdminUsers__SetEmailByUserId(userId, externalEmail);

                    await _adminRepo.DirectoryUsers__SetEmailByExternalId(externalId, externalEmail);

                    trx.Complete();
                }

                // (5) If any version headers are supplied: examine their freshness
                {
                    // Permissions
                    var clientVersion = context.HttpContext.Request.Headers["X-Admin-Permissions-Version"].FirstOrDefault();
                    if (!string.IsNullOrWhiteSpace(clientVersion))
                    {
                        var databaseVersion = userInfo.PermissionsVersion;
                        context.HttpContext.Response.Headers.Add("x-admin-permissions-version",
                                                                 clientVersion == databaseVersion ? Constants.Fresh : Constants.Stale);
                    }
                }

                {
                    // User Settings
                    var clientVersion = context.HttpContext.Request.Headers["X-Admin-User-Settings-Version"].FirstOrDefault();
                    if (!string.IsNullOrWhiteSpace(clientVersion))
                    {
                        var databaseVersion = userInfo.UserSettingsVersion;
                        context.HttpContext.Response.Headers.Add("x-admin-user-settings-version",
                                                                 clientVersion == databaseVersion ? Constants.Fresh : Constants.Stale);
                    }
                }

                {
                    // Settings
                    var clientVersion = context.HttpContext.Request.Headers["X-Admin-Settings-Version"].FirstOrDefault();
                    var adminInfo     = new { SettingsVersion = clientVersion }; // await _adminRepo.GetAdminInfoAsync(); // TODO
                    if (!string.IsNullOrWhiteSpace(clientVersion))
                    {
                        var databaseVersion = adminInfo.SettingsVersion;
                        context.HttpContext.Response.Headers.Add("x-settings-version",
                                                                 clientVersion == databaseVersion ? Constants.Fresh : Constants.Stale);
                    }
                }

                // Finally call the Action itself
                await next();
            }
コード例 #19
0
ファイル: AgentsController.cs プロジェクト: crazyants/BSharp
        protected override async Task <IQueryable <AgentForQuery> > ApplyReadPermissionsCriteria(IQueryable <AgentForQuery> query, IEnumerable <AbstractPermission> permissions)
        {
            if (ViewId() != ALL)
            {
                return(await base.ApplyReadPermissionsCriteria(query, permissions));
            }
            else
            {
                // Get all permissions related to agents
                var allPermissions = await ControllerUtilities.GetPermissions(_db.AbstractPermissions, PermissionLevel.Read, _agentTypes);

                if (!allPermissions.Any())
                {
                    // User doesn't have access to any type of agent
                    throw new ForbiddenException();
                }
                else if (allPermissions.Any(e => e.ViewId == ALL))
                {
                    // Optimization
                    return(query);
                }
                else if (_agentTypes.All(t => allPermissions.Any(e => e.ViewId == t && string.IsNullOrWhiteSpace(e.Criteria))))
                {
                    // this might be risky if the developer forgets to add an agent type in 'agentTypes' array
                    return(query);
                }
                else
                {
                    /* IF we reach here it means the user can only see a filtered list of agents
                     * The purpose of the code below is to construct a dynamic linq query that looks like this:
                     *
                     * e =>
                     * (e.AgentType == "individuals" && <dynamic linq for individuals>) ||
                     * (e.AgentType == "organizations" && <dynamic linq for organizations>) ||
                     *
                     */

                    // The parameter on which the dynamic LINQ expression is based
                    var eParam = Expression.Parameter(typeof(AgentForQuery));

                    Expression fullExpression = null;
                    foreach (var g in allPermissions.GroupBy(e => e.ViewId))
                    {
                        string viewId = g.Key;

                        Expression typePropAccess   = Expression.Property(eParam, nameof(AgentForQuery.AgentType));
                        Expression viewIdConstant   = Expression.Constant(viewId);
                        Expression typePropEquality = Expression.Equal(typePropAccess, viewIdConstant);
                        Expression viewIdExpression;

                        if (g.Any(e => string.IsNullOrWhiteSpace(e.Criteria)))
                        {
                            // The user can read all records of this type
                            viewIdExpression = typePropEquality;
                        }
                        else
                        {
                            // The user has access to part of the data set based on a list of filters that will
                            // be ORed together in a dynamic linq query
                            IEnumerable <string> criteriaList = g.Select(e => e.Criteria);

                            // First criteria
                            viewIdExpression = _filterParser.ParseFilterExpression <AgentForQuery>(criteriaList.First(), eParam);

                            // The remaining criteria
                            foreach (var criteria in criteriaList.Skip(1))
                            {
                                var criteriaExpression = _filterParser.ParseFilterExpression <AgentForQuery>(criteria, eParam);
                                viewIdExpression = Expression.OrElse(viewIdExpression, criteriaExpression);
                            }

                            viewIdExpression = Expression.AndAlso(typePropEquality, viewIdExpression);
                        }

                        // OR this viewId expression with the remaining viewId expressions
                        fullExpression = fullExpression == null ? viewIdExpression : Expression.OrElse(fullExpression, viewIdExpression);
                    }

                    var lambda = Expression.Lambda <Func <AgentForQuery, bool> >(fullExpression, eParam);
                    return(query.Where(lambda));
                }
            }
        }
コード例 #20
0
        protected override Task <List <LineDefinitionForSave> > SavePreprocessAsync(List <LineDefinitionForSave> entities)
        {
            var settings = _settingsCache.GetCurrentSettingsIfCached().Data;

            entities.ForEach(lineDefinition =>
            {
                lineDefinition.AllowSelectiveSigning ??= false;
                lineDefinition.ViewDefaultsToForm ??= false;
                lineDefinition.BarcodeBeepsEnabled ??= false;

                lineDefinition.Columns ??= new List <LineDefinitionColumnForSave>();
                lineDefinition.Entries ??= new List <LineDefinitionEntryForSave>();
                lineDefinition.GenerateParameters ??= new List <LineDefinitionGenerateParameterForSave>();
                lineDefinition.StateReasons ??= new List <LineDefinitionStateReasonForSave>();
                lineDefinition.Workflows ??= new List <WorkflowForSave>();

                lineDefinition?.Columns.ForEach(column =>
                {
                    // Those two are required in the sql table, so they cannot be null
                    if (column.ColumnName == nameof(Entry.CenterId))
                    {
                        column.VisibleState  = LineState.Draft;
                        column.RequiredState = LineState.Draft;
                    }

                    if (column.ColumnName == nameof(Entry.CurrencyId))
                    {
                        column.VisibleState  = LineState.Draft;
                        column.RequiredState = LineState.Draft;
                    }

                    // IMPORTANT: Keep in sync with line-definitions-details.component.ts
                    switch (column.ColumnName)
                    {
                    case "PostingDate":
                    case "Memo":
                    case "CurrencyId":
                    case "CenterId":
                    case "CustodianId":
                    case "CustodyId":
                    case "ParticipantId":
                    case "ResourceId":
                    case "Quantity":
                    case "UnitId":
                    case "Time1":
                    case "Time2":
                    case "ExternalReference":
                    case "InternalReference":
                        break;

                    default:
                        column.InheritsFromHeader = 0;     // Only listed columns can inherit
                        break;
                    }

                    if (column.ColumnName == null || !column.ColumnName.EndsWith("Id"))
                    {
                        column.Filter = null; // Only listed columns can inherit
                    }
                });

                // Generate Parameters
                lineDefinition.GenerateParameters.ForEach(parameter =>
                {
                    parameter.ControlOptions = ControllerUtilities.PreprocessControlOptions(parameter.Control, parameter.ControlOptions, settings);
                });

                // Workflows
                lineDefinition?.Workflows.ForEach(workflow =>
                {
                    workflow?.Signatures?.ForEach(signature =>
                    {
                        if (signature != null)
                        {
                            if (signature.RuleType != RuleTypes.ByUser)
                            {
                                signature.UserId = null;
                            }

                            if (signature.RuleType != RuleTypes.ByRole)
                            {
                                signature.RoleId = null;
                            }

                            if (signature.RuleType != RuleTypes.ByCustodian)
                            {
                                signature.RuleTypeEntryIndex = null;
                            }

                            if (signature.RuleType == RuleTypes.Public)
                            {
                                signature.ProxyRoleId = null;
                            }

                            if (signature.PredicateType == null)
                            {
                                signature.PredicateTypeEntryIndex = null;
                                signature.Value = null;
                            }
                        }
                    });
                });
            });

            return(Task.FromResult(entities));
        }
コード例 #21
0
ファイル: AgentsController.cs プロジェクト: crazyants/BSharp
        protected override async Task ValidateAsync(List <AgentForSave> entities)
        {
            // Get the agent type from the context
            string agentType = ViewId();

            // Hash the indices for performance
            var indices = entities.ToIndexDictionary();

            // Check that Ids make sense in relation to EntityState, and that no entity is DELETED
            // All these errors indicate a bug
            foreach (var entity in entities)
            {
                if (entity.EntityState == EntityStates.Deleted)
                {
                    // Won't be supported for this API
                    var index = indices[entity];
                    ModelState.AddModelError($"[{index}].{nameof(entity.EntityState)}", _localizer["Error_Deleting0IsNotSupportedFromThisAPI", PluralName()]);
                }
            }

            // Check that Ids are unique
            var duplicateIds = entities.Where(e => e.Id != null).GroupBy(e => e.Id.Value).Where(g => g.Count() > 1);

            foreach (var groupWithDuplicateIds in duplicateIds)
            {
                foreach (var entity in groupWithDuplicateIds)
                {
                    // This error indicates a bug
                    var index = indices[entity];
                    ModelState.AddModelError($"[{index}].{nameof(entity.Id)}", _localizer["Error_TheEntityWithId0IsSpecifiedMoreThanOnce", entity.Id]);
                }
            }

            // No need to invoke SQL if the model state is full of errors
            if (ModelState.HasReachedMaxErrors)
            {
                return;
            }

            // Perform SQL-side validation
            DataTable entitiesTable = ControllerUtilities.DataTable(entities, addIndex: true);
            var       entitiesTvp   = new SqlParameter("Entities", entitiesTable)
            {
                TypeName = $"dbo.{nameof(AgentForSave)}List", SqlDbType = SqlDbType.Structured
            };
            int remainingErrorCount = ModelState.MaxAllowedErrors - ModelState.ErrorCount;

            // (1) Code must be unique
            var sqlErrors = await _db.Validation.FromSql($@"
SET NOCOUNT ON;
	DECLARE @ValidationErrors [dbo].[ValidationErrorList];

    INSERT INTO @ValidationErrors([Key], [ErrorName])
    SELECT '[' + CAST([Id] AS NVARCHAR(255)) + '].Id' As [Key], N'Error_CannotModifyInactiveItem' As [ErrorName]
    FROM @Entities
    WHERE Id IN (SELECT Id from [dbo].[Custodies] WHERE IsActive = 0)
	OPTION(HASH JOIN);

    -- Non Null Ids must exist
    INSERT INTO @ValidationErrors([Key], [ErrorName], [Argument1])
    SELECT '[' + CAST([Id] AS NVARCHAR(255)) + '].Id' As [Key], N'Error_TheId0WasNotFound' As [ErrorName], CAST([Id] As NVARCHAR(255)) As [Argument1]
    FROM @Entities
    WHERE Id Is NOT NULL AND Id NOT IN (SELECT Id from [dbo].[Custodies] WHERE CustodyType = 'Agent' AND AgentType = '{agentType}')

	-- Code must be unique
	INSERT INTO @ValidationErrors([Key], [ErrorName], [Argument1], [Argument2], [Argument3], [Argument4], [Argument5]) 
	SELECT '[' + CAST(FE.[Index] AS NVARCHAR(255)) + '].Code' As [Key], N'Error_TheCode0IsUsed' As [ErrorName],
		FE.Code AS Argument1, NULL AS Argument2, NULL AS Argument3, NULL AS Argument4, NULL AS Argument5
	FROM @Entities FE 
	JOIN [dbo].[Custodies] BE ON FE.Code = BE.Code
	WHERE (FE.Id IS NULL) OR (FE.Id <> BE.Id);

SELECT TOP {remainingErrorCount} * FROM @ValidationErrors;
", entitiesTvp).ToListAsync();

            // Loop over the errors returned from SQL and add them to ModelState
            foreach (var sqlError in sqlErrors)
            {
                var formatArguments = sqlError.ToFormatArguments();

                string key          = sqlError.Key;
                string errorMessage = _localizer[sqlError.ErrorName, formatArguments];

                ModelState.AddModelError(key: key, errorMessage: errorMessage);
            }
        }
コード例 #22
0
ファイル: RolesController.cs プロジェクト: armohamm/BSharp
 protected override Expression ParseSpecialFilterKeyword(string keyword, ParameterExpression param)
 {
     return(ControllerUtilities.CreatedByMeFilter <M.Role>(keyword, param, _tenantInfoAccessor.GetCurrentInfo().UserId.Value));
 }
コード例 #23
0
ファイル: AgentsController.cs プロジェクト: crazyants/BSharp
        protected override async Task <(List <AgentForQuery>, IQueryable <AgentForQuery>)> PersistAsync(List <AgentForSave> entities, SaveArguments args)
        {
            // Some properties are always set to null for organizations
            string agentType = ViewId();

            if (agentType == ORGANIZATION)
            {
                entities.ForEach(e =>
                {
                    e.Title  = null;
                    e.Title2 = null;
                    e.Gender = null;
                });
            }

            // Add created entities
            DataTable entitiesTable = ControllerUtilities.DataTable(entities, addIndex: true);
            var       entitiesTvp   = new SqlParameter("Entities", entitiesTable)
            {
                TypeName  = $"dbo.{nameof(AgentForSave)}List",
                SqlDbType = SqlDbType.Structured
            };

            // The agent type
            var agentTypeParameter = new SqlParameter("AgentType", agentType);

            string saveSql = $@"
-- Procedure: AgentsForSave
SET NOCOUNT ON;
	DECLARE @IndexedIds [dbo].[IndexedIdList];
	DECLARE @TenantId int = CONVERT(INT, SESSION_CONTEXT(N'TenantId'));
	DECLARE @Now DATETIMEOFFSET(7) = SYSDATETIMEOFFSET();
    DECLARE @UserId INT = CONVERT(INT, SESSION_CONTEXT(N'UserId'));

-- Deletions
	DELETE FROM [dbo].[Custodies]
	WHERE [Id] IN (SELECT [Id] FROM @Entities WHERE [EntityState] = N'Deleted');

	INSERT INTO @IndexedIds([Index], [Id])
	SELECT x.[Index], x.[Id]
	FROM
	(
		MERGE INTO [dbo].[Custodies] AS t
		USING (
			SELECT [Index], [Id], [Name], [Name2], [Code], [Address], [BirthDateTime], [IsRelated], [TaxIdentificationNumber], [Title], [Title2], [Gender]
			FROM @Entities 
			WHERE [EntityState] IN (N'Inserted', N'Updated')
		) AS s ON (t.Id = s.Id)
		WHEN MATCHED
		THEN
			UPDATE SET 
				t.[Name]			        = s.[Name],
				t.[Name2]			        = s.[Name2],
				t.[Code]			        = s.[Code],
				t.[Address]			        = s.[Address],
				t.[BirthDateTime]	        = s.[BirthDateTime],
			    t.[IsRelated]				= s.[IsRelated],
			    t.[TaxIdentificationNumber] = s.[TaxIdentificationNumber],
			    t.[Title]					= s.[Title],
			    t.[Title2]					= s.[Title2],
			    t.[Gender]					= s.[Gender],
				t.[ModifiedAt]		        = @Now,
				t.[ModifiedById]		        = @UserId
		WHEN NOT MATCHED THEN
			INSERT ([TenantId], [CustodyType], [Name], [Name2], [Code], [Address], [BirthDateTime], [AgentType], [IsRelated], [TaxIdentificationNumber], [Title], [Title2], [Gender], [CreatedAt], [CreatedById], [ModifiedAt], [ModifiedById])
			VALUES (@TenantId, 'Agent', s.[Name], s.[Name2], s.[Code], s.[Address], s.[BirthDateTime], @AgentType, s.[IsRelated], s.[TaxIdentificationNumber], s.[Title], [Title2], s.[Gender], @Now, @UserId, @Now, @UserId)
		OUTPUT s.[Index], inserted.[Id] 
	) AS x;
";

            // Optimization
            if (!(args.ReturnEntities ?? false))
            {
                // IF no returned items are expected, simply execute a non-Query and return an empty list;
                await _db.Database.ExecuteSqlCommandAsync(saveSql, entitiesTvp, agentTypeParameter);

                return(new List <AgentForQuery>(), null);
            }
            else
            {
                // If returned items are expected, append a select statement to the SQL command
                saveSql = saveSql += "SELECT * FROM @IndexedIds;";

                // Retrieve the map from Indexes to Ids
                var indexedIds = await _db.Saving.FromSql(saveSql, entitiesTvp, agentTypeParameter).ToListAsync();

                //// Load the entities using their Ids
                //DataTable idsTable = DataTable(indexedIds.Select(e => new { e.Id }), addIndex: false);
                //var idsTvp = new SqlParameter("Ids", idsTable)
                //{
                //    TypeName = $"dbo.IdList",
                //    SqlDbType = SqlDbType.Structured
                //};

                // var q = _db.Agents.FromSql("SELECT * FROM dbo.[Custodies] WHERE Id IN (SELECT Id FROM @Ids)", idsTvp);
                var ids = indexedIds.Select(e => e.Id);
                var q   = _db.VW_Agents.Where(e => ids.Contains(e.Id.Value));
                q = Expand(q, args.Expand);
                var savedEntities = await q.ToListAsync();


                // SQL Server does not guarantee order, so make sure the result is sorted according to the initial index
                Dictionary <int, int> indices = indexedIds.ToDictionary(e => e.Id, e => e.Index);
                var sortedSavedEntities       = new AgentForQuery[savedEntities.Count];
                foreach (var item in savedEntities)
                {
                    int index = indices[item.Id.Value];
                    sortedSavedEntities[index] = item;
                }

                // Return the sorted collection
                return(sortedSavedEntities.ToList(), q);
            }
        }
コード例 #24
0
ファイル: Controller.Core.cs プロジェクト: Ashrafnet/XIOT
        public static string GetInsertView(string controller)
        {
            ControllerUtilities c = new ControllerUtilities();

            return(c.GetActionView(controller, "createForm1", "Insert"));
        }
コード例 #25
0
ファイル: AgentsController.cs プロジェクト: crazyants/BSharp
 public async Task <ActionResult <EntitiesResponse <Agent> > > Deactivate([FromBody] List <int> ids, [FromQuery] DeactivateArguments <int> args)
 {
     return(await ControllerUtilities.ExecuteAndHandleErrorsAsync(() =>
                                                                  ActivateDeactivate(ids, args.ReturnEntities ?? false, args.Expand, isActive : false)
                                                                  , _logger));
 }
コード例 #26
0
        public async Task <ActionResult <ProjectTask> > Get(int id)
        {
            var result = await _taskRepository.Get(id);

            return(ControllerUtilities.CheckResult(result));
        }
コード例 #27
0
ファイル: AgentsController.cs プロジェクト: crazyants/BSharp
        protected override async Task <(List <AgentForSave>, Func <string, int?>)> ToDtosForSave(AbstractDataGrid grid, ParseArguments args)
        {
            // Get the properties of the DTO for Save, excluding Id or EntityState
            string mode            = args.Mode;
            var    readType        = typeof(Agent);
            var    custodySaveType = typeof(CustodyForSave);
            var    agentSaveType   = typeof(AgentForSave);

            var readProps = readType.GetProperties(BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly)
                            .ToDictionary(prop => _metadataProvider.GetMetadataForProperty(readType, prop.Name)?.DisplayName ?? prop.Name, StringComparer.InvariantCultureIgnoreCase);

            var orgExemptProperties = new string[] { nameof(Agent.Title), nameof(Agent.Title2), nameof(Agent.Gender) };

            var saveProps = custodySaveType.GetProperties(BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly)
                            .Union(agentSaveType.GetProperties(BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly))
                            .Where(e => ViewId() == INDIVIDUAL || orgExemptProperties.Contains(e.Name)) // Take away
                            .ToDictionary(prop => _metadataProvider.GetMetadataForProperty(agentSaveType, prop.Name)?.DisplayName ?? prop.Name, StringComparer.InvariantCultureIgnoreCase);

            // Maps the index of the grid column to a property on the DtoForSave
            var saveColumnMap = new List <(int Index, PropertyInfo Property)>(grid.RowSize);

            // Make sure all column header labels are recognizable
            // and construct the save column map
            var firstRow = grid[0];

            for (int c = 0; c < firstRow.Length; c++)
            {
                var    column      = firstRow[c];
                string headerLabel = column.Content?.ToString();

                // So any thing after an empty column is ignored
                if (string.IsNullOrWhiteSpace(headerLabel))
                {
                    break;
                }

                if (saveProps.ContainsKey(headerLabel))
                {
                    var prop = saveProps[headerLabel];
                    saveColumnMap.Add((c, prop));
                }
                else if (readProps.ContainsKey(headerLabel))
                {
                    // All good, just ignore
                }
                else
                {
                    AddRowError(1, _localizer["Error_Column0NotRecognizable", headerLabel]);
                }
            }

            // Milestone 1: columns in the abstract grid mapped
            if (!ModelState.IsValid)
            {
                throw new UnprocessableEntityException(ModelState);
            }

            // Construct the result using the map generated earlier
            List <AgentForSave> result = new List <AgentForSave>(grid.Count - 1);

            for (int i = 1; i < grid.Count; i++) // Skip the header
            {
                var row = grid[i];

                // Anything after an empty row is ignored
                if (saveColumnMap.All((p) => string.IsNullOrWhiteSpace(row[p.Index].Content?.ToString())))
                {
                    break;
                }

                var entity = new AgentForSave();
                foreach (var(index, prop) in saveColumnMap)
                {
                    var content  = row[index].Content;
                    var propName = _metadataProvider.GetMetadataForProperty(readType, prop.Name).DisplayName;

                    // Special handling for choice lists
                    if (content != null)
                    {
                        var choiceListAttr = prop.GetCustomAttribute <ChoiceListAttribute>();
                        if (choiceListAttr != null)
                        {
                            List <string> displayNames     = choiceListAttr.DisplayNames.Select(e => _localizer[e].Value).ToList();
                            string        stringContent    = content.ToString();
                            var           displayNameIndex = displayNames.IndexOf(stringContent);
                            if (displayNameIndex == -1)
                            {
                                string seperator = _localizer[", "];
                                AddRowError(i + 1, _localizer["Error_Value0IsNotValidFor1AcceptableValuesAre2", stringContent, propName, string.Join(seperator, displayNames)]);
                            }
                            else
                            {
                                content = choiceListAttr.Choices[displayNameIndex];
                            }
                        }
                    }

                    // Special handling for DateTime and DateTimeOffset
                    if (prop.PropertyType.IsDateOrTime())
                    {
                        try
                        {
                            var date = ParseImportedDateTime(content);
                            content = date;

                            if (prop.PropertyType.IsDateTimeOffset())
                            {
                                content = AddUserTimeZone(date);
                            }
                        }
                        catch (Exception)
                        {
                            AddRowError(i + 1, _localizer["Error_TheValue0IsNotValidFor1Field", content?.ToString(), propName]);
                        }
                    }

                    // Try setting the value and return an error if it doesn't work
                    try
                    {
                        prop.SetValue(entity, content);
                    }
                    catch (ArgumentException)
                    {
                        AddRowError(i + 1, _localizer["Error_TheValue0IsNotValidFor1Field", content?.ToString(), propName]);
                    }
                }

                result.Add(entity);
            }

            // Milestone 2: DTOs created
            if (!ModelState.IsValid)
            {
                throw new UnprocessableEntityException(ModelState);
            }

            // Prepare a dictionary of indices in order to construct any validation errors performantly
            // "IndexOf" is O(n), this brings it down to O(1)
            Dictionary <AgentForSave, int> indicesDic = result.ToIndexDictionary();

            // For each entity, set the Id and EntityState depending on import mode
            if (mode == "Insert")
            {
                // For Insert mode, all are marked inserted and all Ids are null
                // Any duplicate codes will be handled later in the validation
                result.ForEach(e => e.Id          = null);
                result.ForEach(e => e.EntityState = EntityStates.Inserted);
            }
            else
            {
                // For all other modes besides Insert, we need to match the entity codes to Ids by querying the DB
                // Load the code Ids from the database
                var nonNullCodes   = result.Where(e => !string.IsNullOrWhiteSpace(e.Code));
                var codesDataTable = ControllerUtilities.DataTable(nonNullCodes.Select(e => new { e.Code }));
                var entitiesTvp    = new SqlParameter("@Codes", codesDataTable)
                {
                    TypeName  = $"dbo.CodeList",
                    SqlDbType = SqlDbType.Structured
                };

                string agentType = ViewId();

                var idCodesDic = await _db.CodeIds.FromSql(
                    $@"SELECT c.Code, e.Id FROM @Codes c JOIN [dbo].[Custodies] e ON c.Code = e.Code WHERE e.CustodyType = 'Agent' && e.AgentType == {agentType};"
                    , entitiesTvp).ToDictionaryAsync(e => e.Code, e => e.Id);

                result.ForEach(e =>
                {
                    if (!string.IsNullOrWhiteSpace(e.Code) && idCodesDic.ContainsKey(e.Code))
                    {
                        e.Id = idCodesDic[e.Code];
                    }
                    else
                    {
                        e.Id = null;
                    }
                });

                // Make sure no codes are mentioned twice, if we don't do it here, the save validation later will complain
                // about duplicated Id, but the error will not be clear since user deals with code while importing from Excel
                var duplicateIdGroups = result.Where(e => e.Id != null).GroupBy(e => e.Id.Value).Where(g => g.Count() > 1);
                foreach (var duplicateIdGroup in duplicateIdGroups)
                {
                    foreach (var entity in duplicateIdGroup)
                    {
                        int index = indicesDic[entity];
                        AddRowError(index + 2, _localizer["Error_TheCode0IsDuplicated", entity.Code]);
                    }
                }

                if (mode == "Merge")
                {
                    // Merge simply inserts codes that are not found, and updates codes that are found
                    result.ForEach(e =>
                    {
                        if (e.Id != null)
                        {
                            e.EntityState = EntityStates.Updated;
                        }
                        else
                        {
                            e.EntityState = EntityStates.Inserted;
                        }
                    });
                }
                else
                {
                    // In the case of update: codes are required, and MUST match database Ids
                    if (mode == "Update")
                    {
                        for (int index = 0; index < result.Count; index++)
                        {
                            var entity = result[index];
                            if (string.IsNullOrWhiteSpace(entity.Code))
                            {
                                AddRowError(index + 2, _localizer["Error_CodeIsRequiredForImportModeUpdate"]);
                            }
                            else if (entity.Id == null)
                            {
                                AddRowError(index + 2, _localizer["Error_TheCode0DoesNotExist", entity.Code]);
                            }
                        }

                        result.ForEach(e => e.EntityState = EntityStates.Updated);
                    }
                    else
                    {
                        throw new InvalidOperationException("Unknown save mode"); // Developer bug
                    }
                }
            }

            // Milestone 3: Id and EntityState are set
            if (!ModelState.IsValid)
            {
                throw new UnprocessableEntityException(ModelState);
            }

            // Function that maps any future validation errors back to specific rows
            int?errorKeyMap(string key)
            {
                int?rowNumber = null;

                if (key != null && key.StartsWith("["))
                {
                    var indexStr = key.TrimStart('[').Split(']')[0];
                    if (int.TryParse(indexStr, out int index))
                    {
                        // Add 2:
                        // 1 for the header in the abstract grid
                        // 1 for the difference between index and number
                        rowNumber = index + 2;
                    }
                }
                return(rowNumber);
            }

            return(result, errorKeyMap);
        }
コード例 #28
0
ファイル: Controller.Core.cs プロジェクト: mehedi09/GridWork
 public static string GetInsertView(string controller)
 {
     ControllerUtilities c = new ControllerUtilities();
     return c.GetActionView(controller, "createForm1", "Insert");
 }
コード例 #29
0
ファイル: AgentsController.cs プロジェクト: crazyants/BSharp
        private async Task <ActionResult <EntitiesResponse <Agent> > > ActivateDeactivate([FromBody] List <int> ids, bool returnEntities, string expand, bool isActive)
        {
            await CheckActionPermissions(ids.Cast <int?>());

            var isActiveParam = new SqlParameter("@IsActive", isActive);

            DataTable idsTable = ControllerUtilities.DataTable(ids.Select(id => new { Id = id }), addIndex: false);
            var       idsTvp   = new SqlParameter("@Ids", idsTable)
            {
                TypeName  = $"dbo.IdList",
                SqlDbType = SqlDbType.Structured
            };

            string sql = @"
DECLARE @Now DATETIMEOFFSET(7) = SYSDATETIMEOFFSET();
DECLARE @UserId INT = CONVERT(INT, SESSION_CONTEXT(N'UserId'));

MERGE INTO [dbo].[Custodies] AS t
	USING (
		SELECT [Id]
		FROM @Ids
	) AS s ON (t.Id = s.Id)
	WHEN MATCHED AND (t.IsActive <> @IsActive)
	THEN
		UPDATE SET 
			t.[IsActive]	= @IsActive,
			t.[ModifiedAt]	= @Now,
			t.[ModifiedById]	= @UserId;
";

            using (var trx = await _db.Database.BeginTransactionAsync())
            {
                try
                {
                    // Update the entities
                    await _db.Database.ExecuteSqlCommandAsync(sql, idsTvp, isActiveParam);

                    trx.Commit();
                }
                catch (Exception ex)
                {
                    trx.Rollback();
                    throw ex;
                }
            }

            // Determine whether entities should be returned
            if (!returnEntities)
            {
                // IF no returned items are expected, simply return 200 OK
                return(Ok());
            }
            else
            {
                // Load the entities using their Ids
                var affectedDbEntitiesQ         = _db.VW_Agents.Where(e => ids.Contains(e.Id.Value)); //.FromSql("SELECT * FROM [dbo].[Custodies] WHERE Id IN (SELECT Id FROM @Ids)", idsTvp);
                var affectedDbEntitiesExpandedQ = Expand(affectedDbEntitiesQ, expand);
                var affectedDbEntities          = await affectedDbEntitiesExpandedQ.ToListAsync();

                // Add the metadata
                ApplySelectAndAddMetadata(affectedDbEntities, expand, null);

                // sort the entities the way their Ids came, as a good practice
                var affectedEntities = Mapper.Map <List <Agent> >(affectedDbEntities);

                Agent[] sortedAffectedEntities = new Agent[ids.Count];
                Dictionary <int, Agent> affectedEntitiesDic = affectedEntities.ToDictionary(e => e.Id.Value);
                for (int i = 0; i < ids.Count; i++)
                {
                    var   id     = ids[i];
                    Agent entity = null;
                    if (affectedEntitiesDic.ContainsKey(id))
                    {
                        entity = affectedEntitiesDic[id];
                    }

                    sortedAffectedEntities[i] = entity;
                }

                // Apply the permission masks (setting restricted fields to null) and adjust the metadata accordingly
                await ApplyReadPermissionsMask(affectedDbEntities, affectedDbEntitiesExpandedQ, await UserPermissions(PermissionLevel.Read), GetDefaultMask());

                // Flatten related entities and map each to its respective DTO
                var relatedEntities = FlattenRelatedEntitiesAndTrim(affectedDbEntities, expand);

                // Prepare a proper response
                var response = new EntitiesResponse <Agent>
                {
                    Data            = sortedAffectedEntities,
                    CollectionName  = GetCollectionName(typeof(Agent)),
                    RelatedEntities = relatedEntities
                };

                // Commit and return
                return(Ok(response));
            }
        }
コード例 #30
0
ファイル: Controller.Core.cs プロジェクト: mehedi09/GridWork
 public static string GetUpdateView(string controller)
 {
     ControllerUtilities c = new ControllerUtilities();
     return c.GetActionView(controller, "editForm1", "Update");
 }
コード例 #31
0
        private async Task <GetByIdResponse <User> > SaveMyUserImpl([FromBody] MyUserForSave me)
        {
            int myId = _appRepo.GetUserInfo().UserId.Value;
            var user = await _appRepo.Users.Expand("Roles").FilterByIds(myId).FirstOrDefaultAsync();

            // Create a user for save
            var userForSave = new UserForSave
            {
                Id                = user.Id,
                Email             = user.Email,
                Name              = me.Name?.Trim(),
                Name2             = me.Name2?.Trim(),
                Name3             = me.Name3?.Trim(),
                PreferredLanguage = me.PreferredLanguage?.Trim(),
                Image             = me.Image,
                EntityMetadata    = new EntityMetadata
                {
                    [nameof(UserForSave.Id)]                = FieldMetadata.Loaded,
                    [nameof(UserForSave.Email)]             = FieldMetadata.Loaded,
                    [nameof(UserForSave.Name)]              = FieldMetadata.Loaded,
                    [nameof(UserForSave.Name2)]             = FieldMetadata.Loaded,
                    [nameof(UserForSave.Name3)]             = FieldMetadata.Loaded,
                    [nameof(UserForSave.PreferredLanguage)] = FieldMetadata.Loaded,
                    [nameof(UserForSave.Image)]             = FieldMetadata.Loaded
                },

                // The roles must remain the way they are
                Roles = user.Roles?.Select(e => new RoleMembershipForSave
                {
                    Id             = e.Id,
                    Memo           = e.Memo,
                    RoleId         = e.RoleId,
                    UserId         = e.UserId,
                    EntityMetadata = new EntityMetadata
                    {
                        [nameof(RoleMembershipForSave.Id)]     = FieldMetadata.Loaded,
                        [nameof(RoleMembershipForSave.Memo)]   = FieldMetadata.Loaded,
                        [nameof(RoleMembershipForSave.RoleId)] = FieldMetadata.Loaded,
                        [nameof(RoleMembershipForSave.UserId)] = FieldMetadata.Loaded
                    },
                })
                        .ToList()
            };

            var entities = new List <UserForSave>()
            {
                userForSave
            };

            // Start a transaction scope for save since it causes data modifications
            using var trx = ControllerUtilities.CreateTransaction(null, GetSaveTransactionOptions());

            // Validation
            await SaveValidateAsync(entities);

            if (!ModelState.IsValid)
            {
                // TODO map the errors
                throw new UnprocessableEntityException(ModelState);
            }

            // Save and retrieve response
            await SaveExecuteAsync(entities, null, false);

            var response = await GetMyUserImpl();

            // Commit and return
            trx.Complete();
            return(response);
        }