Storing data using Infoset

Topics: Customizing Orchard, General
May 26, 2014 at 7:17 AM
Well, I've take a look more attentively at the new feature of Orchard 1.8 - storing data using Infoset.
According this article I don't have to create additional tables for my parts, I just need to update my getter and setters to use Store and Retrieve mehtods. All my data now can be stored in one table. I was happy!
Until I tried to store there more complex type than just dates, strings etc.
For example, I have this content part:
    public partial class CompanyPart: ContentPart
    {
        [Required]
        public String ShortName 
        { 
            get { return this.Retrieve(x=>x.ShortName); }
            set { this.Store(x=>x.ShortName, value); }
        }
        [Required]
        public String Name 
        { 
            get { return this.Retrieve(x=>x.Name); }
            set { this.Store(x=>x.Name, value); }
        }
    }
and I want it to be a part of my second part, like this:
    public partial class ApplicationFormTypePart: ContentPart
    {
        [Required]
        public String Name 
        { 
            get { return this.Retrieve(x=>x.Name); }
            set { this.Store(x=>x.Name, value); }
        }
        public CompanyPart Company 
        { 
            get { return this.Retrieve(x=>x.Company); }
            set { this.Store(x=>x.Company, value); }
        }
    }
But when I'm trying to save ApplicationFormTypePart, I've got an exception
System.NotSupportedException: Could not handle type CompanyPart
at Orchard.ContentManagement.XmlHelper.ToString[T](T value) in d:\work\Cabinet\src\Orchard\ContentManagement\XmlHelper.cs:line 223
at Orchard.ContentManagement.XmlHelper.Attr[T](XElement el, String name, T value) in d:\work\Cabinet\src\Orchard\ContentManagement\XmlHelper.cs:line 56
at Orchard.ContentManagement.InfosetHelper.Store[TProperty](InfosetPart infosetPart, String partName, String name, TProperty value, Boolean versioned) in d:\work\Cabinet\src\Orchard\ContentManagement\InfosetHelper.cs:line 111
at Orchard.ContentManagement.InfosetHelper.Store[TPart,TProperty](TPart contentPart, Expression`1 targetExpression, TProperty value, Boolean versioned) in d:\work\Cabinet\src\Orchard\ContentManagement\InfosetHelper.cs:line 91
Perhaps I miss something or maybe there is another way to get what I want?
Developer
May 26, 2014 at 4:39 PM
Any more complex and not .NET types won't work OOTB since Orchard doesn't know how to store them. You could write de/serialization logic in your properties' getter/setters, see: http://weblogs.asp.net/bleroy/archive/2013/11/04/the-shift-how-orchard-painlessly-shifted-to-document-storage-and-how-it-ll-affect-you.aspx
Marked as answer by bonzaster on 5/26/2014 at 11:32 AM
May 26, 2014 at 7:32 PM
Piedone wrote:
Any more complex and not .NET types won't work OOTB since Orchard doesn't know how to store them. You could write de/serialization logic in your properties' getter/setters, see: http://weblogs.asp.net/bleroy/archive/2013/11/04/the-shift-how-orchard-painlessly-shifted-to-document-storage-and-how-it-ll-affect-you.aspx
Thank you, Piedone!
Jul 7, 2014 at 8:56 AM
Hello Piedone,

Could you pls provide a sample code to handle ContentRecord type in this situation? I have the same problem like bonzaster and still cannot solve it after read the article.

Thank in advance.
Developer
Jul 7, 2014 at 11:35 AM
I'm not sure I understand. You don't save ContentRecord derivatives to infoset.
Jul 8, 2014 at 3:27 AM
So basically, we cannot save ContentPartRecord to InfoSet, right? This is from Building a 1-N Relationship documentation:

Before shift
namespace RelationSample.Models {
    public class AddressPart : ContentPart<AddressPartRecord> {
        public string Address {
            get { return Record.Address; }
            set { Record.Address = value; }
        }
        public string City {
            get { return Record.City; }
            set { Record.City = value; }
        }
        public StateRecord State {
            get { return Record.StateRecord; }
            set { Record.StateRecord = value; }
        }
        public string Zip {
            get { return Record.Zip; }
            set { Record.Zip = value; }
        }
    }
}
After shift
namespace RelationSample.Models {
    public class AddressPart : ContentPart<AddressPartRecord> {
        public string Address {
            get { return Retrieve(r => r.Address); }
            set { Store(r => r.Address, value); }
        }
        public string City {
            get { return Retrieve(r => r.City); }
            set { Store(r => r.City, value); }
        }
        public StateRecord State {
            get { return Record.StateRecord; }
            set { Record.StateRecord = value; }
        }
        public string Zip {
            get { return Retrieve(r => r.Zip); }
            set { Store(r => r.Zip, value); }
        }
    }
}
Jul 8, 2014 at 6:16 AM
Yes, this should work. But don't forget, StateRecord here is not ContentPart.



2014-07-08 5:27 GMT+03:00 azltdanh <[email removed]>:

From: azltdanh

So basically, we cannot save ContentPartRecord to InfoSet, right? This is from Building a 1-N Relationship documentation:

Before shift
namespace RelationSample.Models {
    public class AddressPart : ContentPart<AddressPartRecord> {
        public string Address {
            get { return Record.Address; }
            set { Record.Address = value; }
        }
        public string City {
            get { return Record.City; }
            set { Record.City = value; }
        }
        public StateRecord State {
            get { return Record.StateRecord; }
            set { Record.StateRecord = value; }
        }
        public string Zip {
            get { return Record.Zip; }
            set { Record.Zip = value; }
        }
    }
}
After shift
namespace RelationSample.Models {
    public class AddressPart : ContentPart<AddressPartRecord> {
        public string Address {
            get { return Retrieve(r => r.Address); }
            set { Store(r => r.Address, value); }
        }
        public string City {
            get { return Retrieve(r => r.City); }
            set { Store(r => r.City, value); }
        }
        public StateRecord State {
            get { return Record.StateRecord; }
            set { Record.StateRecord = value; }
        }
        public string Zip {
            get { return Retrieve(r => r.Zip); }
            set { Store(r => r.Zip, value); }
        }
    }
}

Read the full discussion online.

To add a post to this discussion, reply to this email ([email removed])

To start a new discussion for this project, email [email removed]

You are receiving this email because you subscribed to this discussion on CodePlex. You can unsubscribe on CodePlex.com.

Please note: Images and attachments will be removed from emails. Any posts to this discussion will also be available online at CodePlex.com


May 25, 2015 at 6:33 PM
The new version of Building a 1-N Relationship documentation is wrong. It doesn't work with Store and Retrieve methods. You will get this exception:
"System.NotSupportedException: Could not handle type StateRecord" I think because the xmlhelper doesn't know how to make a string from it. Unfortunately I couldn't find a single example in Orchard documentation on how it could be done.

You could look at Bertrand Nwazet module https://bitbucket.org/bleroy/nwazet.commerce/src/7a62fe2a7702983542a2fa7613f971084d24ff96/Models/ProductPart.cs?at=default at this line:
   public IEnumerable<PriceTier> PriceTiers {
        get {
            var rawTiers = Retrieve<string>("PriceTiers");
            return PriceTier.DeserializePriceTiers(rawTiers);
        }
        set {
            var serializedTiers = PriceTier.SerializePriceTiers(value);
            Store("PriceTiers", serializedTiers ?? "");
        }
    }
And this in PriceTiers class:
   public static IEnumerable<PriceTier> DeserializePriceTiers(string priceTiers) {
        if (priceTiers != null) {
            return priceTiers.Split(new[] { ';', ' ' }, StringSplitOptions.RemoveEmptyEntries)
                .Select(t => t.Split('=')).Select(st => new PriceTier() {
                    Quantity = Convert.ToInt32(st[0]),
                    Price = (!st[1].EndsWith("%") ? st[1].ToDouble() : null),
                    PricePercent = (st[1].EndsWith("%") ? st[1].Substring(0, st[1].Length - 1).ToDouble() : null)
                })
                .OrderBy(t => t.Quantity)
                .ToList();
        }
        else {
            return new List<PriceTier>();
        }
    }

    public static string SerializePriceTiers(IEnumerable<PriceTier> priceTiers) {
        if (priceTiers != null) {
            return string.Join(";", priceTiers.Select(t => t.Quantity + "=" + (t.Price != null ? t.Price.ToString() : t.PricePercent.ToString() + "%")));
        }
        else {
            return string.Empty;
        }
    }

I don't know how to report the documentation issues. But I hope that author(s) reads this conversation and make the necessary changes to it. I guess that example is one of the most important part of documentation to learn the Orchard.
May 28, 2015 at 2:39 PM
@AsaDeveloper, there is an OrchardDocs on Github I believe. You can post an "issue" there maybe and the authors will likely see it and, if necessary, make the changes.