IList in Apps/Articles/Pages/Article.cs

Feb 12, 2015 at 10:44 AM
Edited Feb 12, 2015 at 12:15 PM
Hello,
i would like to add an IList in Apps/Articles/Pages/Article.cs

i did it like this
namespace MrCMS.Web.Apps.Articles.Pages
{
    public class Article : TextPage, IBelongToUser
    {
        [AllowHtml]
        [StringLength(160, ErrorMessage = "Abstract cannot be longer than 500 characters.")]
        public virtual string Abstract { get; set; }

        [DisplayName("Author")]
        public virtual User User { get; set; }

        public virtual IList<MediaCategory> ArticleMediaCategoryList { get; set; }
        
    }
}
Am I wrong with it?
how can i accomplish what I want to do?

In DB I'd like to have a table with saved relations Article (many)<> ArticleMediaCategoryList (many)


Also I wrote two services in
Apps/Articles/Services/ArticleService.cs
public void AddMediaCategory(int article, int mediaCategory)
        {
            var art = _session.Get<Article>(article);
            var mc = _session.Get<MediaCategory>(mediaCategory);
            if ((art != null) && (mc != null))
            {
                art.ArticleMediaCategoryList.Add(mc);
                _session.Transact(session => session.SaveOrUpdate(art));
            }            
        }
        
        public void RemoveMediaCategoryFromArticle(int article, int mediaCategory)
        {
            var art = _session.Get<Article>(article);
            var mc = _session.Get<MediaCategory>(mediaCategory);
            if ((art != null) && (mc != null))
            {
                art.ArticleMediaCategoryList.Remove(mc);
                _session.Transact(session => session.SaveOrUpdate(art));
            }
        }
but those are not writing anything on DB


I'm a beginner with asp.net mvc.

thank you,
Andrea
Feb 12, 2015 at 10:48 AM
Edited Feb 12, 2015 at 12:15 PM
let me know
Coordinator
Feb 12, 2015 at 1:15 PM
Hi Andrea,

To deal with a many-to-many relationship, you have to take a slightly more complex approach than just adding an IList, unfortunately, due to the way that NHibernate/FluentNhibernate work. An example of this would be the relationship between Documents (Entities.Documents/Document.cs) and Tags (Entities.Documents/Tag.cs) in the MrCMS library.

If you're happy to just change the file in the core for your project, change both ends to have an Iesi.Collections.Generic.ISet<T> where T is the opposite side of the association. You'll then need to add to the ArticleOverride.cs, something along the lines of
            mapping.HasManyToMany(document => document.Tags).Table("DocumentTags").Cascade.SaveUpdate();
from DocumentOverride.cs, changing the types, property, and desired join-table name to match up.

Looking at that, you may be able to get away with not modifying the MediaCategory class in the core, as there's not a corresponding override required for Tags, although the existence of Tag.Documents may be what it uses to wire it up. I'd suggest trying it without, then if that doesn't work, add the ISet<Article> on MediaCategories and see what happens.

The other alternative is to create the entity as a join entity explicitly and use it, with a simple IList on both collections to that, and hopefully it should auto-magically work via convention, though you lose the niceness of just having the collection on the object.

Hope this makes sense, shout if you need anything clearing up, and I'll try to help.

Gary
Feb 12, 2015 at 1:47 PM
At the moment I did it like this:
namespace MrCMS.Web.Apps.Faqs.Entities
{
    public class ArticleGallery : SiteEntity
    {
        public virtual Article Article { get; set; }

        public virtual MediaCategory MediaCategory { get; set; }
    }
}
I have taken faqapp as a "model" and I had understood something more useful to develop what I'd like to do.

I add this
public class ArticleOverride : IAutoMappingOverride<Article>
    {
        public void Override(AutoMapping<Article> mapping)
        {
            mapping.HasManyToMany(document => document.MediaCategoryList).Table("ArticleGallery").Cascade.SaveUpdate();
        }
    }
it has been created a new table called ArticleGallery with this fields:
Id / CreatedOn / UpdatedOn / IsDeleted / ArticleId / SiteId
the only fields used are Id (identity) and ArticleId -> (the others are NULL)
where is the join between Articles and Gallery -> I havent found it in the Document Table?

So, for the moment it works: i can add many galleries to my articles.

I wish this is the right thing to do. what do you think about my choice?

The next step is to follow your instructions to develop it like it should be developed.

thank you very much.

A
Coordinator
Feb 12, 2015 at 1:58 PM
I think you may have kind of combined the two approaches there, which may be causing the unusual behavior. If you're using .HasManyToMany(), you don't need the explicit ArticleGallery entity, just to do the Override as you've done there. What that should do, is generate a join table in the style of DocumentTags, with a shared primary key, and foreign keys to the entities.

I'd suggest removing the ArticleGallery SiteEntity, and dropping the ArticleGallery table, (MrCMS will only do non-destructive updates to the schema, as a safety precaution), and letting it be regenerated, to see what happens.

Gary
Feb 12, 2015 at 3:27 PM
I'm sorry Gary,
i can't understand this error:
i did what you suggested, I duplicated /Entities/Documents/Tag.cs so I created FotoGallery.cs.
I modified Document.cs as you suggest and all the other necessary thing to fix errors.

Now I'm writing the ArticleService.cs
public void Add(int articleId, int mediaCategoryId)
        {
            var article = _session.Get<Article>(articleId);
            var mediacategory = _session.Get<MediaCategory>(mediaCategoryId);
            if ((article != null) && (mediacategory != null))
            {
                var fotogallery = GetFotoGallery(mediaCategoryId);
                if (fotogallery == null)
                {
                    fotogallery = new FotoGallery();
                    fotogallery.Documents.Add(article);
                    fotogallery.MediaCategory = mediacategory;
                    _session.Transact(session => session.Save(fotogallery));
                }
                if (!article.FotoGalleries.Contains(fotogallery))
                {
                    article.FotoGalleries.Add(fotogallery);
                    fotogallery.Documents.Add(article);
                }
            }
        }
but this error is thrown:
Found shared references to a collection: MrCMS.Web.Apps.Articles.Pages.Article.FotoGalleries
what can I do now?

thank you
A
Feb 13, 2015 at 8:31 AM
ok, I found the problem:
I inserted mapping.HasManyToMany(.....) both in
Lib/DBConfiguration/Overrides/DocumentOverride.cs
and
Sites/MrCMS.Web/Apps//Articles/DbConfiguration/ArticleOverride.cs

I removed the last one,.

I wish this could help someone else.

Thank you,
A
Coordinator
Feb 13, 2015 at 8:40 AM
Hi Andrea,

That makes sense, as I've done the same myself when explicitly mapping a property at different hierarchy levels, I guess that's the equivalent error for collections. I might as well mention in here for anyone reading it that if you override a property mapping for two levels, for if you're say wanting to restrict a string/nvarchar field's length, you get a slightly obtuse exception about indexes being out of range when trying to save (can't remember the exact phrasing off the top of my head). The reason for that is that on the subclass, you essentially have mapped the property twice, so when it loops through the mapped field list, it goes past the length of the array of the available fields, or something along those lines (not too au fait with the inner workings of NHibernate).

Anyway, just shout if you have any more bother.

Gary