Strange LazyField problem

Topics: Writing modules
Mar 8, 2012 at 7:02 PM
Edited Mar 8, 2012 at 7:03 PM

I have a weird problem, here is my driver:

 

public class EntryTypePartDriver : ContentPartDriver<EntryTypePart> {
        private readonly IEntryValidationService _validation;
        public new string Prefix { get { return "EntryType"; } }

        public EntryTypePartDriver(IEntryValidationService validation) {
            _validation = validation;
        }

        protected override DriverResult Editor(EntryTypePart part, dynamic shapeHelper) {
            var model = new EntryTypeEditorViewModel()
            {
                Name = part.Name,
                Active = part.Active,
                Calculated = part.Calculated,
                Expression = part.Expression,
                Validate = part.Validate,
                ValidationRules = 
                    part
                    .ValidationRules
                    .Select(
                        x =>
                            new EntryValidationRuleViewModel() {
                                Id = x.Id,
                                Expression = x.Expression,
                                Message = x.Message
                            }
                    ),
                RequireApproval = part.RequireApproval,
                RequireNote = part.RequireNote,
                DisplayPriority = part.DisplayPriority,
                IsHourType = part.TypeDefinition.Name == "HourType",
                IsPayType = part.TypeDefinition.Name == "PayType"
            };

            model.ValidationRulesJson = JsonConvert.SerializeObject(model.ValidationRules);

            return ContentShape(
                "Parts_EntryType_Edit",
                () =>
                    shapeHelper.EditorTemplate(
                        TemplateName: "Parts/EntryType",
                        Model: model,
                        Prefix: Prefix
                    )
            );
        }

        protected override DriverResult Editor(EntryTypePart part, Orchard.ContentManagement.IUpdateModel updater, dynamic shapeHelper) {
            var model = new EntryTypeEditorViewModel();
            
            if (updater.TryUpdateModel(model, Prefix, null, null)) {
                var partIds = part.ValidationRules.Select(x => x.Id).ToList().ToList();
                part.Name = model.Name;
                part.Active = model.Active;
                part.Calculated = model.Calculated;
                part.Expression = model.Expression;
                part.RequireApproval = model.RequireApproval;
                part.RequireNote = model.RequireNote;
                part.DisplayPriority = model.DisplayPriority;
                part.Validate = model.Validate;

                var modelIds = model.ValidationRules.Select(x => x.Id).ToList();
                var removed = partIds.Where(x => !modelIds.Contains(x)).ToList();
                
                removed.ForEach(x => _validation.Delete(x));
                
                model
                    .ValidationRules
                    .Where(x => x.Id == 0 && !partIds.Contains(x.Id))
                    .ToList()
                    .ForEach(
                        x =>
                        _validation
                            .Create(
                                new EntryValidationRuleRecord() {
                                    EntryTypeId = part.Id,
                                    Expression = x.Expression,
                                    Message = x.Message
                                }
                            )
                    );

            }

            return Editor(part, shapeHelper);
        }
    }

 

Of note is that the ValidationRules property is backed by a LazyField whose loader calls a service (an IEntryValidationService) to load the rules for this particular EntryType from an IRepository. 

What is weird here is when I remove an Rule entry in the dashboard and post back, the ValidationRules collection on the part is empty so the code to remove the ones missing from the ViewModel doesn't remove them, but by the time it comes all the way back around to the view, the rules are in there and are rendered in the view.

Any ideas on what's going on here or how I may be doing this wrong. I don't know how I could be, since I have another part whose driver is amazingly similar to this one. The only difference being the LazyField in that part loads ContentParts from the ContentManager and not records from an IRepository. 

Developer
Mar 8, 2012 at 10:07 PM
Edited Mar 8, 2012 at 10:08 PM

Perhaps you should invoke a projection method directly:

ValidationRules = 
                    part
                    .ValidationRules
                    .Select(
                        x =>
                            new EntryValidationRuleViewModel() {
                                Id = x.Id,
                                Expression = x.Expression,
                                Message = x.Message
                            }
                    ).ToList(),

Notice the .ToList() appended. Not sure if it's the cause of the problem, although not doing this early enough has bitten me before.

Mar 8, 2012 at 11:56 PM

Ya that one has gotten me too. That part works perfectly, in fact it works perfectly when it shouldn't. 

var partIds = part.ValidationRules.Select(x => x.Id).ToList();

This line produces a collection with 0 items, even though there should some. So again the removal logic never works when you remove rules from the dashboard and the view model comes back without them present in it's ValidationRules collection.

And then here:

return Editor(part, shapeHelper);

I am passing the part through the first Editor method, so you'd think the lines you quoted above would also produce an empty collection, but it doesn't. The Rules are now mystically in the collection.