2

我正在使用指南在 Orchard (ocs.orchardproject.net/Documentation/Creating-1-n-and-nn-relations) 中创建 n 对 n 关系,并稍作修改。虽然示例代码运行良好,但在创建或编辑项目后,我自己的内容部分始终为空白。我无法弄清楚,因为我发誓我的代码几乎与他们的相同(除了内容部分具有更多/更少不相关的字段)。

我怀疑这可能与驱动程序中的前缀有关。我真的不知道前缀应该做什么,但是将其设置为一个值会在创建/编辑时产生运行时错误,其他值只会产生所有字段空白的结果。

原始样本工作正常,所以它必须是我做过或没做过的事情,但我就是不知道它是什么。

一些相关的类:

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>

这几天我一直在碰壁,请提出任何你认为理论上可能有帮助的建议,因为我很绝望:(

UPD: StreetHandler 类:

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));
        }
    }
}

日志上有一个异常:

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

编辑:一些模型类:

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));
        }
    }
}
4

2 回答 2

2

您的 StreetAreaRecord 模型缺少 Id 属性。由于它不是 ContentPartRecord,因此您必须手动设置该属性。

public virtual int Id { get; set; }
于 2012-04-10T02:59:06.907 回答
0

前缀是为了确保部件编辑器的 html 字段中的唯一 id 属性。您可能在具有“名称”字段的单个内容类型中有多个部分。如果没有前缀,则 html 将无效,并且回发将不起作用,因为将有两个 id="Name" 字段。您可以简单地将前缀设置为零件的名称。

我不确定前缀是什么阻止你的部分保存。你检查过你的处理程序吗?确保它为 StreetPartRecord 设置了过滤器,这通常是导致新部件没有在回发时保存的原因。

于 2012-04-09T13:54:08.503 回答