public ActionResult SaveRecipe(PostedRecipeViewModel postedRecipeViewModel) { // NOTE: This action handles saving for both new recipes // and edited recipes. New Recipes post to this action while // edits post to this action via Ajax. var isNewRecipe = false; // Hydrate JSON ReceipeViewModel var recipeViewModel = postedRecipeViewModel.HydrateRecipeJson(); isNewRecipe = recipeViewModel.IsNewRecipe(); // Validation (client validates also ... this to ensure data consistency) var validator = new RecipeViewModelValidator(); var validationResult = validator.Validate(recipeViewModel); if (!validationResult.IsValid) { if (isNewRecipe) { this.AppendMessage(new ErrorMessage { Text = "Did you leave something blank? Please check your entries and try again." }); ViewBag.RecipeCreationOptions = this.RecipeService.GetRecipeCreationOptions(); return(this.View("NewRecipe", recipeViewModel)); } // Signals Invalid return(this.Content("0")); } using (var unitOfWork = this.UnitOfWorkFactory.NewUnitOfWork()) { try { var recipe = recipeViewModel.IsNewRecipe() ? new Recipe() : this.RecipeService.GetRecipeById(recipeViewModel.RecipeId); // Issue 404 if recipe does not exists or not owned by user if (!isNewRecipe && !recipe.WasCreatedBy(this.ActiveUser.UserId)) { return(this.Issue404()); } // Map Recipe Mapper.Map(recipeViewModel, recipe); #region INGREDIENT DELETIONS if (!isNewRecipe) { // Delete Fermentables that were removed var fermentablesForDeletion = recipe.Fermentables.Except(recipe.Fermentables.Join(recipeViewModel.Fermentables ?? new List <RecipeFermentableViewModel>(), x => x.RecipeFermentableId, y => Converter.Convert <int>(y.Id), (x, y) => x)).ToList(); this.RecipeService.MarkRecipeIngredientsForDeletion <RecipeFermentable>(fermentablesForDeletion); // Delete Hops that were removed var hopsForDeletion = recipe.Hops.Except(recipe.Hops.Join(recipeViewModel.Hops ?? new List <RecipeHopViewModel>(), x => x.RecipeHopId, y => Converter.Convert <int>(y.Id), (x, y) => x)).ToList(); this.RecipeService.MarkRecipeIngredientsForDeletion <RecipeHop>(hopsForDeletion); // Delete Yeasts that were removed var yeastsForDeletion = recipe.Yeasts.Except(recipe.Yeasts.Join(recipeViewModel.Yeasts ?? new List <RecipeYeastViewModel>(), x => x.RecipeYeastId, y => Converter.Convert <int>(y.Id), (x, y) => x)).ToList(); this.RecipeService.MarkRecipeIngredientsForDeletion <RecipeYeast>(yeastsForDeletion); // Delete Adjuncts that were removed var adjunctsForDeletion = recipe.Adjuncts.Except(recipe.Adjuncts.Join(recipeViewModel.Others ?? new List <RecipeOtherViewModel>(), x => x.RecipeAdjunctId, y => Converter.Convert <int>(y.Id), (x, y) => x)).ToList(); this.RecipeService.MarkRecipeIngredientsForDeletion <RecipeAdjunct>(adjunctsForDeletion); // Delete MashSteps that were removed var mashStepsForDeletion = recipe.MashSteps.Except(recipe.MashSteps.Join(recipeViewModel.MashSteps ?? new List <RecipeMashStepViewModel>(), x => x.RecipeMashStepId, y => Converter.Convert <int>(y.Id), (x, y) => x)).ToList(); this.RecipeService.MarkRecipeIngredientsForDeletion <RecipeMashStep>(mashStepsForDeletion); } #endregion #region INGREDIENT ADDITIONS // Add Fermentables if (recipeViewModel.Fermentables != null) { recipeViewModel.Fermentables.Where(x => Converter.Convert <int>(x.Id) == 0) .ForEach(x => recipe.Fermentables.Add(Mapper.Map(x, new RecipeFermentable()))); } // Add Hops if (recipeViewModel.Hops != null) { recipeViewModel.Hops.Where(x => Converter.Convert <int>(x.Id) == 0) .ForEach(x => recipe.Hops.Add(Mapper.Map(x, new RecipeHop()))); } // Add Yeasts if (recipeViewModel.Yeasts != null) { recipeViewModel.Yeasts.Where(x => Converter.Convert <int>(x.Id) == 0) .ForEach(x => recipe.Yeasts.Add(Mapper.Map(x, new RecipeYeast()))); } // Add Adjuncts if (recipeViewModel.Others != null) { recipeViewModel.Others.Where(x => Converter.Convert <int>(x.Id) == 0) .ForEach(x => recipe.Adjuncts.Add(Mapper.Map(x, new RecipeAdjunct()))); } // Add MashStep if (recipeViewModel.MashSteps != null) { recipeViewModel.MashSteps.Where(x => Converter.Convert <int>(x.Id) == 0) .ForEach(x => recipe.MashSteps.Add(Mapper.Map(x, new RecipeMashStep()))); } #endregion #region INGREDIENT UPDATES if (!isNewRecipe) { // Update Fermentables if (recipeViewModel.Fermentables != null) { foreach (var recipeFermentableViewModel in recipeViewModel.Fermentables.Where(x => Converter.Convert <int>(x.Id) > 0)) { var match = recipe.Fermentables.FirstOrDefault(x => x.RecipeFermentableId == Converter.Convert <int>(recipeFermentableViewModel.Id)); if (match == null) { throw new InvalidOperationException("Unable to find matching fermentable"); } Mapper.Map(recipeFermentableViewModel, match); } } // Update Hops if (recipeViewModel.Hops != null) { foreach (var recipeHopViewModel in recipeViewModel.Hops.Where(x => Converter.Convert <int>(x.Id) > 0)) { var match = recipe.Hops.FirstOrDefault(x => x.RecipeHopId == Converter.Convert <int>(recipeHopViewModel.Id)); if (match == null) { throw new InvalidOperationException("Unable to find matching Hop"); } Mapper.Map(recipeHopViewModel, match); } } // Update Yeasts if (recipeViewModel.Yeasts != null) { foreach (var recipeYeastViewModel in recipeViewModel.Yeasts.Where(x => Converter.Convert <int>(x.Id) > 0)) { var match = recipe.Yeasts.FirstOrDefault(x => x.RecipeYeastId == Converter.Convert <int>(recipeYeastViewModel.Id)); if (match == null) { throw new InvalidOperationException("Unable to find matching Yeast"); } Mapper.Map(recipeYeastViewModel, match); } } // Update Adjuncts if (recipeViewModel.Others != null) { foreach (var recipeAdjunctViewModel in recipeViewModel.Others.Where(x => Converter.Convert <int>(x.Id) > 0)) { var match = recipe.Adjuncts.FirstOrDefault(x => x.RecipeAdjunctId == Converter.Convert <int>(recipeAdjunctViewModel.Id)); if (match == null) { throw new InvalidOperationException("Unable to find matching Adjunct"); } Mapper.Map(recipeAdjunctViewModel, match); } } // Update MashSteps if (recipeViewModel.MashSteps != null) { foreach (var recipeMashStepViewModel in recipeViewModel.MashSteps.Where(x => Converter.Convert <int>(x.Id) > 0)) { var match = recipe.MashSteps.FirstOrDefault(x => x.RecipeMashStepId == Converter.Convert <int>(recipeMashStepViewModel.Id)); if (match == null) { throw new InvalidOperationException("Unable to find matching MashStep"); } Mapper.Map(recipeMashStepViewModel, match); } } } #endregion #region STEP DELETIONS / ADDITIONS / UPDATES // Deletions if (!isNewRecipe) { var stepsForDeletion = recipe.Steps.Except(recipe.Steps.Join(recipeViewModel.Steps ?? new List <RecipeStepViewModel>(), x => x.RecipeStepId, y => Converter.Convert <int>(y.Id), (x, y) => x)).ToList(); this.RecipeService.MarkRecipeStepsForDeletion(stepsForDeletion); } // Additions if (recipeViewModel.Steps != null) { if (recipe.Steps == null) { recipe.Steps = new List <RecipeStep>(); } recipeViewModel.GetSteps() .Where(x => Converter.Convert <int>(x.Id) == 0) .Where(x => !string.IsNullOrWhiteSpace(x.Text)) .ForEach(x => recipe.Steps.Add(Mapper.Map(x, new RecipeStep { DateCreated = DateTime.Now }))); } // Updates if (!isNewRecipe) { if (recipeViewModel.Steps != null) { foreach (var recipeStep in recipeViewModel.GetSteps() .Where(x => Converter.Convert <int>(x.Id) > 0) .Where(x => !string.IsNullOrWhiteSpace(x.Text))) { var match = recipe.Steps.FirstOrDefault(x => x.RecipeStepId == Converter.Convert <int>(recipeStep.Id)); if (match == null) { throw new InvalidOperationException("Unable to find matching step"); } match = Mapper.Map(recipeStep, match); match.DateModified = DateTime.Now; } } } #endregion // Save the Image if (isNewRecipe) { if (recipeViewModel.PhotoForUpload != null) { // Save the New Image recipe.ImageUrlRoot = this.StaticContentService.SaveRecipeImage(recipeViewModel.PhotoForUpload.InputStream, this.WebSettings.MediaPhysicalRoot); } } // Finalize Recipe this.RecipeService.FinalizeRecipe(recipe); unitOfWork.Commit(); if (isNewRecipe) { this.ForwardMessage(new SuccessMessage { Text = BrewgrMessages.RecipeSaved }); return(Redirect(Url.RecipeEditUrl(recipe.RecipeId))); } else { // Signals Success return(Content("1")); } } catch (Exception ex) { this.LogHandledException(ex); unitOfWork.Rollback(); if (isNewRecipe) { ViewBag.RecipeCreationOptions = this.RecipeService.GetRecipeCreationOptions(); this.AppendMessage(new ErrorMessage { Text = GenericMessages.ErrorMessage }); return(View("NewRecipe", recipeViewModel)); } else { // Signals Failure return(Content("-1")); } } } }
public ActionResult SaveRecipe(PostedRecipeViewModel postedRecipeViewModel) { // NOTE: This action handles saving for both new recipes // and edited recipes. New Recipes post to this action while // edits post to this action via Ajax. var isNewRecipe = false; // Hydrate JSON ReceipeViewModel var recipeViewModel = postedRecipeViewModel.HydrateRecipeJson(); isNewRecipe = recipeViewModel.IsNewRecipe(); // Validation (client validates also ... this to ensure data consistency) var validator = new RecipeViewModelValidator(); var validationResult = validator.Validate(recipeViewModel); if(!validationResult.IsValid) { if(isNewRecipe) { this.AppendMessage(new ErrorMessage { Text = "Did you leave something blank? Please check your entries and try again."}); ViewBag.RecipeCreationOptions = this.RecipeService.GetRecipeCreationOptions(); return this.View("NewRecipe", recipeViewModel); } // Signals Invalid return this.Content("0"); } using (var unitOfWork = this.UnitOfWorkFactory.NewUnitOfWork()) { try { var recipe = recipeViewModel.IsNewRecipe() ? new Recipe() : this.RecipeService.GetRecipeById(recipeViewModel.RecipeId); // Issue 404 if recipe does not exists or not owned by user if (!isNewRecipe && !recipe.WasCreatedBy(this.ActiveUser.UserId)) { return this.Issue404(); } // Map Recipe Mapper.Map(recipeViewModel, recipe); #region INGREDIENT DELETIONS if(!isNewRecipe) { // Delete Fermentables that were removed var fermentablesForDeletion = recipe.Fermentables.Except(recipe.Fermentables.Join(recipeViewModel.Fermentables ?? new List<RecipeFermentableViewModel>(), x => x.RecipeFermentableId, y => Converter.Convert<int>(y.Id), (x, y) => x)).ToList(); this.RecipeService.MarkRecipeIngredientsForDeletion<RecipeFermentable>(fermentablesForDeletion); // Delete Hops that were removed var hopsForDeletion = recipe.Hops.Except(recipe.Hops.Join(recipeViewModel.Hops ?? new List<RecipeHopViewModel>(), x => x.RecipeHopId, y => Converter.Convert<int>(y.Id), (x, y) => x)).ToList(); this.RecipeService.MarkRecipeIngredientsForDeletion<RecipeHop>(hopsForDeletion); // Delete Yeasts that were removed var yeastsForDeletion = recipe.Yeasts.Except(recipe.Yeasts.Join(recipeViewModel.Yeasts ?? new List<RecipeYeastViewModel>(), x => x.RecipeYeastId, y => Converter.Convert<int>(y.Id), (x, y) => x)).ToList(); this.RecipeService.MarkRecipeIngredientsForDeletion<RecipeYeast>(yeastsForDeletion); // Delete Adjuncts that were removed var adjunctsForDeletion = recipe.Adjuncts.Except(recipe.Adjuncts.Join(recipeViewModel.Others ?? new List<RecipeOtherViewModel>(), x => x.RecipeAdjunctId, y => Converter.Convert<int>(y.Id), (x, y) => x)).ToList(); this.RecipeService.MarkRecipeIngredientsForDeletion<RecipeAdjunct>(adjunctsForDeletion); // Delete MashSteps that were removed var mashStepsForDeletion = recipe.MashSteps.Except(recipe.MashSteps.Join(recipeViewModel.MashSteps ?? new List<RecipeMashStepViewModel>(), x => x.RecipeMashStepId, y => Converter.Convert<int>(y.Id), (x, y) => x)).ToList(); this.RecipeService.MarkRecipeIngredientsForDeletion<RecipeMashStep>(mashStepsForDeletion); } #endregion #region INGREDIENT ADDITIONS // Add Fermentables if (recipeViewModel.Fermentables != null) { recipeViewModel.Fermentables.Where(x => Converter.Convert<int>(x.Id) == 0) .ForEach(x => recipe.Fermentables.Add(Mapper.Map(x, new RecipeFermentable()))); } // Add Hops if (recipeViewModel.Hops != null) { recipeViewModel.Hops.Where(x => Converter.Convert<int>(x.Id) == 0) .ForEach(x => recipe.Hops.Add(Mapper.Map(x, new RecipeHop()))); } // Add Yeasts if (recipeViewModel.Yeasts != null) { recipeViewModel.Yeasts.Where(x => Converter.Convert<int>(x.Id) == 0) .ForEach(x => recipe.Yeasts.Add(Mapper.Map(x, new RecipeYeast()))); } // Add Adjuncts if (recipeViewModel.Others != null) { recipeViewModel.Others.Where(x => Converter.Convert<int>(x.Id) == 0) .ForEach(x => recipe.Adjuncts.Add(Mapper.Map(x, new RecipeAdjunct()))); } // Add MashStep if (recipeViewModel.MashSteps != null) { recipeViewModel.MashSteps.Where(x => Converter.Convert<int>(x.Id) == 0) .ForEach(x => recipe.MashSteps.Add(Mapper.Map(x, new RecipeMashStep()))); } #endregion #region INGREDIENT UPDATES if (!isNewRecipe) { // Update Fermentables if (recipeViewModel.Fermentables != null) { foreach (var recipeFermentableViewModel in recipeViewModel.Fermentables.Where(x => Converter.Convert<int>(x.Id) > 0)) { var match = recipe.Fermentables.FirstOrDefault(x => x.RecipeFermentableId == Converter.Convert<int>(recipeFermentableViewModel.Id)); if (match == null) { throw new InvalidOperationException("Unable to find matching fermentable"); } Mapper.Map(recipeFermentableViewModel, match); } } // Update Hops if (recipeViewModel.Hops != null) { foreach (var recipeHopViewModel in recipeViewModel.Hops.Where(x => Converter.Convert<int>(x.Id) > 0)) { var match = recipe.Hops.FirstOrDefault(x => x.RecipeHopId == Converter.Convert<int>(recipeHopViewModel.Id)); if (match == null) { throw new InvalidOperationException("Unable to find matching Hop"); } Mapper.Map(recipeHopViewModel, match); } } // Update Yeasts if (recipeViewModel.Yeasts != null) { foreach (var recipeYeastViewModel in recipeViewModel.Yeasts.Where(x => Converter.Convert<int>(x.Id) > 0)) { var match = recipe.Yeasts.FirstOrDefault(x => x.RecipeYeastId == Converter.Convert<int>(recipeYeastViewModel.Id)); if (match == null) { throw new InvalidOperationException("Unable to find matching Yeast"); } Mapper.Map(recipeYeastViewModel, match); } } // Update Adjuncts if (recipeViewModel.Others != null) { foreach (var recipeAdjunctViewModel in recipeViewModel.Others.Where(x => Converter.Convert<int>(x.Id) > 0)) { var match = recipe.Adjuncts.FirstOrDefault(x => x.RecipeAdjunctId == Converter.Convert<int>(recipeAdjunctViewModel.Id)); if (match == null) { throw new InvalidOperationException("Unable to find matching Adjunct"); } Mapper.Map(recipeAdjunctViewModel, match); } } // Update MashSteps if (recipeViewModel.MashSteps != null) { foreach (var recipeMashStepViewModel in recipeViewModel.MashSteps.Where(x => Converter.Convert<int>(x.Id) > 0)) { var match = recipe.MashSteps.FirstOrDefault(x => x.RecipeMashStepId == Converter.Convert<int>(recipeMashStepViewModel.Id)); if (match == null) { throw new InvalidOperationException("Unable to find matching MashStep"); } Mapper.Map(recipeMashStepViewModel, match); } } } #endregion #region STEP DELETIONS / ADDITIONS / UPDATES // Deletions if (!isNewRecipe) { var stepsForDeletion = recipe.Steps.Except(recipe.Steps.Join(recipeViewModel.Steps ?? new List<RecipeStepViewModel>(), x => x.RecipeStepId, y => Converter.Convert<int>(y.Id), (x, y) => x)).ToList(); this.RecipeService.MarkRecipeStepsForDeletion(stepsForDeletion); } // Additions if (recipeViewModel.Steps != null) { if(recipe.Steps == null) { recipe.Steps = new List<RecipeStep>(); } recipeViewModel.GetSteps() .Where(x => Converter.Convert<int>(x.Id) == 0) .Where(x => !string.IsNullOrWhiteSpace(x.Text)) .ForEach(x => recipe.Steps.Add(Mapper.Map(x, new RecipeStep { DateCreated = DateTime.Now }))); } // Updates if (!isNewRecipe) { if (recipeViewModel.Steps != null) { foreach (var recipeStep in recipeViewModel.GetSteps() .Where(x => Converter.Convert<int>(x.Id) > 0) .Where(x => !string.IsNullOrWhiteSpace(x.Text))) { var match = recipe.Steps.FirstOrDefault(x => x.RecipeStepId == Converter.Convert<int>(recipeStep.Id)); if (match == null) { throw new InvalidOperationException("Unable to find matching step"); } match = Mapper.Map(recipeStep, match); match.DateModified = DateTime.Now; } } } #endregion // Save the Image if(isNewRecipe) { if(recipeViewModel.PhotoForUpload != null) { // Save the New Image recipe.ImageUrlRoot = this.StaticContentService.SaveRecipeImage(recipeViewModel.PhotoForUpload.InputStream, this.WebSettings.MediaPhysicalRoot); } } // Finalize Recipe this.RecipeService.FinalizeRecipe(recipe); unitOfWork.Commit(); if(isNewRecipe) { this.ForwardMessage(new SuccessMessage { Text = BrewgrMessages.RecipeSaved }); return Redirect(Url.RecipeEditUrl(recipe.RecipeId)); } else { // Signals Success return Content("1"); } } catch (Exception ex) { this.LogHandledException(ex); unitOfWork.Rollback(); if(isNewRecipe) { ViewBag.RecipeCreationOptions = this.RecipeService.GetRecipeCreationOptions(); this.AppendMessage(new ErrorMessage { Text = GenericMessages.ErrorMessage }); return View("NewRecipe", recipeViewModel); } else { // Signals Failure return Content("-1"); } } } }