Orchard CMS 1.4.2 - Edit view for my custom content type doesn't show content part data - orchardcms

Setup:
I am creating a module so that my clients can manage their own corporate emails as content.
The idea is to avoid putting people's real email addresses on a public website, so for a website user to send an email, I get Orchard to display a form. No problem with that. My problem (see below) is related to how Orchard displays content items in the dashboard, not the public facing part of the website.
Moving on:
I have created (see migration.cs below) a content type called EmailAddress. It is basically just a content type wrapper around a content part called CorporateEmailPart.
The other relevant bit of my setup is the driver (see CorporateEmailPartDriver.cs below). I have followed Kevin Kuebler's videos on PluralSight.com to write the driver. Debugging shows it working fine.
The shapes are placed using the Placement.info file in my module.
So, everything would be working fine if it wasn't for...
The Problem:
The driver correctly allows me to create the shape for the editor of my content type, which displays fine.
Or rather, displays fine to allow me to create a NEW EmailAddress. I can save the CorporateEmailPart to the database just fine.
However, when I save the new EmailAddress content type, or try to edit an existing one, the fields for the CorporateEmailPart don't display at all on my EmailAddress editor.
Ie, when in my POST DriverResult Editor method I return the GET Editor ContentShape, only the CommonPart of my Content Type is displayed (ie, the owner field of the ContentItem). No CorporateEmailPart data is displayed. Not even empty text boxes.
I must be missing something simple, cos everything else works.
But I just can't see what...!
CODE:
Migrations.cs:
using Orchard.ContentManagement.MetaData;
using Orchard.Core.Contents.Extensions;
using Orchard.Data;
using Orchard.Data.Migration;
using Wingspan.CorporateEmails.Models;
namespace Wingspan.CorporateEmails {
public class Migrations : DataMigrationImpl {
public int Create() {
ContentDefinitionManager.AlterTypeDefinition("EmailAddress", builder =>
builder
.WithPart("CommonPart")
.Creatable());
SchemaBuilder.CreateTable("CorporateEmailPartRecord", table =>
table.ContentPartRecord()
.Column<string>("Alias")
.Column<string>("EmailAddress")
.Column<int>("DisplayOrder")
.Column<string>("DisplayTitle")
.Column<bool>("IsDefault"));
ContentDefinitionManager.AlterPartDefinition(typeof(CorporateEmailPart).Name, p => p.Attachable());
ContentDefinitionManager.AlterTypeDefinition("EmailAddress", builder =>
builder
.WithPart(typeof(CorporateEmailPart).Name));
SchemaBuilder.CreateTable("EmailMessageRecord", table =>
table
.Column<int>("Id", col => col.PrimaryKey().Identity())
.Column<int>("CorporateEmailPartRecord_Id")
.Column<string>("Sender")
.Column<string>("Recipient")
.Column<string>("Subject")
.Column<string>("Body")
.Column<string>("TitleAndName")
.Column<string>("Address")
.Column<string>("Telephones"));
return 1;
}
}
}
CorporateEmailDriver.cs
using System.Collections.Generic;
using System.Web.Routing;
using Orchard;
using Orchard.ContentManagement;
using Orchard.ContentManagement.Drivers;
using Orchard.Data;
using Orchard.Services;
using Wingspan.CorporateEmails.Models;
using Wingspan.CorporateEmails.ViewModels;
using System.Linq;
namespace Wingspan.CorporateEmails.Drivers {
public class CorporateEmailPartDriver : ContentPartDriver<CorporateEmailPart>{
private readonly IRepository<EmailMessageRecord> _repositoryEmailMessage;
private readonly IEnumerable<IHtmlFilter> _htmlFilters;
private readonly RequestContext _requestContext;
private const string TemplateName = "Parts/CorporateEmail";
protected override string Prefix
{
get
{
{ return "CorporateEmail"; }
}
}
public IOrchardServices Services { get; set; }
public CorporateEmailPartDriver(IRepository<EmailMessageRecord> repositoryEmailMessage)
{
_repositoryEmailMessage = repositoryEmailMessage;
}
public CorporateEmailPartDriver(IOrchardServices services, IEnumerable<IHtmlFilter> htmlFilters, RequestContext requestContext) {
_htmlFilters = htmlFilters;
Services = services;
_requestContext = requestContext;
}
protected override DriverResult Display(CorporateEmailPart part, string displayType, dynamic shapeHelper)
{
return ContentShape(TemplateName, () => shapeHelper.Parts_CorporateEmail(CorporateEmailPart: part));
}
//GET
protected override DriverResult Editor(CorporateEmailPart part, dynamic shapeHelper)
{
var model = BuildEditorViewModel(part);
return ContentShape("Parts_CorporateEmail_Edit", () =>
shapeHelper.EditorTemplate(TemplateName: TemplateName, Model: model, Prefix: Prefix));
}
//POST
protected override DriverResult Editor(CorporateEmailPart part, IUpdateModel updater, dynamic shapeHelper)
{
updater.TryUpdateModel(part, Prefix, null, null);
var model = BuildEditorViewModel(part);
return ContentShape("Parts_CorporateEmail_Edit",
() => shapeHelper.EditorTemplate(TemplateName: TemplateName, Model: model, Prefix: Prefix));
}
#region Private Utilities
private CorporateEmailEditorViewModel BuildEditorViewModel(CorporateEmailPart part)
{
return new CorporateEmailEditorViewModel
{
Alias = part.Alias,
EmailAddress = part.EmailAddress,
DisplayOrder = part.DisplayOrder,
DisplayTitle = part.DisplayTitle,
IsDefault = part.IsDefault,
EmailMessageSummaries = part.EmailMessages.Select(ue => ue.Summary).ToList()
};
}
#endregion
}
}
Any suggestions much appreciated.

OK, a more careful debugging session with a clear mind and I found the issue:
An error is caused when building the viewmodel in my driver:
private CorporateEmailEditorViewModel BuildEditorViewModel(CorporateEmailPart part)
{
return new CorporateEmailEditorViewModel
{
Alias = part.Alias,
EmailAddress = part.EmailAddress,
DisplayOrder = part.DisplayOrder,
DisplayTitle = part.DisplayTitle,
IsDefault = part.IsDefault,
EmailMessageSummaries = part.EmailMessages.Select(ue => ue.Summary).ToList()
};
}
The offending line is:
EmailMessageSummaries = part.EmailMessages.Select(ue => ue.Summary).ToList()
The reason is that there is some problem with retrieving data from the EmailMessageRecord table using the CorporateEmailPartRecord_Id foreign key. I still haven't figured out what (have never used nHibernate which is what orchard uses), but the existence of this error answers the current question.
A Word of Advice:
Orchard works fine.
In fact, it works really well.
What doesn'twork is the code in your module.
So be more careful with your debugging.
Kevin Kuebler's videos are a great orchard learning resource.
When you refactor the name of something, double check to make sure they have ALL been changed.
Addendum:
I have found what was causing the hHibernate error: shoddy refactoring.
I originally called the project CompanyEmails, but then refactored everything to call it CorporateEmails. The only problem being, I didn't refactor everything, did I? There was still a CompanyEmailPartRecord_Id field on the EmailMessageRecord model class, whereas in the database, I had changed it to CorporateEmailRecordPart_Id. So, just another bug, just a failure to refactor properly.

Related

Adding the target parameter to the Custom Link MenuItem in Orchard CMS

I have created my navigation menus in Orchard using a mix of Content Item and Custom Link Elements (parts of the website are outside the scope of the CMS). Now there are a couple of links that I need to open in a new window/tab, basically the target="_blank" behaviour.
SInce the original Custom Link does not have any parameters I tried to create an extended version of it. In the admin backend I went to "Content definition" looked up Custom Link and tried to create a copy of it, then add a target field that I could check for and use in my theme's Menu.cshtml file.
However I can't even get the basic carbon copy of the Custom Link item working. It has the same stereotype, same Parts, same Forms (none) as the original Custom Link, and it does appear in the list of items on the admin -> navigation window. However the item does not have a field for the URL/link. It only has the field for Menu Text, nothing else.
So my question is 2-tiered:
How can I get a carbon copy of the Custom Link item type working in my Orchard backend navigation?
When I have my copy of the Custom Link working and add a text field named target, how can I access its value in the Menu.cshtml view?
(I tried simply adding a URL field to my copy, that would then show up in the navigation editor, however the navigation itself would ignore it in the output and create a link to the content item id instead).
Any help is greatly appreciated!
Edit: Here are some screenshots to better illustrate the problem, maybe they can help pin down the problem.
Seems like you've done everything right. Please double check if MenuItemPart is there. This part is responsible for holding the URL information and displaying an editor for it. Not sure if this part is attachable though - if it's not, then make it so in the Content Definition\Parts pane.
Instead of hardwiring things inside Menu.cshtml, you should create a file named MenuItemLink-[YourTypeName].cshtml. This shape file will be used to display your custom menu items. Then you can access any fields via Model.Content object, eg. Model.Content.YourTypeName.FieldWithTargetName.Value.
You need to use the MenuItemPart because it has a few important functions integrated into Orchard.Core.
This works fine:
AdvancedMenuItemPartRecord:
public class AdvancedMenuItemPartRecord : ContentPartRecord
{
public virtual string Target { get; set; }
public virtual string Classes { get; set; }
}
AdvancedMenuItemPart:
public class AdvancedMenuItemPart : ContentPart<AdvancedMenuItemPartRecord>
{
public string Target
{
get { return Retrieve(x => x.Target); }
set { Store(x => x.Target, value); }
}
public string Classes
{
get { return Retrieve(x => x.Classes); }
set { Store(x => x.Classes, value); }
}
}
AdvancedMenuItemPartDriver:
public class AdvancedMenuItemPartDriver : ContentPartDriver<AdvancedMenuItemPart>
{
protected override string Prefix
{
get { return "AdvancedMenuItem"; }
}
protected override DriverResult Editor(AdvancedMenuItemPart part, dynamic shapeHelper)
{
return ContentShape("Parts_AdvancedMenuItem_Edit", () => shapeHelper.EditorTemplate(TemplateName: "Parts/AdvancedMenuItem", Model: part, Prefix: Prefix));
}
protected override DriverResult Editor(AdvancedMenuItemPart part, IUpdateModel updater, dynamic shapeHelper)
{
updater.TryUpdateModel(part, Prefix, null, null);
return Editor(part, shapeHelper);
}
}
AdvancedMenuItemPartHandler (ActivatingFilter add MenuItemPart to your AdvancedMenuItem dynamicaly):
public class AdvancedMenuItemPartHandler : ContentHandler
{
public AdvancedMenuItemPartHandler(IRepository<AdvancedMenuItemPartRecord> repository)
{
Filters.Add(StorageFilter.For(repository));
Filters.Add(new ActivatingFilter<MenuItemPart>("AdvancedMenuItem"));
}
}
Placement.info:
<Place Parts_AdvancedMenuItem_Edit="Content:11"/>
Migrations:
public int UpdateFrom2()
{
SchemaBuilder.CreateTable("AdvancedMenuItemPartRecord",
table => table
.ContentPartRecord()
.Column<string>("Target")
.Column<string>("Classes")
);
ContentDefinitionManager.AlterPartDefinition("AdvancedMenuItemPart", part => part
.WithDescription(""));
ContentDefinitionManager.AlterTypeDefinition("AdvancedMenuItem", cfg => cfg
.WithPart("AdvancedMenuItemPart")
.WithPart("MenuPart")
.WithPart("CommonPart")
.WithIdentity()
.DisplayedAs("Custom Link Advanced")
.WithSetting("Description", "Custom Link with target and classes fields")
.WithSetting("Stereotype", "MenuItem")
// We don't want our menu items to be draftable
.Draftable(false)
// We don't want the user to be able to create new ActionLink items outside of the context of a menu
.Creatable(false)
);
return 3;
}
MenuItemLink-AdvancedMenuItem.cshtml:
#{
var advancedPart = Model.Content.AdvancedMenuItemPart;
var tag = new TagBuilder("a");
tag.InnerHtml = WebUtility.HtmlDecode(Model.Text.Text);
tag.MergeAttribute("href", Model.Href);
if (!string.IsNullOrWhiteSpace(advancedPart.Target)) {
tag.MergeAttribute("target", advancedPart.Target);
}
if (!string.IsNullOrWhiteSpace(advancedPart.Classes))
{
tag.AddCssClass(advancedPart.Classes);
}
}
#Html.Raw(tag.ToString(TagRenderMode.Normal))

Customs binding cross views in mvvmcross

I need a custom binding and I know when and where but I don't know how I can do it. This is the relation of the view in my custom binding. Think about the *Views like controls.
I have the connections from ViewModel->ContainerView->FirstView but I can't connect it with the TableView. To connect the ContainerView to FirstView I did a custom binding (in one direction for now). And in the setvalue method I call the firstview's method SetBinding (where I want to do the binding)
I tried a few option but nothing happens, the last one looks like this:
public GolferList CurrentGolferList { get; set; }
public void SetBinding(GolferList golferList){
this.CurrentGolferList = golferList;
TableSource = new TableSourcePlayers(TableViewPlayers);
var bindingDescription = new[]{
new MvxBindingDescription {TargetName = "ItemsSource",SourcePropertyPath = "CurrentGolferList"} ,
};
Binder.Bind(this,TableSource, bindingDescription);
TableViewPlayers.Source = TableSource;
TableViewPlayers.ReloadData();
}
I would be grateful if you could tell me another way to handle it.
Update:
I followed Stuart's link and now it works fine, thanks a lot Stuart!
Actually, in my scheme the TableView is a MvxSimpleBindableTableViewSource and I want to bind the data there. So in order to make it work, I used the code below (SetBinding needs some external refactor):
private List<IMvxUpdateableBinding> bindings;
private string BindingText = "{'ItemsSource':{'Path':'CurrentGolfers'}}";
public object DataContext {
get { return dataContext; }
set { dataContext = value;
if (bindings == null)
bindings = this.GetService<IMvxBinder>().Bind(dataContext, TableSource, BindingText).ToList();
else
bindings.ForEach(b => b.DataContext = dataContext);
}
}
public void SetBinding(GolferList golferList){
this.DataContext = PlayViewModel;
tableView.Source = TableSource;
tableView.ReloadData();
}
Note that BindingText points to the table, not to the view itself.
Update 2
Now in V3 it's a bit different. First, the view must implement IMvxBindable and this members:
public object DataContext
{
get { return BindingContext.DataContext; }
set { BindingContext.DataContext = value; }
}
public IMvxBindingContext BindingContext { get; set; }
(Don't forget dispose calling BindingContext.ClearAllBindings() and also call to CreateBindingContext() in the viewload )
And then you'll be able to bind in your class. In my case:
var set = this.CreateBindingSet<FirstPlayViewController, PlayViewModel>();
set.Bind(source).To(vm => vm.CurrentGolfers).Apply(); //I love the new fluent api :)
I think what you want to do is actual a data-bound View, rather than a custom binding.
This is covered in this question - Custom bindable control in a MvvmCross Touch project
Basically what you need to do is to add a collection of 'Bindings' and the 'DataContext' property to your FirstView.
If you do that then you should be able to databind (to DataContext) within FirstView just like you do within any normal MvvmCross view.
Note - this will be much easier to do in v3 as we've added a 'BindingContext' object to assist with exactly this type of operation

Reusable generic LightSwitch screen with WCF RIA Services

I'm new to WCF RIA Services, and have been working with LightSwitch for 4 or so months now.
I created a generic screen to be used for editing lookup tables all over my LightSwitch application, mostly to learn how to create a generic screen that can be used with different entity sets on a dynamic basis.
The screen is pretty simple:
Opened with arguments similar to this:
Application.ShowLookupTypesList("StatusTypes", "StatusTypeId"); which correspond to the entity set for the lookup table in the database.
Here's my WCF RIA service code:
using System.Data.Objects.DataClasses;
using System.Diagnostics;
using System.Reflection;
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Data;
using System.Linq;
using System.ServiceModel.DomainServices.EntityFramework;
using System.ServiceModel.DomainServices.Server;
namespace WCF_RIA_Project
{
public class LookupType
{
[Key]
public int TypeId { get; set; }
public string Name { get; set; }
}
public static class EntityInfo
{
public static Type Type;
public static PropertyInfo Key;
public static PropertyInfo Set;
}
public class WCF_RIA_Service : LinqToEntitiesDomainService<WCSEntities>
{
public IQueryable<LookupType> GetLookupTypesByEntitySet(string EntitySetName, string KeyName)
{
EntityInfo.Set = ObjectContext.GetType().GetProperty(EntitySetName);
EntityInfo.Type = EntityInfo.Set.PropertyType.GetGenericArguments().First();
EntityInfo.Key = EntityInfo.Type.GetProperty(KeyName);
return GetTypes();
}
[Query(IsDefault = true)]
public IQueryable<LookupType> GetTypes()
{
var set = (IEnumerable<EntityObject>)EntityInfo.Set.GetValue(ObjectContext, null);
var types = from e in set
select new LookupType
{
TypeId = (int)EntityInfo.Key.GetValue(e, null),
Name = (string)EntityInfo.Type.GetProperty("Name").GetValue(e, null)
};
return types.AsQueryable();
}
public void InsertLookupType(LookupType lookupType)
{
dynamic e = Activator.CreateInstance(EntityInfo.Type);
EntityInfo.Key.SetValue(e, lookupType.TypeId, null);
e.Name = lookupType.Name;
dynamic set = EntityInfo.Set.GetValue(ObjectContext, null);
set.AddObject(e);
}
public void UpdateLookupType(LookupType currentLookupType)
{
var set = (IEnumerable<EntityObject>)EntityInfo.Set.GetValue(ObjectContext, null);
dynamic modified = set.FirstOrDefault(t => (int)EntityInfo.Key.GetValue(t, null) == currentLookupType.TypeId);
modified.Name = currentLookupType.Name;
}
public void DeleteLookupType(LookupType lookupType)
{
var set = (IEnumerable<EntityObject>)EntityInfo.Set.GetValue(ObjectContext, null);
var e = set.FirstOrDefault(t => (int)EntityInfo.Key.GetValue(t, null) == lookupType.TypeId);
Debug.Assert(e.EntityState != EntityState.Detached, "Entity was in a detached state.");
ObjectContext.ObjectStateManager.ChangeObjectState(e, EntityState.Deleted);
}
}
}
When I add an item to the list from the running screen, save it, then edit it and resave, I receive data conflict "Another user has deleted this record."
I can workaround this by reloading the query after save, but it's awkward.
If I remove, save, then readd and save an item with the same name I get unable to save data, "The context is already tracking a different entity with the same resource Uri."
Both of these problems only affect my generic screen using WCF RIA Services. When I build a ListDetail screen for a specific database entity there are no problems. It seems I'm missing some logic, any ideas?
I've learned that this the wrong approach to using LightSwitch.
There are several behind-the-scenes things this generic screen won't fully emulate and may not be do-able without quite a bit of work. The errors I've received are just one example. LightSwitch's built-in conflict resolution will also fail.
LS's RAD design means just creating a bunch of similar screens is the way to go, with some shared methods. If the actual layout needs changed across many identical screens at once, you can always find & replace the .lsml files if you're careful and make backups first. Note that modifying these files directly isn't supported.
I got that error recently. In my case I create a unique ID in my WCF RIA service, but in my screen behind code I must explicitly set a unique ID when I create the object that will later be passed to the WCF RIA Service insert method (this value will then be overwritten with the unique counter ID in the table of the underlying database).
See the sample code for this project:
http://lightswitchhelpwebsite.com/Blog/tabid/61/EntryId/157/A-Visual-Studio-LightSwitch-Picture-File-Manager.aspx

ContentManager.Create does nothing

I am trying to build a service in Orchard that allows me to create content through a custom form on a page. The service and the content type definitions look fine to me, but somehow, eventhough I don't get any errors or other signs in the Orchard log files, creating new content using the IContentManager does nothing for me.
Parts involved
The controller accepting the form values
[HttpPost]
public ActionResult Create(CreateSopViewModel viewModel)
{
if(!ModelState.IsValid)
{
var shape = _shape.CreateContent();
shape.Header = _shape.Parts_Title(Title: "New item");
// Add the original fields to the shape.
shape.Title = viewModel.Title;
shape.Description = viewModel.Description;
shape.InitialComments = viewModel.InitialComments;
return new ShapeResult(this, shape);
}
// Store the new procedure in the database
_service.CreateContentItem(
viewModel.Title,viewModel.Description,viewModel.InitialComments);
// Redirect the user back to the homepage.
return Redirect("~/");
}
The service that contains the CreateContentItem method:
public void CreateContentItem(string title, string description, string initialComments)
{
// Initialize a new content item based on the SOP type
var customPart = _services.ContentManager.New<MyCustomPart>("CustomContentType");
customPart.Description = description;
customPart.Identifier = BuildIdentifier(title);
customPart.ContentItem.As<TitlePart>().Title = title;
_services.ContentManager.Create(customPart.ContentItem);
}
The content part + record
public class MyCustomPart: ContentPart<MyCustomPartRecord>
{
[Required]
public string Identifier
{
get { return Record.Identifier; }
set { Record.Identifier = value; }
}
[Required]
public string Description
{
get { return Record.Description; }
set { Record.Description = value; }
}
}
public class MyCustomPartRecord: ContentPartRecord
{
public virtual string Identifier { get; set; }
public virtual string Description { get; set; }
}
The migration
SchemaBuilder.CreateTable(typeof(MyCustomPartRecord).Name, table => table
.ContentPartRecord()
.Column<string>("Description")
.Column<string>("Identifier"));
ContentDefinitionManager.AlterPartDefinition("StandardOperationalProcedurePart", builder => builder
.Attachable(true));
ContentDefinitionManager.AlterTypeDefinition("CustomContentType", builder => builder
.DisplayedAs("Custom Content Type")
.WithPart("TitlePart")
.WithPart("MyCustomPart")
.Creatable(true));
Question
Again, I don't get any errors, not in the log and not in Visual Studio. However, my new content item doesn't get created or at least, I can't see it in the admin section of the site under Content.
What is going on and how can I debug this behavior?
I had a similar problem, which was solved when I used the overloaded Create method taking a VersionOptions enum value:
content.Create(customPart.ContentItem, VersionOptions.Published);
This should work even if the content item is not creatable, as mine isn't.
I had a similar issue. In my case the item did appear eventually, but not right away.
The solution for me was to do:
_contentManager.Flush();
I was having this issue, in my case it was that I actually had an error in the database (trying to put 100+ characters into a field that would only hold 100!).
I found the error I was getting (null id in Orchard.Indexing.Models.IndexingTaskRecord entry (don't flush the Session after an exception occurs) ), actually masked the issue. I had to go hunt in the logs to find the real problem.
So anyway, my advice is if you see that contentmanager.create seems to be doing nothing, and any errors don't seem to help, check the logs carefully. They can be found in the logs sub-folder of the appdata folder in the main Orchard.Web project. Because as I've found in the last 48 hours, often the answer is there.

Orchard CMS - new properties not updating after migration

I am writing a custom module that retrieves and pushes data directly from the Orchard DB using an injected IRepository.
This works fine until i need to update a content part. I add an update in my migrations class and the update runs through (DB schema updated with default values), however I can't update any of the new values through IRepository. I have to drop down into the NHibernate.ISession to flush the changes through.
This all works fine on a newly created recipe, it's only when i alter a part. Here are the key code snippets:
public class TranslationsPartRecord : ContentPartRecord
{
internal const string DefaultProductName = "Product";
public TranslationsPartRecord()
{
ProductName = DefaultProductName;
}
public virtual string ProductName { get; set; }
}
public class TranslationsPart : ContentPart<TranslationsPartRecord>
{
public string ProductName
{
get { return Record.ProductName; }
set { Record.ProductName = value; }
}
}
public class TranslationsHandler : ContentHandler
{
public TranslationsHandler(IRepository<TranslationsPartRecord> repository)
{
Filters.Add(StorageFilter.For(repository));
}
}
public class Migrations : DataMigrationImpl
{
public int Create()
{
SchemaBuilder.CreateTable("TranslationsPartRecord", table => table
.Column<int>("Id", column => column.PrimaryKey().Identity())
.Column("ProductName", DbType.String, column => column.NotNull().WithDefault(TranslationsPartRecord.DefaultProductName))
);
return 1;
}
public int UpdateFrom1()
{
SchemaBuilder.AlterTable("TranslationsPartRecord", table => table.AddColumn("ProductDescription", DbType.String, column => column.NotNull().WithDefault(TranslationsPartRecord.DefaultProductDescription)));
return 2;
}
}
When i add the second property "ProductDescription" in this example, after the update is run the columns appear in the DB but i cannot update them until i recreate the Orchard recipe (blat App_Data and start again).
here's how I am trying to update:
// ctor
public AdminController(IRepository<TranslationsPartRecord> translationsRepository)
{
_translationsRepository = translationsRepository;
}
[HttpPost]
public ActionResult Translations(TranslationsViewModel translationsViewModel)
{
var translations = _translationsRepository.Table.SingleOrDefault();
translations.ProductName = translationsViewModel.ProductName;
translations.ProductDescription = translationsViewModel.ProductDescription;
_translationsRepository.Update(translations);
_translationsRepository.Flush();
}
and here's the NHibernate "fix":
var session = _sessionLocator.For(typeof(TranslationsPartRecord));
var translations = _translationsRepository.Table.SingleOrDefault();
// is translations.Id always 1?
var dbTranslations = session.Get<TranslationsPartRecord>(translations.Id);
dbTranslations.ProductName = translationsViewModel.ProductName;
dbTranslations.ProductDescription = translationsViewModel.ProductDescription;
session.Update(dbTranslations);
session.Flush();
which seems a bit kludgey...
Cheers.
ps i'm still running Orchard 1.3.9
pps after more testing, the NHibernate fix has stopped working now, so perhaps my initial findings were a red herring. It seems as though new properties on the content part are totally ignored by NHibernate when updating/retrieving - as though the object definition is cached somewhere...
If your mappings aren't being updated that is strange. You can try to force it by deleting the mappings.bin in the app_data folder, and restarting the application. Orchard should recreate the nhibernate mappings and save as mappings.bin.
I have ran into the same issue, and the only way around it that I can find is to delete mappings.bin (I don't need to disable and re-enable the module). In fact, this is the answer that I got from Bertrand when I asked why this was happening.
I have logged this as an issue at http://orchard.codeplex.com/workitem/19306. If you could vote this up, then we may get it looked at quicker.
This seems like a similar issue to what I am seeing... I am seeing that when you enable a module, it runs the NHibernate mappings BEFORE running the Migrations..
https://orchard.codeplex.com/workitem/19603
Josh
Update the hash value in the ComputingHash method in the PersistenceConfiguration Class,
updating the hash value may recreate the mappings.bin file.
public class PersistenceConfiguration : ISessionConfigurationEvents
{
public void Created(FluentConfiguration cfg, AutoPersistenceModel defaultModel)
{
DoModelMapping(cfg, defaultModel);
}
public void ComputingHash(Hash hash)
{
hash.AddString("Some_strings_to_update_hash");
}
private void DoModelMapping(FluentConfiguration cfg, AutoPersistenceModel defaultModel)
{
// mappings here....
}
public void Prepared(FluentConfiguration cfg) { }
public void Building(Configuration cfg) { }
public void Finished(Configuration cfg) { }
}

Resources