public async Task DeleteFieldOfInterestInSQLiteMemory()
        {
            // the SQLite in-memory database only exists while the connection is open
            ContextHelper.OpenSQLiteInMemory();

            try
            {
                // Arrange the test against one instance of the context (name the database to match the method)
                using (var context = ContextHelper.GetSQLiteInMemoryContext())
                {
                    await SeedContextWithFieldsOfInterest(context);

                    var view = new FieldOfInterestView()
                    {
                        FieldOfInterestId = "01"
                    };
                    var repo = new FieldOfInterestViewRepository(context);

                    // Act
                    await repo.DeleteAsync(view);
                }

                // Assert a separate instance of the context to verify correct data was updated in the named database
                using (var context = ContextHelper.GetSQLiteInMemoryContext())
                {
                    var field = context.Find <FieldOfInterest>("01");
                    Assert.IsNull(field, "The field of interest 01 should be deleted");

                    var fieldDescription = context.Find <FieldOfInterestDescription>("01", "NL");
                    Assert.IsNull(fieldDescription, "The field of interest 01 description NL should be deleted");

                    fieldDescription = context.Find <FieldOfInterestDescription>("01", "EN");
                    Assert.IsNull(fieldDescription, "The field of interest 01 description EN should be deleted");

                    field = context.Find <FieldOfInterest>("02");
                    Assert.AreEqual(context.Entry(field).State, EntityState.Unchanged, "The field of interest 02 should be marked unchanged");

                    fieldDescription = context.Find <FieldOfInterestDescription>("02", "NL");
                    Assert.AreEqual(context.Entry(fieldDescription).State, EntityState.Unchanged, "The field of interest 02 description NL should be marked unchanged");

                    fieldDescription = context.Find <FieldOfInterestDescription>("02", "EN");
                    Assert.AreEqual(context.Entry(fieldDescription).State, EntityState.Unchanged, "The field of interest 02 description EN should be marked unchanged");
                }
            }
            finally
            {
                ContextHelper.CloseSQLiteInMemory();
            }
        }
        public async Task <IActionResult> Create([Bind(nameof(FieldOfInterestView.FieldOfInterestId),
                                                       nameof(FieldOfInterestView.Description),
                                                       nameof(FieldOfInterestView.LanguageId))] FieldOfInterestView field)
        {
            // TryValidateModel(field);    // only needed if changes were made since binding

            if (_repo.CreateValid(field) && ModelState.IsValid)
            {
                try
                {
                    await _repo.CreateAsync(field);

                    return(RedirectToAction(nameof(Index)));
                }
                catch (DbUpdateException) // includes DbUpdateConcurrencyException
                {
                    ModelState.AddModelError("", ErrorMessage.DatabaseSaveError);
                }
            }

            return(View(field));
        }
        public async Task <IActionResult> Delete(string id, byte[] rowVersion, [Bind(nameof(FieldOfInterestView.FieldOfInterestId))] FieldOfInterestView field)
        {
            if (id == null || id != field.FieldOfInterestId)  // url id does not match hidden form field, then something's fishy
            {
                TempData["RedirectMessage"] = String.Format(ErrorMessage.RecordIdError, ErrorMessage.FieldOfInterestId.ToLower());
                return(RedirectToAction(nameof(Index)));
            }

            bool concurrencyError = false;
            bool fieldDeleted     = false;
            var  fieldToDelete    = await _repo.ReadAsync(id);

            if (fieldToDelete == null)
            {
                fieldDeleted = true;
            }

            if (!fieldDeleted)
            {
                if (!fieldToDelete.RowVersion.SequenceEqual(rowVersion))  // use this method to check equality on element level (not ==)
                {
                    concurrencyError = true;
                }
                else
                {
                    try
                    {
                        await _repo.DeleteAsync(fieldToDelete);

                        return(RedirectToAction(nameof(Index)));
                    }
                    catch (DbUpdateConcurrencyException)
                    {
                        if (await _repo.ExistAsync(id))
                        {
                            concurrencyError = true;
                        }
                        else
                        {
                            fieldDeleted = true;
                        }
                    }
                    catch (DbUpdateException) // parent of DbUpdateConcurrencyException
                    {
                        ViewData["ErrorMessage"] = ErrorMessage.DatabaseSaveError;
                    }
                }
            }

            if (fieldDeleted)
            {
                return(RedirectToAction(nameof(Index)));  // no longer available, so deleted, so just act as if ok
            }

            if (concurrencyError)
            {
                // Delete does not set modelstate, so the error is set as a generic parameter which is added and displayed in the html page
                ViewData["ErrorMessage"] = ErrorMessage.DatabaseDeleteConcurrencyError;

                // remove field from modelstate since it contains the old value and modelstate takes precendence over the model property if both are present
                ModelState.Remove("RowVersion");
            }

            return(View(fieldToDelete));
        }
        // Parameter field is bound, but initializes all other fields, so do not use field object for actual update
        public async Task <IActionResult> Edit(string id, byte[] rowVersion, [Bind(nameof(FieldOfInterestView.FieldOfInterestId),
                                                                                   nameof(FieldOfInterestView.Description),
                                                                                   nameof(FieldOfInterestView.LanguageId))] FieldOfInterestView field)
        {
            if (id == null || id != field.FieldOfInterestId)  // url id does not match hidden form field, then something's fishy
            {
                TempData["RedirectMessage"] = String.Format(ErrorMessage.RecordIdError, ErrorMessage.FieldOfInterestId.ToLower());
                return(RedirectToAction(nameof(Index)));
            }

            bool concurrencyError = false;
            bool fieldDeleted     = false;
            var  fieldToUpdate    = await _repo.ReadAsync(id);

            if (fieldToUpdate == null)
            {
                fieldDeleted  = true;
                fieldToUpdate = new FieldOfInterestView();
            }

            // also update model when deleted to show the data the user entered back to the user
            await TryUpdateModelAsync <FieldOfInterestView>(fieldToUpdate, "",
                                                            x => x.FieldOfInterestId,
                                                            x => x.Description,
                                                            x => x.LanguageId);

            if (!fieldDeleted && ModelState.IsValid)
            {
                if (!fieldToUpdate.RowVersion.SequenceEqual(rowVersion))   // use this method to check equality on element level (not ==)
                {
                    concurrencyError = true;
                }
                else
                {
                    try
                    {
                        await _repo.UpdateAsync(fieldToUpdate);

                        return(RedirectToAction(nameof(Index)));
                    }
                    catch (DbUpdateConcurrencyException)
                    {
                        if (await _repo.ExistAsync(id))
                        {
                            concurrencyError = true;
                        }
                        else
                        {
                            fieldDeleted = true;
                        }
                    }
                    catch (DbUpdateException) // parent of DbUpdateConcurrencyException
                    {
                        ModelState.AddModelError("", ErrorMessage.DatabaseSaveError);
                    }
                }
            }

            if (fieldDeleted)
            {
                ModelState.AddModelError("", ErrorMessage.DatabaseUpdateDeletedError);
            }

            if (concurrencyError)
            {
                var fieldInDatabase = await _repo.RetrieveOriginalValuesAsync(fieldToUpdate);

                // TODO: retrieve error message from page, size save might not be called Save
                ModelState.AddModelError("", ErrorMessage.DatabaseUpdateConcurrencyError);

                // Next lines are to make it possible to press save again and save it anyways (unless another
                // concurrency has occured in the meantime). This is one way to handle these situations, but
                // could also opt to not allow updates until new data is loaded or redirect the user directly
                // back to the index page with a warning. If this methodology is choosen, then a good practice
                // is to retrieve and show the changed database values as done below.
                // It depends on the entity how much trouble to put into this nice processing or if it is even
                // possible without reloading all data.

                if (fieldToUpdate.Description != fieldInDatabase.Description)
                {
                    ModelState.AddModelError(nameof(fieldToUpdate.Description), String.Format(ErrorMessage.DatabaseCurrentValue, fieldInDatabase.Description));
                }

                // set rowversion to current version so pressing Save button again WILL save the data
                fieldToUpdate.RowVersion = fieldInDatabase.RowVersion;
                // remove field from modelstate since it contains the old value and modelstate takes precendence over the model property if both are present
                ModelState.Remove("RowVersion");
            }

            return(View(fieldToUpdate));
        }