Empty content item after create/edit in Orchard

好久不见. 提交于 2019-12-10 11:46:13

问题


I'm using the guide to creating n-to-n relations in Orchard (ocs.orchardproject.net/Documentation/Creating-1-n-and-n-n-relations) with some slight modifications. While the sample code works well my own content part is always blank after I create or edit the item. I can't figure it out, because I swear my code is almost identical to theirs (with the exception of content parts having more/less unrelated fields).

I suspect it might have to do with the Prefix in the Driver. I don't really know what the prefix is supposed to do, but setting it to one value produces a runtime error on create/edit, other values just produce result with all fields blank.

The original sample works fine, so it has to be something I did or didn't do, but I just can't figure out what it is.

Some relevant classes:

using System.Linq;
using JetBrains.Annotations;
using Orchard.ContentManagement;
using Orchard.ContentManagement.Drivers;
using ArealAds.Models;
using ArealAds.Services;
using ArealAds.ViewModels;

namespace ArealAds.Drivers {
    [UsedImplicitly]
    public class StreetPartDriver : ContentPartDriver<StreetPart> {
        private readonly IStreetService _streetService;

        private const string TemplateName = "Parts/Street";

        public StreetPartDriver(IStreetService streetService) {
            _streetService = streetService;
        }

        // this one gives a runtime error with blank description,
        // other values produce result with all fields blank
        protected override string Prefix {
            get { return "Area"; }
        }

        protected override DriverResult Display(StreetPart part, string displayType, dynamic shapeHelper) {
            return ContentShape("Parts_Street",
                            () => shapeHelper.Parts_Street(
                                ContentPart: part,
                                Name: part.Name,
                                Areas: part.Areas,
                                Districts: part.Districts));

        }

        protected override DriverResult Editor(StreetPart part, dynamic shapeHelper) {
            return ContentShape("Parts_Street_Edit",
                    () => shapeHelper.EditorTemplate(
                        TemplateName: TemplateName,
                        Model: BuildEditorViewModel(part),
                        Prefix: Prefix));
        }

        protected override DriverResult Editor(StreetPart part, IUpdateModel updater, dynamic shapeHelper) {
            var model = new EditStreetViewModel();
            updater.TryUpdateModel(model, Prefix, null, null);

            if (part.ContentItem.Id != 0) {
                _streetService.UpdateAreasForContentItem(part.ContentItem, model.Areas);
            }

            return Editor(part, shapeHelper);
        }

        private EditStreetViewModel BuildEditorViewModel(StreetPart part) {
            var itemAreas = part.Areas.ToLookup(r => r.Id);
            return new EditStreetViewModel {
                Areas = _streetService.GetAreas().Select(r => new AreaEntry {
                    Area = r,
                    IsChecked = itemAreas.Contains(r.Id)
                }).ToList()
            };
        }
    }
}


using System.Collections.Generic;
using System.Linq;
using Orchard;
using Orchard.ContentManagement;
using Orchard.Data;
using ArealAds.Models;
using ArealAds.ViewModels;

namespace ArealAds.Services {
    public interface IStreetService : IDependency {
        void UpdateAreasForContentItem(ContentItem item, IEnumerable<AreaEntry> areas);
        IEnumerable<AreaRecord> GetAreas();
    }

    public class StreetService : IStreetService {
        private readonly IRepository<AreaRecord> _areaRepository;
        private readonly IRepository<StreetAreaRecord> _streetAreaRepository;

        public StreetService(
            IRepository<AreaRecord> areaRepository,
            IRepository<StreetAreaRecord> streetAreaRepository) {

            _areaRepository = areaRepository;
            _streetAreaRepository = streetAreaRepository;
        }

        public void UpdateAreasForContentItem(ContentItem item, IEnumerable<AreaEntry> areas) {
            var record = item.As<StreetPart>().Record;
            var oldAreas = _streetAreaRepository.Fetch(
                r => r.StreetRecord == record);
            var lookupNew = areas
                .Where(e => e.IsChecked)
                .Select(e => e.Area)
                .ToDictionary(r => r, r => false);
            // Delete the areas that are no longer there and mark the ones that should stay
            foreach(var streetAreaRecord in oldAreas) {
                if (lookupNew.ContainsKey(streetAreaRecord.AreaRecord)) {
                    lookupNew[streetAreaRecord.AreaRecord] = true;
                }
                else {
                    _streetAreaRepository.Delete(streetAreaRecord);
                }
            }
            // Add the new areas
            foreach(var area in lookupNew.Where(kvp => !kvp.Value).Select(kvp => kvp.Key)) {
                _streetAreaRepository.Create(new StreetAreaRecord {
                    StreetRecord = record,
                    AreaRecord = area
                });
            }
        }

        public IEnumerable<AreaRecord> GetAreas() {
            return _areaRepository.Table.ToList();
        }
    }
}


using System.ComponentModel.DataAnnotations;
using Orchard.ContentManagement;
using Orchard.ContentManagement.Records;

namespace ArealAds.Models {
    public class StreetAreaRecord : ContentPartRecord {
        public virtual StreetRecord StreetRecord { get; set; }
        public virtual AreaRecord AreaRecord { get; set; }
    }
}

using System;
using System.Collections.Generic;
using System.Data;
using Orchard.ContentManagement.Drivers;
using Orchard.ContentManagement.MetaData;
using Orchard.ContentManagement.MetaData.Builders;
using Orchard.Core.Contents.Extensions;
using Orchard.Data.Migration;
using ArealAds.Models;

namespace ArealAds {
    public class Migrations : DataMigrationImpl {

        public int Create() {
            //
            // Street-Area-District
            //

            SchemaBuilder.CreateTable("DistrictRecord", table => table
                .ContentPartRecord()
                .Column<string>("Name")
            );

            ContentDefinitionManager.AlterPartDefinition(
                typeof(DistrictPart).Name, cfg => cfg.Attachable());

            ContentDefinitionManager.AlterTypeDefinition(
                "District", cfg => cfg
                .WithPart("CommonPart")
                .WithPart("DistrictPart")
                .Creatable()
            );

            SchemaBuilder.CreateTable("AreaRecord", table => table
                .ContentPartRecord()
                .Column<string>("Name")
                .Column<int>("DistrictRecord_Id")
            );

            ContentDefinitionManager.AlterPartDefinition(
                typeof(AreaPart).Name, cfg => cfg.Attachable());

            ContentDefinitionManager.AlterTypeDefinition(
                "Area", cfg => cfg
                .WithPart("CommonPart")
                .WithPart("AreaPart")
                .Creatable()
            );

            SchemaBuilder.CreateTable("StreetRecord", table => table
                .ContentPartRecord()
                .Column<string>("Name")
            );

            ContentDefinitionManager.AlterPartDefinition(
                typeof(StreetPart).Name, cfg => cfg.Attachable());

            ContentDefinitionManager.AlterTypeDefinition(
                "Street", cfg => cfg
                .WithPart("CommonPart")
                .WithPart("StreetPart")
                .Creatable()
            );

            SchemaBuilder.CreateTable("StreetAreaRecord", table => table
                .Column<int>("Id", column => column.PrimaryKey().Identity())
                .Column<int>("StreetRecord_Id")
                .Column<int>("AreaRecord_Id")
            );


            //
            // Address-Ad
            //

            SchemaBuilder.CreateTable("AddressRecord", table => table
                .ContentPartRecord()
                .Column<int>("StreetRecord_Id")
                .Column<int>("Building")
                .Column<int>("Kor")
                .Column<int>("Str")
                .Column<int>("Vl")
                .Column<string>("Note")
                .Column<int>("AreaRecord_Id")
                .Column<int>("DistrictRecord_Id")
                .Column<string>("Phone1")
                .Column<string>("Phone2")
                .Column<string>("Phone3")
            );

            ContentDefinitionManager.AlterPartDefinition(
                typeof(AddressPart).Name, cfg => cfg.Attachable());


            return 1;
        }
    }
}

@model ArealAds.ViewModels.EditStreetViewModel
<fieldset>
    <legend>Улица</legend>

    <div class="editor-label">
        @Html.LabelFor(model => model.Street.Name)
    </div>
    <div class="editor-field">
        @Html.TextBoxFor(model => model.Street.Name)
        @Html.ValidationMessageFor(model => model.Street.Name)
    </div>
<ul>

@for (int i = 0; i < Model.Areas.Count; i++) {
    <li>
        <input type="hidden" value="@Model.Areas[i].Area.Id"
               name="@Html.FieldNameFor(m => m.Areas[i].Area.Id)"/>

        <label for="@Html.FieldNameFor(m => m.Areas[i].IsChecked)">

            <input type="checkbox" value="true"
                name="@Html.FieldNameFor(m => m.Areas[i].IsChecked)"
                id="@Html.FieldNameFor(m => m.Areas[i].IsChecked)"
                @if (Model.Areas[i].IsChecked) {<text>checked="checked"</text>}/>

            @Model.Areas[i].Area.Name
        </label>
    </li>
}
</ul>
</fieldset>

I had been beating my head against the wall on this for days, please make any suggestions you feel might theoretically help 'cos I'm desperate :(

UPD: The StreetHandler class:

using ArealAds.Models;
using Orchard.ContentManagement.Handlers;
using Orchard.Data;

namespace ArealAds.Handlers {
    public class StreetHandler : ContentHandler {
        public StreetHandler(IRepository<StreetRecord> repository) {
            Filters.Add(StorageFilter.For(repository));
        }
    }
}

There is an exception on the log:

2012-04-10 00:07:58,515 [7] Orchard.ContentManagement.Drivers.Coordinators.ContentPartDriverCoordinator - IdentifierGenerationException thrown from IContentPartDriver by ArealAds.Drivers.StreetPartDriver
NHibernate.Id.IdentifierGenerationException: attempted to assign id from null one-to-one property: ContentItemRecord
   â NHibernate.Id.ForeignGenerator.Generate(ISessionImplementor sessionImplementor, Object obj)
   â NHibernate.Event.Default.AbstractSaveEventListener.SaveWithGeneratedId(Object entity, String entityName, Object anything, IEventSource source, Boolean requiresImmediateIdAccess)
   â NHibernate.Event.Default.DefaultSaveOrUpdateEventListener.SaveWithGeneratedOrRequestedId(SaveOrUpdateEvent event)
   â NHibernate.Event.Default.DefaultSaveEventListener.SaveWithGeneratedOrRequestedId(SaveOrUpdateEvent event)
   â NHibernate.Event.Default.DefaultSaveOrUpdateEventListener.EntityIsTransient(SaveOrUpdateEvent event)
   â NHibernate.Event.Default.DefaultSaveEventListener.PerformSaveOrUpdate(SaveOrUpdateEvent event)
   â NHibernate.Event.Default.DefaultSaveOrUpdateEventListener.OnSaveOrUpdate(SaveOrUpdateEvent event)
   â NHibernate.Impl.SessionImpl.FireSave(SaveOrUpdateEvent event)
   â NHibernate.Impl.SessionImpl.Save(Object obj)
   â Orchard.Data.Repository`1.Create(T entity) â d:\TeamCity\Projects\Orchard-Default\src\Orchard\Data\Repository.cs:ñòðîêà 96
   â Orchard.Data.Repository`1.Orchard.Data.IRepository<T>.Create(T entity) â d:\TeamCity\Projects\Orchard-Default\src\Orchard\Data\Repository.cs:ñòðîêà 36
   â ArealAds.Services.StreetService.UpdateAreasForContentItem(ContentItem item, IEnumerable`1 areas) â c:\Users\Mom\Teritoriya\Modules\ArealAds\Services\Street.cs:ñòðîêà 46
   â ArealAds.Drivers.StreetPartDriver.Editor(StreetPart part, IUpdateModel updater, Object shapeHelper) â c:\Users\Mom\Teritoriya\Modules\ArealAds\Controllers\Street.cs:ñòðîêà 47
   â System.Dynamic.UpdateDelegates.UpdateAndExecute4[T0,T1,T2,T3,TRet](CallSite site, T0 arg0, T1 arg1, T2 arg2, T3 arg3)
   â Orchard.ContentManagement.Drivers.ContentPartDriver`1.Orchard.ContentManagement.Drivers.IContentPartDriver.UpdateEditor(UpdateEditorContext context) â d:\TeamCity\Projects\Orchard-Default\src\Orchard\ContentManagement\Drivers\ContentPartDriver.cs:ñòðîêà 30
   â Orchard.ContentManagement.Drivers.Coordinators.ContentPartDriverCoordinator.<>c__DisplayClass10.<UpdateEditor>b__f(IContentPartDriver driver) â d:\TeamCity\Projects\Orchard-Default\src\Orchard\ContentManagement\Drivers\Coordinators\ContentPartDriverCoordinator.cs:ñòðîêà 61
   â Orchard.InvokeExtensions.Invoke[TEvents](IEnumerable`1 events, Action`1 dispatch, ILogger logger) â d:\TeamCity\Projects\Orchard-Default\src\Orchard\InvokeExtensions.cs:ñòðîêà 19

EDIT: some model classes:

using System.ComponentModel.DataAnnotations;
using Orchard.ContentManagement;
using Orchard.ContentManagement.Records;

namespace ArealAds.Models {
    public class AreaRecord : ContentPartRecord {
        public virtual string Name { get; set; }
        public virtual DistrictRecord DistrictRecord { get; set; }
    }

    public class AreaPart : ContentPart<AreaRecord> {
        [Required]
        public string Name {
            get { return Record.Name; }
            set { Record.Name = value; }
        }

        [Required]
        public DistrictRecord DistrictRecord {
            get { return Record.DistrictRecord; }
            set { Record.DistrictRecord = value; }
        }
    }
}

using System.Collections.Generic;
using System.Linq;
using System.ComponentModel.DataAnnotations;
using Orchard.ContentManagement;
using Orchard.ContentManagement.Records;

namespace ArealAds.Models {
    public class StreetRecord : ContentPartRecord {
        public virtual string Name { get; set; }
        public virtual IList<StreetAreaRecord> Areas { get; set; }

        public StreetRecord() {
            Areas = new List<StreetAreaRecord>();
        }
    }

    public class StreetPart : ContentPart<StreetRecord> {
        [Required]
        public string Name {
            get { return Record.Name; }
            set { Record.Name = value; }
        }

        public IEnumerable<AreaRecord> Areas {
            get {
                return Record.Areas.Select (r => r.AreaRecord);
            }
        }

        public IEnumerable<DistrictRecord> Districts {
            get {
                return Record.Areas.Select (r => r.AreaRecord.DistrictRecord).Distinct();
            }
        }
    }
}

using System.ComponentModel.DataAnnotations;
using Orchard.ContentManagement;
using Orchard.ContentManagement.Records;

namespace ArealAds.Models {
    public class StreetAreaRecord : ContentPartRecord {
        public virtual StreetRecord StreetRecord { get; set; }
        public virtual AreaRecord AreaRecord { get; set; }
    }
}

using ArealAds.Models;
using Orchard.ContentManagement.Handlers;
using Orchard.Data;

namespace ArealAds.Handlers {
    public class AreaHandler : ContentHandler {
        public AreaHandler(IRepository<AreaRecord> repository) {
            Filters.Add(StorageFilter.For(repository));
        }
    }
}

using ArealAds.Models;
using Orchard.ContentManagement.Handlers;
using Orchard.Data;

namespace ArealAds.Handlers {
    public class StreetHandler : ContentHandler {
        public StreetHandler(IRepository<StreetRecord> repository) {
            Filters.Add(StorageFilter.For(repository));
        }
    }
}

回答1:


Your StreetAreaRecord model is missing an Id property. Since it's not a ContentPartRecord, you have to set up the property manually.

public virtual int Id { get; set; }



回答2:


The prefix is to ensure a unique id attribute in the html fields in the part editor. You might have multiple parts within a single content type with a "Name" field. Without the prefix the html would be invalid and postback wouldn't work because there would be two fields with id="Name". You can simply set the prefix to be the name of the part.

I'm not sure the prefix is what's preventing your part from saving. Have you checked the Handler for your part? Make sure it sets up the filter for the StreetPartRecord, that is often the cause of a new Part not saving on postback.



来源:https://stackoverflow.com/questions/10073023/empty-content-item-after-create-edit-in-orchard

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!