Skip to content
Nubgrammer
Menu
  • Home
  • Contact Me
Menu

Write an RSS Feed Aggregator, The FeedsManager class

Posted on November 13, 2017September 24, 2018 by Tyler Sells

TL;DR:

We’re getting there.  In this one, we build the Feed class and the FeedsManager class that manages all of the feeds the user wants to view in the Aggregator.

Code is here, video is here (and down below)

The Brain of the Aggregator

Like I said above, we’re gonna talk about the Feed class and the FeedsManager class.  We could really call the FeedsManager the brain of the application.  It’s what handles saving, editing, and deleting feeds that the user wants/doesn’t want to view.

Here’s the code

and….

Here’s the video:

I also recommend that you check out this post about using the ConfigurationManager class.

The Feed class:

The purpose of the Feed class is to store all of the information about a feed and let us load it when we’re ready to display it.  I wrote it this way for two reasons.  One is that we don’t want to be sending unnecessary web requests by loading the feed immediately when it’s created.  Since the creation of the feed is handled in a separate tab page, we would never see any of the data at that point anyway.  Two, I got temporarily blocked on one of the NewEgg feeds because I was sending too many requests to their servers while I was testing things out… D’oh!

The Code:

This is it:

namespace Aggregator_App.Classes
{
    class Feed
    {
        public string Name { get; set; }
        public string URL { get; set; }
        public Channel Channel { get; set; }
        public List Items { get; set; }
        public string LastBuildDate { get; set; }
        private Parser RSSParser { get; set; }
        public Feed(string name, string url)
        {
            Name = name;
            URL = url;
        }
        public void LoadFeed()
        {
            try
            {
                RSSParser = new Parser(URL);
            }
            catch (Exception)
            {
                throw new InvalidFeedException("Feed URL was not valid");
            }
            MapToParser();
        }
        public override string ToString()
        {
            return Name;
        }
        public void UpdateFeed()
        {
            LoadFeed();
        }
        private void MapToParser()
        {
            Channel = RSSParser.RSSChannel;
            Items = RSSParser.Items;
            LastBuildDate = RSSParser.RSSChannel.BuildDate;
        }
    }
}

The Properties:

As you can see, we’ve got a few properties there.  Name and URL store the required information about the Feed when we create it and the other properties (excluding the RSSParser) just mimic the XMLParser class we created earlier in this series.

The Methods:

We’ve got a LoadFeed() function that creates the RSSParser object, an overridden ToString() method so that we can view each instance in a more meaningful way if we need to troubleshoot, an UpdateFeed() function, and a MapToParser() function which maps the RSSParser properties to the Feed properties when it is loaded.

Refresher:

If you don’t remember much about the XMLParser class, that’s ok.  Here’s the post about it, and I’ve included the code as well:

public class Parser
{
    public Channel RSSChannel { get; set; }
    public List Items { get; set; }
    private XDocument doc;
    public Parser(string RSSFeedURL)
    {
        ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls | SecurityProtocolType.Tls12 | SecurityProtocolType.Tls11;
        this.doc = XDocument.Load(RSSFeedURL);
        this.RSSChannel = this.CreateChannel();
        this.Items = this.CreateItems();
    }
    private Channel CreateChannel()
    {
        string title = (from c in doc.Root.Descendants("channel")
                        select c.Element("title").Value).FirstOrDefault();
        string link = (from c in doc.Root.Descendants("channel")
                       select c.Element("link").Value).FirstOrDefault();
        string description = (from c in doc.Root.Descendants("channel")
                              select c.Element("description").Value).FirstOrDefault();
        string buildDate;
        try
        {
            buildDate = (from c in doc.Root.Descendants("channel")
                         select c.Element("lastBuildDate").Value).FirstOrDefault();
        }
        catch (Exception ex)
        {
            buildDate = "Not Available";
        }
        return new Channel(title, link, description, buildDate);
    }
    private List CreateItems()
    {
        List itemTags = doc.Root.Element("channel").Elements("item").ToList();
        List items = new List();
        foreach (XElement item in itemTags)
        {
            string title = item.Element("title").Value;
            string link = item.Element("link").Value;
            string description = item.Element("description").Value;
            string modifiedDescription = description.Replace("src=\"//", "src=\"http://");
            string pubDate = item.Element("pubDate").Value;
            items.Add(new Item(title, link, modifiedDescription, pubDate));
        }
        return items;
    }
    public override string ToString()
    {
        string output = "";
        output += this.RSSChannel.ToString();
        foreach(Item item in this.Items)
        {
            output += item.ToString();
        }
        return output;
    }
}

The FeedsManager class:

Next, we have the FeedsManager class.  This class specifically handles only the configuration of the feeds.  Ya know, adding a feed, modifying a feed link, and deleting them.  It accomplishes this by using what’s called the ConfigurationManager.  I don’t want to spend too much time on this here because there’s a lot of information to cover and I’ve already got a post about it here.  Long story short, the ConfigurationManager is part of the .NET framework that you can use to create an XML based configuration file that your app can interact with.  Again, go check out this post to see how it all works.  You can find all of the code for that particular project here as well.

I do need to point out a slight change though.  When I created the FeedsManager class, I used a Feeds property and initiated that property in the constructor of the class.  This is unnecessary so I changed it.  Anytime we need to get the Feeds from the FeedsManager, we can just call the GetFeedsFromConfig() method.  Yea, I screwed up, sorry guys.

What about the rest?

Great question.  The rest of the video shows how we use the classes we just talked about. All we really have to do is hook up the button events to call the corresponding methods of the FeedsManager class.  There is some logic there too, but nothing super complicated.  Watch the video, dig into the code, and if you’ve got questions, just ask them somewhere (here, or on the YouTube video page) and I’ll be glad to clear them up!

Wait…I feel bad.  Here’s one for free:

private void btn_delete_selected_feed_Click(object sender, EventArgs e)
{
    string prompt = "Are you sure you want to delete the selected feed(s)?";
    if (ltb_feeds.SelectedItems.Count > 0 &&
        MetroMessageBox.Show(this, prompt, "Delete Feed", MessageBoxButtons.YesNoCancel, MessageBoxIcon.Question) == DialogResult.Yes)
    {
        for (int i = ltb_feeds.SelectedItems.Count - 1; i >= 0; i--)
        {
            try
            {
                feedsManager.RemoveFeedFromConfig(((Feed)ltb_feeds.SelectedItems[i]).Name);
                RemoveItemFromListBox(ltb_feeds, (Feed)ltb_feeds.SelectedItems[i]);
            }
            catch (ConfigurationErrorsException ex)
            {
                Alert(ex.Message);
                continue;
            }
        }
    }
}

This is the function we’ll call when the delete button is pressed.  So first we’re going to make sure that there were some items selected in our ListBox.  Since we’re using the MultiExtended option for the SelectionMode property, we can have multiple items selected too.  Then we’re going to show a MetroMessageBox to the user and only perform the delete if they hit the Yes button.  Then we’re going to…

  1. Loop through all of the selected items, in a descending fashion since we’re removing items.  In case you didn’t know, when you’re removing items from something, that changes the count.  Therefore, if you’re using that count to perform the loop, it will get out of whack and you won’t actually remove all the items.  Don’t believe me?  Test it out and see.
  2. Try to remove each feed from the feedsManager.  We’ll have to do some casting to get that ltb_feeds.SelectedItems[i] to give us the Name property of the feed.
  3. In the same breath, we’ll remove the item from the ltb_feeds ListBox as well.
  4. If we failed, we want to tell the user why we failed, and continue on through the loop

We’re done with this part now

Alright Guys and Girls, thanks for reading.  Stay tuned for the next one!

Author: Tyler Sells

Github

Share this:

  • Click to share on X (Opens in new window) X
  • Click to share on Facebook (Opens in new window) Facebook
  • More
  • Click to share on LinkedIn (Opens in new window) LinkedIn
  • Click to email a link to a friend (Opens in new window) Email
  • Click to share on Reddit (Opens in new window) Reddit

Like this:

Like Loading...

Leave a Reply Cancel reply

Your email address will not be published. Required fields are marked *

Follow me on Twitter

My Tweets

Github Repos

vtsells (Tyler Sells)

Tyler Sells

vtsells
http://www.nubgrammer.com
Joined on Jun 21, 2017
9 Public Repositories
100DaysOfCode
embers
MultiSelect
MVC-Project-Start
nubgrammer.com
PermIT
Spray
vtsells.github.io
Wizard
0 Public Gists

Categories

  • #100DaysOfCode (4)
  • ASP.NET (7)
  • ASP.NET Core (1)
  • ASP.NET MVC (3)
  • CSS (4)
  • General (13)
  • JS (3)
  • LESS (2)
  • Snippets (4)
  • Tools (4)
  • Tutorials (9)

Recent Posts

  • Creating a Knockout.js project on Codepen
  • 100DaysOfCode Day 3 – A State of Mind
  • 100DaysOfCode Day 2 – The Building Blocks
  • 100DaysOfCode Day 1 (Sort of cheated already)
  • Committing to #100DaysOfCode
© 2025 Nubgrammer | Powered by Superbs Personal Blog theme
We use cookies on our website to give you the most relevant experience by remembering your preferences and repeat visits. By clicking “Accept”, you consent to the use of ALL the cookies.
Do not sell my personal information.
Cookie SettingsAccept
Manage consent

Privacy Overview

This website uses cookies to improve your experience while you navigate through the website. Out of these, the cookies that are categorized as necessary are stored on your browser as they are essential for the working of basic functionalities of the website. We also use third-party cookies that help us analyze and understand how you use this website. These cookies will be stored in your browser only with your consent. You also have the option to opt-out of these cookies. But opting out of some of these cookies may affect your browsing experience.
Necessary
Always Enabled
Necessary cookies are absolutely essential for the website to function properly. These cookies ensure basic functionalities and security features of the website, anonymously.
CookieDurationDescription
cookielawinfo-checkbox-analytics11 monthsThis cookie is set by GDPR Cookie Consent plugin. The cookie is used to store the user consent for the cookies in the category "Analytics".
cookielawinfo-checkbox-functional11 monthsThe cookie is set by GDPR cookie consent to record the user consent for the cookies in the category "Functional".
cookielawinfo-checkbox-necessary11 monthsThis cookie is set by GDPR Cookie Consent plugin. The cookies is used to store the user consent for the cookies in the category "Necessary".
cookielawinfo-checkbox-others11 monthsThis cookie is set by GDPR Cookie Consent plugin. The cookie is used to store the user consent for the cookies in the category "Other.
cookielawinfo-checkbox-performance11 monthsThis cookie is set by GDPR Cookie Consent plugin. The cookie is used to store the user consent for the cookies in the category "Performance".
viewed_cookie_policy11 monthsThe cookie is set by the GDPR Cookie Consent plugin and is used to store whether or not user has consented to the use of cookies. It does not store any personal data.
Functional
Functional cookies help to perform certain functionalities like sharing the content of the website on social media platforms, collect feedbacks, and other third-party features.
Performance
Performance cookies are used to understand and analyze the key performance indexes of the website which helps in delivering a better user experience for the visitors.
Analytics
Analytical cookies are used to understand how visitors interact with the website. These cookies help provide information on metrics the number of visitors, bounce rate, traffic source, etc.
Advertisement
Advertisement cookies are used to provide visitors with relevant ads and marketing campaigns. These cookies track visitors across websites and collect information to provide customized ads.
Others
Other uncategorized cookies are those that are being analyzed and have not been classified into a category as yet.
SAVE & ACCEPT
%d

    Privacy Policy