Creating new record with IRepository

Topics: Troubleshooting, Writing modules
May 5, 2011 at 10:00 PM
Edited May 6, 2011 at 5:58 PM

I'm re-writing this question because I don't think I worded it very well and I've also done more research to try to better understand the use of IRepository and creating records.

So...my question now is simply how to create a record using IRepository when the ContentPartRecord contains an item that is another ContentPartRecord.

ContentPartRecord1
    UserId
    Date
    ContentPartRecord2

I'm using IRepository as such

_ContentPartRecord1Repository.Create(new ContentPartRecord1
{
    UserId = Services.WorkContext.CurrentUser.Id,
    Date = DateTime.Now,
    ContentPartRecord2 = ???
});

 I thought I needed to pass it an actual ContentPartRecord2 like such

ContentPartRecord2 = _ContentPartRecord2Repository.Fetch(r => r.Id = 1).Single()

or even create that ContentPartRecord2 item separately as a var and use it in the .Create method as such

var cpr2 = _ContentPartRecord2Repository.Fetch(r => r.Id = 1).Single();

None of these methods seem to be correct.

Can someone give me an idea on how this works? Thanks!

May 6, 2011 at 9:10 PM
Edited May 6, 2011 at 10:52 PM

I updated my question...hopefully its a little better worded

My instinct with this is to do the following

 

ContentPartRecord2 = _contentpartrecord2repository.Fetch(r => r.Id = 1).FirstOrDefault()

but that doesn't seem to be the right approach.

May 6, 2011 at 11:19 PM
Edited May 6, 2011 at 11:38 PM

Okay...I actually got an error that somewhat makes sense, I just need to figure out what it actually means.

 

attempted to assign id from null one-to-one property: ContentItemRecord

I'm guessing that probably means something isn't setup properly in my model or migration. The table I'm adding to was created as a ContentPart, but the Id column isn't set to be an Identity column. I assumed from other posts that Orchard would create an Id for this column automatically since my migration has it defined as a ContentPart.

Should I be overriding the migration and setting that column to Identity? I tried manually setting the Id in the Repository.Create() method, but I got the same error. I also tried setting ContentPartRecord2 to null since the database table says that column allows nulls. Neither attempt worked.

May 8, 2011 at 10:05 PM

The Id column is automatically created when you specify a record as a content part record (in migrations).

Can you copy in all your code, i.e. the record definitions as well as your migrations, maybe I can see what's going wrong.

May 9, 2011 at 4:39 PM

Sure thing...and thanks for helping me try to figure this out.

Let's start with something really simply as I can't even get this to work and get the same error. Here is the model code:

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

namespace Raptor.MyRealityPicks.Models
{
    public class ShowRecord : ContentPartRecord
    {
        public virtual string show_nm { get; set; }
        public virtual bool show_act_ind { get; set; }
        public virtual string show_img { get; set; }
    }

    public class ShowPart : ContentPart<ShowRecord>
    {
        [Required]
        [DisplayName("Show Name")]
        public string show_nm
        {
            get { return Record.show_nm; }
            set { Record.show_nm = value; }
        }

        [Required]
        [DisplayName("Active?")]
        public bool show_act_ind
        {
            get { return Record.show_act_ind; }
            set { Record.show_act_ind = value; }
        }

        public string show_img
        {
            get { return Record.show_img; }
            set { Record.show_img = value; }
        }
    }
}

Here is the migration entry for that table:

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;

namespace Raptor.MyRealityPicks {
    public class Migrations : DataMigrationImpl {

        public int Create() {
			// Creating table ShowRecord
			SchemaBuilder.CreateTable("ShowRecord", table => table
				.ContentPartRecord()
                                .Column("show_nm", DbType.String, column => column.WithLength(50))
				.Column("show_act_ind", DbType.Boolean)
                                .Column("show_img", DbType.String, column => column.WithLength(255))
			);

ContentDefinitionManager.AlterPartDefinition("ShowPartRecord", builder => builder.Attachable());

            return 1;
        }
    }
}

Here is the very basic Handler code for that model record:

using Orchard.ContentManagement.Handlers;
using Raptor.MyRealityPicks.Models;
using Orchard.Data;

namespace Raptor.MyRealityPicks.Handlers
{
    public class ShowHandler : ContentHandler
    {
        public ShowHandler(IRepository repository)
        {
            Filters.Add(StorageFilter.For(repository));
        }
    }
}

And again...here is the code from the Controller that throws the null one-to-one property:

        [HttpPost]
        public ActionResult Submit()
        {
            _showRepository.Create(new ShowRecord
            {
                show_nm = "ShowName",
                show_act_ind = true,
                show_img = "showimage.png"
            });

            return Json(new { success = true, message = "Success" }, JsonRequestBehavior.AllowGet);
        }

As you can see, at this point I'm not even trying to pass anything to the function, and this insert doesn't use any other content part records...it's just a simple write to the table.

Thanks again for the help!

Coordinator
May 9, 2011 at 4:57 PM

The handler is wrong. It should inject an IRepository<ShowRecord>.

May 9, 2011 at 5:01 PM
Edited May 9, 2011 at 5:04 PM

That's weird...I swore I copied and pasted the entire code...here it is again

 

using Orchard.ContentManagement.Handlers;
using Raptor.MyRealityPicks.Models;
using Orchard.Data;

namespace Raptor.MyRealityPicks.Handlers
{
    public class ShowHandler : ContentHandler
    {
        public ShowHandler(IRepository<ShowRecord> repository)
        {
            Filters.Add(StorageFilter.For(repository));
        }
    }
}

 

That's what it actually is...not sure why the <ShowRecord> didn't paste in. This is correct, yes?

I double checked all the other handlers and they all inject the correct models.

May 9, 2011 at 5:38 PM
Edited May 9, 2011 at 5:38 PM

That's Codeplex; it sometimes screws up angle brackets (presumably missing an HtmlEncode somewhere...)

The only thing I can possibly think that looks wrong with all of that, is that ShowRecord should be called ShowPartRecord. In fact I'm pretty certain that's it; by convention you need Record appended onto your model name for NHibernate to persist it.

May 9, 2011 at 5:40 PM

Oh, this line is wrong as well:

ContentDefinitionManager.AlterPartDefinition("ShowPartRecord", builder => builder.Attachable());

That should be "ShowPart" not "ShowPartRecord" - the name of the part should be the model name not record name.

May 9, 2011 at 6:07 PM

Thanks for looking at it. I'll change it back to ShowPart and ShowPartRecord. I originally had them named this, but tried to simplify it a bit. If that's causing the problem though, I'll change them back and see what happens. I'll post in a few with the results. Thanks again.

May 9, 2011 at 6:34 PM

Negative...same exact issue. I renamed all the Records to PartRecords in the Models, modified the Handlers and the Migration and any other files (Controllers, ViewModels), removed the tables and had the Migration recreate them by updating my module in Orchard Admin.

It has to be something really simple that I'm missing that's throwing that error and preventing me from writing to the tables. Any other files with settings you can think of  that I might need to check?

May 9, 2011 at 6:34 PM
Edited May 9, 2011 at 6:34 PM

sorry...double click on accident. Too much caffeine I suppose =)

May 9, 2011 at 6:42 PM

How about my Controller declaration for the repository? Does this look correct?

namespace Raptor.MyRealityPicks.Controllers
{
     [Themed]
     public class ShowController : Controller
     {
          public IOrchardServices Services { get; set; }

          public Localizer T { get; set; }

          private readonly IRepository<ShowPartRecord> _showRepository;

          public ShowController(IRepository<ShowPartRecord> showRepository, IOrchardServices services)
          {
               _showRepository = showRepository;

               Services = services;
               T = NullLocalizer.Instance;
          }
     }
}

Coordinator
May 9, 2011 at 7:40 PM

That looks fine mechanically althout I would encourage you to move data access code into a service class, and then inject that into your controller.

May 9, 2011 at 8:11 PM

yes...that is the plan once I can figure out how to get the code to work.

Well...I'm at a complete loss on where to go from here in order to get this working. I moved the code to create the record right into the Index action so it would just run when I loaded the page...trying to isolate it as much as possible. The error returned is from NHibernate...

attempted tp assign id from null one-to-one property: ContentItemRecord

NHibernate.Id.IdentifierGenerationException:

Perhaps the problem is in the relationships...most of the relationships should be one to many...for example

The table I'm trying to write to now is Shows. The show_id will be used as a value in most other tables...for example Seasons, which will be laid out as follows

Id, season_nbr, season_nm, show_id

and can have multiple entries for the same show_id

1, 22, Season 22, 1
2, 23, Season 23, 1
3, 24, Season 24, 1

When I created my Season model, I used

public virtual int season_nbr { get; set; }
public virtual string season_nm { get; set; }
public virtual ShowPartRecord show { get; set; }

public int season_nbr
{
     get { return Record.season_nbr; }
     set { Record.season_nbr = value; }
}

public string season_nm
{
     get { return Record.season_nm; }
     set { Record.season_nm = value; }
}

public ShowPartRecord show
{
     get { return Record.show; }
     set { Record.show = value; }
}
Is that correct for this type of relationship?

Coordinator
May 9, 2011 at 8:26 PM

Hold on. Now you have a relationship? You didn't seem to have that before. To rewind a bit, I don't understand why you are going through the repository to create the record if you have a part for the record. Why don't you do _contentManager.New<ShowPart>("Show")? If you just create the part through the repository, as it is a part record, it will create an orphan part, which will result in the error you're seeing.

Now if you want all that stuff to work independently of parts and content items, don't make that thing a content part record.

May 9, 2011 at 8:54 PM

That's probably where my disconnect has been with this then...There really isn't a need for them to be content items, so I'll try removing all of that. If that doesn't work, I'll go the other route you mentioned and use _contentManager.

I didn't realize that using irepository on content items would do that. Thanks for clearing that up.

Coordinator
May 9, 2011 at 9:25 PM

Well, the part that really does it is in the migration, when you make it a content part record, as well as inheriting from ContentPartRecord. You'll have to replace that with a declaration of the Id in the migration and the record.

May 9, 2011 at 9:52 PM

I decided to try the ContentPart method first since I'll have to change a lot less code.

I seem to be getting a Specified cast is not valid error when I try this though:

var myshow = Services.ContentManager.New<ShowPart>("Show");
myshow.show_nm = "Show Name";
myshow.show_act_ind = true;
myshow.show_img = "image";
Services.ContentManager.Create(myshow);

Am I missing a step somewhere? On the New<ShowPart>("Show") line, is "Show" supposed to correspond to something I've already created. Intellisense says ContentType, so I'm not sure if this is something I have created or should be creating somewhere.

Coordinator
May 9, 2011 at 10:02 PM

"Show" would be a content type that you would have to create in your migration.

May 9, 2011 at 11:22 PM

I'm trying both routes to see which I like better working with...

If I go the IRepository route, do I need to define a Handler, and if so, what do I need to change to get it to work? Currently it looks like this

namespace Raptor.MyRealityPicks.Handlers
{
    public class ShowHandler : ContentHandler
    {
        public ShowHandler(IRepository<ShowRecord> repository)
        {
            Filters.Add(StorageFilter.For(repository));
        }
    }
}

But of course that throws an error because it's no longer a ContentPart.
Without a Handler though, I don't seem to be pulling anything back from the database.
Coordinator
May 9, 2011 at 11:44 PM

No, you don't need to define a handler but where are you querying from?

May 9, 2011 at 11:53 PM

I had the migration create the tables inside the Orchard database and the query itself is sitting in my controller right now. Is that what your question was referring to?

Am I missing something that's wiring up IRepository to the tables? I've been going through the Vandelay ThemePicker module to see what I might be missing.

May 9, 2011 at 11:59 PM

The error is again from NHibernate...NHibernate.MappingException: No persister for: Raptor.MyRealityPicks.Models.Show

Coordinator
May 10, 2011 at 12:01 AM

What's Show now?

May 10, 2011 at 12:07 AM
Edited May 10, 2011 at 12:07 AM

Show is a model class

using System;

namespace Raptor.MyRealityPicks.Models
{
    public class Show
    {
public virtual int show_id { get; set; } public virtual string show_nm { get; set; } public virtual bool show_act_ind { get; set; } public virtual string show_img { get; set; } }

which I have the migration creating a table for

namespace Raptor.MyRealityPicks {
    public class Migrations : DataMigrationImpl {

        public int Create() {
			// Creating table ShowRecord
			SchemaBuilder.CreateTable("Show", table => table
				.Column("show_id"), DbType.Int32, column => column.PrimaryKey().Identity()
                                .Column("show_nm", DbType.String, column => column.WithLength(50))
				.Column("show_act_ind", DbType.Boolean)
                                .Column("show_img", DbType.String, column => column.WithLength(255))
			);

            return 1;
        }
    }
}

and I'm trying to query using IRepository

namespace Raptor.MyRealityPicks.Controllers
{
     [Themed]
     public class ShowController : Controller
     {
          public IOrchardServices Services { get; set; }

          public Localizer T { get; set; }

          private readonly IRepository<Show> _showRepository;

          public ShowController(IRepository<Show> showRepository, IOrchardServices services)
          {
               _showRepository = showRepository;

               Services = services;
               T = NullLocalizer.Instance;
          }
     }
}
May 10, 2011 at 12:12 AM

Does my Id column have to be named simply Id. I named it show_id...don't know if that's a requirement for it to be named just Id

Coordinator
May 10, 2011 at 12:17 AM

Name it Id. The code that you included from your controller unfortunately does not include the intresting part, i.e. the querying.

May 10, 2011 at 12:24 AM

Renaming them all now. Sorry about the queries, I was working on a different machine and couldn't copy/paste.

Here are a couple of queries. thisShow is defined in the ViewModel as type Show

            ShowViewModel show = new ShowViewModel();

            show.thisShow = (from shows in _showRepository.Table
                             where shows.show_nm == showName && shows.show_act_ind == true
                             select shows).FirstOrDefault();

and here's a create query

            _showRepository.Create(new Show
            {
                show_nm = "Show Name",
                show_act_ind = true,
                show_img = "image"
            });

Coordinator
May 10, 2011 at 12:35 AM

And what happens when this runs?

May 10, 2011 at 12:38 AM

I get the error message: NHibernate.MappingException: No persister for: Raptor.MyRealityPicks.Models.Show

Coordinator
May 10, 2011 at 12:40 AM

Did you clear the database and re-enable the feature so your migration gets run?

May 10, 2011 at 12:43 AM

Yes sir...I'm actually running through that step again since I changed all the Primary Keys to just Id. Two more minutes and I'll have an update

May 10, 2011 at 1:03 AM

I stripped it down and got the query running from a single table, however, now when I try to join to another table through a query, it's telling me the column name is not valid.

So...here is the Show model

namespace Raptor.MyRealityPicks.Models
{
    public class Show
    {
        public virtual int Id { get; set; }
        public virtual string show_nm { get; set; }
        public virtual bool show_act_ind { get; set; }
        public virtual string show_img { get; set; }
    }
} 

and here is the Season model

namespace Raptor.MyRealityPicks.Models
{
    public class Season
    {
        public virtual int Id { get; set; }
        public virtual int season_nbr { get; set; }
        public virtual string season_nm { get; set; }
        public virtual bool season_act_ind { get; set; }
        public virtual Show show { get; set; }
    }
} 

the query that connects them is

ShowViewModel showViewModel = new ShowViewModel();

showViewModel.thisSeason = (from seasons in _seasonRepository.Table
    where seasons.show.show_nm == showName && seasons.season_act_ind == true
    select seasons).FirstOrDefault();

and it's throwing an error:

The column name is not valid. [ Node name (if any) = this_,Column name = show_id ]

I must not be naming something correctly for it to figure our the relationships.

May 10, 2011 at 1:06 AM

and the really good news is that I was able to write a record to the table using IRepository now! WOO HOO!

Now just need to figure out how to name things so the relationships work properly. ALMOST THERE!

Coordinator
May 10, 2011 at 1:10 AM

Not sure if it would have an impact but your property names are going against all C# coding guidelines. Could you fix that first? It would make it easier to debug into this.

May 10, 2011 at 7:07 AM
Edited May 10, 2011 at 7:23 AM

Okay...I'm working on getting this cleaned up. Hopefully this helps:

ShowRecord Model

 

using System;

namespace Raptor.MyRealityPicks.Models
{
    public class ShowRecord
    {
        public virtual int Id { get; set; }
        public virtual string ShowName { get; set; }
        public virtual bool ShowActive { get; set; }
        public virtual string ShowImage { get; set; }
    }
}

 

SeasonRecord Model

 

using System;

namespace Raptor.MyRealityPicks.Models
{
    public class SeasonRecord
    {
        public virtual int Id { get; set; }
        public virtual int SeasonNumber { get; set; }
        public virtual string SeasonName { get; set; }
        public virtual bool SeasonActive { get; set; }
public virtual ShowRecord Show { get; set; } } }

 

ShowViewModel ViewModel

 

using System;
using System.Collections.Generic;
using Raptor.MyRealityPicks.Models;

namespace Raptor.MyRealityPicks.ViewModels
{
    public class ShowViewModel
    {
        public ShowRecord thisShow { get; set; }
        public SeasonRecord thisSeason { get; set; }
        public EpisodeRecord nextEpisode { get; set; }
        public EpisodeRecord thisEpisode { get; set; }
        public IEnumerable<int> Episodes { get; set; }
        public IEnumerable<CategoryRecord> Categories { get; set; }
        public IEnumerable<TeamMembersRecord> thisCast { get; set; }
    }
}

 

And here's the query that's throwing the error

 

showViewModel.thisSeason = (from seasons in _seasonRepository.Table
                   where seasons.Show.ShowName == "ShowName" && seasons.SeasonActive == true
                   orderby seasons.SeasonNumber descending
                   select seasons).FirstOrDefault();

I was under the impression that I would be able to get to the other table's properties using standard LINQ, but this setup throws the error:

The column name is not valid. [ Node name (if any) = this_,Column name = Show_id ]

It's specifically the seasons.Show.ShowName that is causing the error...and it sounds to me that it's having a problem connecting the two tables because it's looking for a column named Show_id, but the key in that table is simply Id. I tried changing the primary keys to Show_id, and Season_id, but that didn't work...it threw other errors and I'm pretty sure it's expecting that primary key column to just be Id.

May 10, 2011 at 8:06 AM

AH HA! Think I got it figured out...

So...even though the model is setup like this:

    public class SeasonRecord
    {
        public virtual int Id { get; set; }
        public virtual int SeasonNumber { get; set; }
        public virtual string SeasonName { get; set; }
        public virtual bool SeasonActive { get; set; }
        public virtual ShowRecord Show { get; set; }
    }

The DataMigration is setup like this:

            // Creating table SeasonRecord
            SchemaBuilder.CreateTable("SeasonRecord", table => table
                .Column("Id", DbType.Int32, column => column.PrimaryKey().Identity())
		.Column("SeasonNumber", DbType.Int32)
                .Column("SeasonName", DbType.String, column => column.WithLength(50))
		.Column("SeasonActive", DbType.Boolean)
		.Column("Show_Id", DbType.Int32)
	    );
I thought that what I named the ShowRecord in the model was supposed to match what I named the column in the migration. But if I name the Show Record Show in the model and the column Show_Id in the migration, it works.

May 10, 2011 at 4:37 PM

Just wanted to say thanks for all of your guys help and patience while I figure all this out. It's definitely been a learning experience getting used to MVC and Orchard and I'm pretty sure I wouldn't be able to do it without all of your assistance. I believe I'm on the right track to getting my Module done, even though I'm sure I'll have more questions here and there on other things. Thanks again!

Coordinator
May 10, 2011 at 6:50 PM

I'm glad we were able to help you and that you got it working.

Sep 9, 2011 at 9:27 PM
Edited Sep 9, 2011 at 9:43 PM

Hey, I tried to do the same thing and it worked!. But now, have you tried to make this a widget part? It just keeps throwing "base {System.SystemException} = {System.NullReferenceException: Object reference not set to an instance of an object.   at Orchard.Widgets.Models.WidgetPart.get_LayerId()} "

I'm new at Codeplex, so I don't really know if I should open a new discussion for this, my apologies for that.

Thanks in advance!

Oct 7, 2011 at 2:00 AM

That is a fantastic exchange. I've learn't more from this than any documentation.