Question Add Items to a WishList from 2 Separate Models

Mitchelln11

Active member
Joined
Apr 10, 2020
Messages
39
Programming Experience
Beginner
I'm having issues trying to make a wishlist.

I have a database with records of National Parks.
I also have a database with records of Hikers.

I have a button on the Park details view page that is supposed to redirect to an action.
C#:
@Html.ActionLink("Add Park to Wish List", "AddParkToWishList", "Hiker", new { @id = Model.ParkId }, new { @class = "btn btn-default" })

The button is passing the park Id, so I'm already getting that through the parameter.

I created a method to find the logged in user and the current park, but I'm stuck at trying to add items to my database.
C#:
public async Task<ActionResult> AddParkToWishList(int id)
        {
            string currentUser = FindRegisteredUserId(); // AspNetUser Id
            var currentHiker = _context.Hikers.Where(h => h.ApplicationId == currentUser); // Match against Hikers to find logged in user

            // Find current park
            var currentPark = _context.Parks.Where(p => p.ParkId == id).FirstOrDefault();

            HikerParkWishlistViewModel wishlist = new HikerParkWishlistViewModel()
            {
                wishlist.Hiker = currentHiker;
                // STUCK HERE. CAN'T ADD ANYTHING TO JUNCTION TABLE
            }

            await _context.SaveChangesAsync();
            return RedirectToAction("Details");
        }

I'm not sure what my Junction table is supposed to look like.
At first, I had it set up like the following, but was faced with error related to looping through a single object:
C#:
    public class HikerParkWishlist  // Junction table
    {
        [Key]
        public int HikerParkWishlistId { get; set; }

        [ForeignKey("Hiker")]
        public int HikerId { get; set; }
        public Hiker Hiker { get; set; }

        [ForeignKey("Park")]
        public int ParkId { get; set; }
        public string ParkName { get; set; }
        public Park Park { get; set; }
    }

A previous post of mine led me to believe that I should try the following:
C#:
    public class HikerParkWishlist
    {
        [Key]
        public int HikerParkWishlistId { get; set; }

        [ForeignKey("Hiker")]
        public IEnumerable<Hiker> Hiker { get; set; }

        [ForeignKey("Park")]
        public IEnumerable<Park> Parks { get; set; }
    }

Any hiker should be able to add any/all parks to their wishlist.
Any park can exist in in any/all hikers wishlists, so that should be a many-to-many relationship (correct me if that's wrong)

Where am I going wrong?

Current error:
C#:
CS1922 C# Cannot initialize type with a collection initializer because it does not implement 'System.Collections.IEnumerable'

I feel like I should be using IEnumerable, or a list on my action, but I'm not quite sure how to implement that.
 
Last edited:
<tongue-in-cheek>
Part of the issue here is that you are trying to do something object oriented in a relational database way. Welcome to your first experience of the object-relational impedance mismatch. Maybe you should be using an object oriented database instead of wedging objects into a relational database using an ORM like EntityFramework.
</tongue-in-cheek>

I don't use EF, but to the mighty Google, this is how you would go about configuring it to do a many-to-many relationship:
 
And a more succinct response answer in the Microsoft documentation:
 
That did it.

On my junction table:
C#:
    public class HikerParkWishlist
    {
        public int HikerId { get; set; }
        public Hiker Hiker { get; set; }

        public int ParkId { get; set; }
        public string ParkName { get; set; }
        public Park Park { get; set; }
    }


Hiker model:
C#:
public class Hiker
    {
        [Key]
        public int HikerId { get; set; }
        ...
        public List<HikerParkWishlist> Wishlists { get; set; }
    }

Park model:
C#:
    public class Park
    {
        [Key]
        public int ParkId { get; set; }
        ...
        [Display(Name = "Park Name")]
        public string ParkName { get; set; }
        public List<HikerParkWishlist> Wishlists { get; set; }
    }


The controller method:
C#:
        public async Task<ActionResult> AddParkToWishList(int id)
        {

            // Find logged in user to add to the correct wishlist
            string currentUser = FindRegisteredUserId();
            var currentHiker = _context.Hikers.Where(h => h.ApplicationId == currentUser).FirstOrDefault();

            // Find current park
            var currentPark = _context.Parks.Where(p => p.ParkId == id).FirstOrDefault();

            HikerParkWishlist wishlist = new HikerParkWishlist()
            {
                HikerId = currentHiker.HikerId,
                ParkId = currentPark.ParkId,
                ParkName = currentPark.ParkName
            };

            // make sure there are no duplicates
            bool parkExistsInWishlist = _context.HikerParkWishlists.Any(w => w.ParkId == wishlist.ParkId);
            if (parkExistsInWishlist == false)
            {
                _context.HikerParkWishlists.Add(wishlist);
                await _context.SaveChangesAsync();
            }

            return RedirectToAction("Details", "Hiker", new { id = currentHiker.HikerId });
        }

And finally the ApplicationDbContext:
C#:
        public DbSet<HikerParkWishlist> HikerParkWishlists { get; set; }

        protected override void OnModelCreating(ModelBuilder modelBuilder)
        {
            base.OnModelCreating(modelBuilder);
            modelBuilder.Entity<HikerParkWishlist>()
                .HasKey(t => new { t.HikerId, t.ParkId }); // Have to add unique id to Junction Table

            modelBuilder.Entity<HikerParkWishlist>()
                .HasOne(h => h.Hiker)
                .WithMany(l => l.Wishlists)
                .HasForeignKey(hi => hi.HikerId); // Matches HasKey

            modelBuilder.Entity<HikerParkWishlist>()
                .HasOne(p => p.Park)
                .WithMany(w => w.Wishlists)
                .HasForeignKey(pi => pi.ParkId); // Matches HasKey
        }

And that adds content to my Wishlist!

Does that look right? Or does it still look like a bastardized version of what it should be?
 
I recently created a method to add items to a wishlist.

I am using Park and Hiker models, and the wishlist is created to combine values from both tables.

Information is saving to the Wishlist table just fine, but I am getting an error trying to loop through each record.

Park Model:
C#:
 public class Park
    {
        [Key]
        public int ParkId { get; set; }
        [Display(Name = "Park Name")]
        public string ParkName { get; set; }
        ...
        public List<HikerParkWishlist> Wishlists { get; set; }
    }


Hiker Model:
C#:
public class Hiker
    {
        [Key]
        public int HikerId { get; set; }
        ...
        public List<HikerParkWishlist> Wishlists { get; set; }
    }

HikerPark Junction Table:
C#:
    public partial class HikerParkWishlist
    {
        public int HikerId { get; set; }
        public Hiker Hiker { get; set; }

        public int ParkId { get; set; }
        public string ParkName { get; set; }
        public Park Park { get; set; }
    }

Then in my view, I am trying to use a partial view:
C#:
<partial name="../Shared/_WishlistPartialView.cshtml" for="Wishlists" />
The ~View/Shared... method does not work for me

Partial View:
C#:
@model walkinthepark.Models.HikerParkWishlist

<h3 class="text-center">National Parks Wishlist</h3>
@foreach (var item in Model.Wishlists)
    {
        <p>@Html.DisplayFor(modelItem => item.ParkName)</p>
    }

Getting Ienumerator not set to an instance because it returns null error
 
-------------------------
Update:

Added a new list to allow the foreach loop to enumerate through
Updated Partial View:
C#:
@model walkinthepark.Models.HikerParkWishlist

<h3 class="text-center">National Parks Wishlist</h3>
@{
    // Create an empty list for the marker values to add into
    List<walkinthepark.Models.HikerParkWishlist> wishlist = new List<HikerParkWishlist>();
}

@foreach (var item in wishlist)
{
    HikerParkWishlist wishlistItems = HikerParkWishlist();

    wishlistItems.ParkName = item.ParkName;
    wishlist.Add(wishlistItems);

    <p>@Html.DisplayFor(modelItem => item.ParkName)</p>
}

The <h3> now displays, but the loop doesn't return anything, so that must be wrong.
 
The code is technically the same, and so I've merged your topics for you, as there is no need to create a new topic when you're troubleshooting the same problematic code. Unless the code and the problem are completely different.

I am sorry, I don't have time at them moment to review your problem. If I find time later, tonight I will take a look.
 
Back
Top Bottom