Design considerations for an RSS client - c#

I need to develop a RSS client using c# and I wonder how any RSS client stores what the user read or not.
The simple answer is to store all the feeds of each url and mark whether the user read it or not.
So I need to know how other RSS clients manage the feeds state from being read or not from the user. do they store all the feeds from all the urls or not
Also, I need to know if there are any .net Library for client using pubsubhubbub protocol
For example,
If I subscribe for CNN feeds , the application will load the current CNN feeds then I make it read. After while , I open the client , I should find all the feeds that I read is marked as read.
So this means , that the client will store - for example in its database - all the links of the CNN feeds and save for each link its status whether it is read or not
my question is , is there another way to track the feeds is read or not instead of saving all the feeds of all the sites on DB which will lead to huge database

Welcome to StackOverflow :D
Representing RSS feeds
You could use the following types to represent feeds and articles :
using System;
using System.Collections.Generic;
using System.Linq;
public abstract class RssItem
{
public virtual bool IsRead { get; set; }
public string Name { get; set; }
public string Url { get; set; }
}
public class RssFeed : RssItem
{
public List<RssFeedArticle> Articles { get; set; }
public override bool IsRead
{
get { return Articles.All(s => s.IsRead); }
set { Articles.ForEach(s => s.IsRead = true); }
}
}
public class RssFeedArticle : RssItem
{
public string Content { get; set; }
}
This is really a simple representation, feel free to enhance it.
Basically when you set feed.IsRead = true; all articles will be marked as read, if you query the value it will return true only if all the articles have been read.
Example :
var article1 = new RssFeedArticle {Name = "article1", Content = "content1"};
var article2 = new RssFeedArticle {Name = "article2", Content = "content2"};
var feed = new RssFeed
{
Name = "cool feed",
Articles =
new List<RssFeedArticle>(new[]
{
article1,
article2
})
};
article1.IsRead = true;
feed.IsRead = true;
Storing your data
A common approach is to store your application data is in ApplicationData folder or in My Documents.
The advantage of using My Documents is that the user will generally backup this folder, not necessarily the case of ApplicationData for which novice users probably don't even know its existence.
Example for retrieving your application folder :
using System.IO;
private void Test()
{
string applicationFolder = GetApplicationFolder();
}
private static string GetApplicationFolder()
{
var applicationName = "MyCoolRssReader";
string folderPath = Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData);
string applicationFolder = Path.Combine(folderPath, applicationName);
bool exists = Directory.Exists(applicationFolder);
if (!exists)
{
Directory.CreateDirectory(applicationFolder);
}
return applicationFolder;
}
If you prefer My Documents instead :
string folderPath = Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments);
Here's some explanation/advice from a Microsoft developer :
http://blogs.msdn.com/b/patricka/archive/2010/03/18/where-should-i-store-my-data-and-configuration-files-if-i-target-multiple-os-versions.aspx
pubsubhubbub
There's a library for C# : https://code.google.com/p/pubsubhubbub-publisherclient-csharp/
(from https://code.google.com/p/pubsubhubbub/wiki/PublisherClients)
If you are satisfied with my answer then do not forget to accept it; if you still have some interrogations update your question and I'll try to address them.

Related

Best way to send multiple email types in ASP.NET MVC

Hi there to the good friends of SO!
This is more of a design question so I'll get into a detailed example.
Let me explain the way we're sending emails.
In various parts of the application, we create entries in our Notification table for different kinds of email we might have to send.
For eg: The NotificationQueue table looks like this:
NotificationQueueID OrderID EmailType Notes SentDatetime
1 461196 OrderUpdate SomeNote1 2020-09-01 14:45:13.153
2 461194 OrderCancellation SomeNote2 2020-09-01 14:45:13.153
It's accessed using the property in the DbContext as:
public DbSet<NotificationQueue> NotificationQueues { get; set; }
The different types of email is modeled in an enum:
public enum TypeOfEmail
{
OrderCancellation,
OrderUpdate
}
We have a EmailModel class that has a TicketsInNotificationQueue property that has a list of any of the email types we have. For eg: At any given time, it can have list of either UpdatedTickets or CancelledTickets. The email type says what type of tickets are in the TicketsInNotificationQueue property.
public class EmailModel
{
public EmailModel(TypeOfEmail emailType, TicketsInNotificationQueue ticketsInNotificationQueue)
{
EmailType = emailType;
TicketsInNotificationQueue = ticketsInNotificationQueue;
}
public TypeOfEmail EmailType { get; set; }
public TicketsInNotificationQueue TicketsInNotificationQueue { get; set; }
}
public class TicketsInNotificationQueue
{
public List<OrderCancellation> CancelledTickets { get; set; }
public List<OrderUpdate> UpdatedTickets { get; set; }
}
public class OrderCancellation : CommonOrderInformation
{
public string SomeOrderId { get; set; }
}
public class OrderUpdate: CommonOrderInformation
{
public string SomeUpdateRelatedProperty { get; set; }
}
public class CommonOrderInformation
{
public int NotificationQueueId { get; set; }
public string ReferenceNumber { get; set; }
}
There's a method that retrieves tickets from Notification table:
public async Task<TicketsInNotificationQueue> GetTicketsfromNotificationQueueAsync(TypeOfEmail emailType)
{
var ticketsInNotificationQueue = new TicketsInNotificationQueue();
using (var dbCon = GetSomeDbContext())
{
var notifications = dbCon.NotificationQueues.Where(x => x.EmailType == emailType.ToString()).ToList();
foreach (var ntf in notifications)
{
if (ntf.EmailType == TypeOfEmail.OrderCancellation.ToString())
{
if (ticketsInNotificationQueue.CancelledTickets == null)
{
ticketsInNotificationQueue.CancelledTickets = new List<OrderCancellation>();
}
ticketsInNotificationQueue.CancelledTickets.Add(new OrderCancellation()
{
NotificationQueueId = ntf.NotificationQueueID,
ReferenceNumber = ntf.OrderID,
SomeOrderId = "Something from a table."
});
}
else if (ntf.EmailType == TypeOfEmail.OrderUpdate.ToString())
{
if (ticketsInNotificationQueue.UpdatedTickets == null)
{
ticketsInNotificationQueue.UpdatedTickets = new List<OrderUpdate>();
}
var notes = dbCon.NotificationQueues.FirstOrDefault(x => x.NotificationQueueID == ntf.NotificationQueueID)?.Notes;
ticketsInNotificationQueue.UpdatedTickets.Add(new OrderUpdate()
{
NotificationQueueId = ntf.NotificationQueueID,
ReferenceNumber = ntf.OrderID,
SomeUpdateRelatedProperty = "Something from a table."
});
}
}
}
return ticketsInNotificationQueue;
}
Now I just take this list, and filter out the notificationIds for the type of tickets that I just received, and work on them down the line. (I need those notificationIds to set the SentDatetime after the notification has been sent).
var ticketsReceived = false;
notificationIds = new List<int>();
if (ticketsInNotificationQueue.CancelledTickets != null && ticketsInNotificationQueue.CancelledTickets.Any())
{
ticketsReceived = true;
notificationIds = ticketsInNotificationQueue.CancelledTickets.Select(x => x.NotificationQueueId).ToList();
}
else if (ticketsInNotificationQueue.UpdatedTickets != null && ticketsInNotificationQueue.UpdatedTickets.Any())
{
ticketsReceived = true;
notificationIds = ticketsInNotificationQueue.UpdatedTickets.Select(x => x.NotificationQueueId).ToList();
}
if (ticketsReceived)
{
// Proceed with the process of sending the email, and setting the `SentDateTime`
}
The problem I see here is that as the type of emails grows bigger, let's say 10-20, the method to retrieve tickets and filter them out later needs to grow so big that it's going to spin out of control in terms of readability and code manageability which I'm not liking at all. The part where I need to check what emailType is requested in the fetch and what emailType has been received(to get the corresponding notificationIds for SentDateTime update).
So is there some other way to design this workflow (I'm even open to using reflection and such) to make it more manageable and concise?
Any help would be greatly appreciated!
There is significant improvements that you can make to the existing system and the existing code. In the interest of having a more complete answer I'm going to recommend a not-too-expensive system overhaul and then proceed to your exact answer.
A different and industry standard approach
You already have the data structure correct, this is a perfect job for distributed persistent queues, where you don't need to worry about querying the database as much; instead you just enqueue the messages and have a processor that deals with them. Since you're using C# and .net, I strongly encourage you to check out Azure Service Bus. This is effectively a large queue where you can send messages (in your case send email requests) and you can enqueue your messages to different channels in the service bus depending on their type.
You could also look into creating a queue processor / which Azure Functions have a trigger out of the box. Once your email is sent, then you can write to your DB, we've sent this email.
So, the good design looks like
Have distributed persistent queues, channels / enqueue the email requests to them directly.
If you want to process them at a cadence, run your processor using cron - which most industry solutions support.
If you want to process them as they are ending up in the queue, use a trigger.
You can enrich your processor based on your scenario, it looks like it has something to do with orders, so you may need to handle cases like not sending an already queued email after an order in cancelled, etc..
Improving what you have
Due to some circumstances, the solution above might not be available to you - so let's get to it.
See how to refactor switch statements (since you have one with if / else ifs)
https://sourcemaking.com/refactoring/smells/switch-statements
Ways to eliminate switch in code
You could get this through polymorphism, just create a base mail type and override the behaviors in subclasses. This way you can associate the correct queue with the correct email type.
Example:
var results = await getSomeEmails(OrderMail);
// returns a separate processor inherited from the base one, implemented in different ways.
var processor = ProcessorFactory.Create(OrderMail);
await processor.Send(results);
Some more improvements
foreach (var ntf in notifications)
{
if (ntf.EmailType == TypeOfEmail.OrderCancellation.ToString())
You are checking the email type over and over again unnecessarily in this loop, you should look into moving those statements above the for and check through the passed-in parameter, since you already know the type you're querying for.
Thank you for the answer #Mavi Domates.
But this is what I ended up doing:
I modified the EmailModel's TicketsInNotificationQueue property so that instead of having different types of classes for different types of email, we just have one type of common class. This will avoid having us to put those checks for checking what kind of email was requested in the fetch logic and also to retrieve notification Ids down the line (to update SentDateTime after email is sent) as indicated in the original question.
public class EmailModel
{
public EmailModel(TypeOfEmail emailType, IEnumerable<CommonEmailModel> ticketsInNotificationQueue)
{
EmailType = emailType;
TicketsInNotificationQueue = ticketsInNotificationQueue;
}
public TypeOfEmail EmailType { get; set; }
public IEnumerable<CommonEmailModel> TicketsInNotificationQueue { get; set; }
}
public enum TypeOfEmail
{
OrderCancellation,
OrderUpdate
}
I added a new class called: CommonEmailModel and removed all those different email type classes (classes for OrderCancellation, OrderUpdate etc.).
public class CommonEmailModel
{
// Common to all email types. A lot of email types only need these first 4 properties
public string EmailType { get; set; }
public int NotificationQueueId { get; set; }
public string OrderId { get; set; }
public string Notes { get; set; }
// Cancellation related
public string SomeOrderId { get; set; }
// Update related
public string SomeUpdateRelatedProperty { get; set; }
public static async Task<IEnumerable<CommonEmailModel>> GetEmailBodyRecordsAsync(TypeOfEmail emailType)
{
var emailModels = new List<CommonEmailModel>();
var emailEntries = await EmailNotificationQueue.GetEmailEntriesAsync(emailType);
var relevantOrdIds = emailEntries.Select(x => x.OrderID).Distinct().ToList();
using (var dbCon = GetSomeDbContext())
{
orders = dbCon.Orders.Where(x => relevantOrdIds.Contains(x.OrdNumber)).ToList();
}
foreach (var record in emailEntries)
{
var emailModel = new CommonEmailModel
{
EmailType = emailType,
NotificationQueueId = record.NotificationQueueID,
OrderId = record.OrderID,
Notes = record.Notes,
SomeOrderId = orders?.FirstOrDefault(o => o.OrdNumber == record.OrderID)?.SomeOrderIdINeed,
SomeUpdateRelatedProperty = orders?.FirstOrDefault(o => o.OrdNumber == record.OrderID)?.UpdateRelatedPropertyINeed
};
emailModels.Add(emailModel);
}
return emailModels;
}
}
I just get the records the following way:
var emailRecords = await CommonEmailModel.GetEmailBodyRecordsAsync(emailType);
And simply pass this to EmailModel constructor as the ticketsInNotificationQueue parameter. No need to do all that extra check of figuring out if records of certain emailType was requested. The views for OrderCancellation and OrderUpdate will use the common properties and their respective relevant properties that are present in the CommonEmailModel class.
if (emailRecords.Any())
{
var emailModel = new EmailModel(emailType, emailRecords);
}
Now all I have to do is pass the notification Ids to a method that marks the SentDateTime column with the current timestamp by simply calling:
if (emailWasSent)
{
await UpdateNotificationSentTimeAsync(emailRecords.Select(t => t.NotificationQueueId));
}
In the future if we keep on adding new emailType (most probably they'll carry the information in those 4 first common properties in CommonEmailModel), we can simply add new properties to the CommonEmailModel to accommodate that and just create a new view. This way I can avoid code repetition and complexity in the fetch and also at the end while updating the SentDateTime.

how to Serialize .Net Class to Avro.Generic.GenericRecord for publishing into kafka topic?

I am trying to find a way/helper to convert.Net Class to Avro.Generic.GenericRecord . Currently, I am manually adding field-name and field-value to Generic record. Is there a serializer/converter which I can use to convert the object to generic record and publish on to a kafka topic.
class Plant
{
public long Id { get; set; }
public string Name { get; set; }
public List<PlantProperties> PlantProperties{ get; set; }
}
class PlantProperties
{
public long Leaves{ get; set; }
public string Color{ get; set; }
}
Please suggest.
Assuming you are using the Confluent Schema Regsitry, you can use their .NET client1
https://github.com/confluentinc/confluent-kafka-dotnet
Copied from the examples folder
using (var serdeProvider = new AvroSerdeProvider(avroConfig))
using (var producer = new Producer<string, GenericRecord>(producerConfig, serdeProvider.GetSerializerGenerator<string>(), serdeProvider.GetSerializerGenerator<GenericRecord>()))
{
Console.WriteLine($"{producer.Name} producing on {topicName}. Enter user names, q to exit.");
int i = 0;
string text;
while ((text = Console.ReadLine()) != "q")
{
var record = new GenericRecord(s);
record.Add("name", text);
record.Add("favorite_number", i++);
record.Add("favorite_color", "blue");
producer
.ProduceAsync(topicName, new Message<string, GenericRecord> { Key = text, Value = record })
.ContinueWith(task => task.IsFaulted
? $"error producing message: {task.Exception.Message}"
: $"produced to: {task.Result.TopicPartitionOffset}");
}
}
cts.Cancel();
}
Where, in your case, update the record.Add uses accordingly
However, since you have a class, therefore, you should try to use SpecificRecord, rather than serializing back and forth between Avro and a .NET class via a GenericRecord. See the README section on the AvroGen tool for examples of this
1. I'm not aware of an alternative .NET library
Below are the steps I did to solve the problem using the suggestion from #cricket_007.
To avoid the complexity of writing the avro schema, create the c# classes first then use AvroSerializer to generate schema.
AvroSerializer.Create().WriterSchema.ToString()
This will generate the schema json for the class.
Move it to a schema file and
Make all the types to have nulls as Required
Then used avro_gen.exe tool to regenerate class files which implements ISpecific Record.
Add used the below code to publish to queue
using (var serdeProvider = new AvroSerdeProvider(avroConfig))
using (var producer = new Producer<string, MYClass>(producerConfig,
serdeProvider.GetSerializerGenerator<string>(),
serdeProvider.GetSerializerGenerator<MYClass>()))
{
Console.WriteLine($"{producer.Name} producing on
{_appSettings.PullListKafka.Topic}.");
producer.ProduceAsync(_appSettings.PullListKafka.Topic, new
Message<string, MYClass> { Key = Guid.NewGuid().ToString(), Value = MYClassObject})
.ContinueWith(task => task.IsFaulted
? $"error producing message: {task.Exception.Message}"
: $"produced to: {task.Result.TopicPartitionOffset}");
}
some links to help do this.
https://shanidgafur.github.io/blog/apache-avro-on-dotnet
https://github.com/SidShetye/HelloAvro/tree/master/Avro

DynamoDB C# expression to query/scan

I have a generic repository structure in place using interfaces and I am trying to develop a DynamoDB implementation for this. This is my first experience with DynamoDB and NoSQL (previously all T-SQL).
The problem I am having is that I am unable to find any way of converting a lambda expression in C# to a format that I can use to query/scan DynamoDB.
My Get method looks like this:
public Task<TEntity> GetAsync(Expression<Func<TEntity, bool>> where)
{
return await this.DataContext.ScanAsync(...);
}
Is there an existing way to do this? There doesn't seem to be anything in the documentation that addresses this and I am struggling to find an example of where someone else has had a similar problem.
Maybe my lack of experience with NoSQL and/or DynamoDB is just the problem here. If so, please do point out a better approach if necessary though I will note that as previously mentioned, I am implementing an interface which is already defined and changing this isn't really an option.
As far as I know, you can use the ServiceStack.Aws, which is similar to LINQ.
For example:
using System;
using Amazon;
using Amazon.DynamoDBv2;
using ServiceStack;
using ServiceStack.Text;
using ServiceStack.Aws.DynamoDb;
using ServiceStack.DataAnnotations;
var awsDb = new AmazonDynamoDBClient("keyId","key",
new AmazonDynamoDBConfig { ServiceURL="http://localhost:8000"});
var db = new PocoDynamo(awsDb);
public class Todo
{
[AutoIncrement]
public long Id { get; set; }
public string Content { get; set; }
public int Order { get; set; }
public bool Done { get; set; }
}
db.RegisterTable<Todo>();
db.DeleteTable<Todo>(); // Delete existing Todo Table (if any)
db.InitSchema(); // Creates Todo DynamoDB Table
var newTodo = new Todo {
Content = "Learn PocoDynamo",
Order = 1
};
db.PutItem(newTodo);
var savedTodo = db.GetItem<Todo>(newTodo.Id);
"Saved Todo: {0}".Print(savedTodo.Dump());
savedTodo.Done = true;
db.PutItem(savedTodo);
var updatedTodo = db.GetItem<Todo>(newTodo.Id);
"Updated Todo: {0}".Print(updatedTodo.Dump());
db.DeleteItem<Todo>(newTodo.Id);
var remainingTodos = db.GetAll<Todo>();
"No more Todos: {0}".Print(remainingTodos.Dump());

WPF dynamic layout using user config file

I have an application that receives IoT data. I would like to change the layout (that displays the data) depending on the configuration set by the user.
Eg: The user decides that 3 bytes will be device_id, 4th byte when multiplied by a value gives temperature value,etc. How can I create such a user config file and save it for later use ?
After saving the data, how can I display the data based on these config files? I am thinking of using labels to just match the data. Is there a better way to do this ?
So I have done as #Nobody suggested.
I have created a class with details like number of bytes, device id, etc and then took the data from user input via a form. Later used Basic Serialization to save the data and deserialization to read it back the next time I open the Application as per this link.
Code :
[Serializable()]
public class Config
{
public string DeviceId { get; set; }
public string Name { get; set; }
public int Length { get; set; }
}
using (Stream testFileStream = File.Create(pathString)) // Serialization code
{
BinaryFormatter serializer = new BinaryFormatter();
serializer.Serialize(testFileStream, config);
testFileStream.Close();
}
using (Stream testFileStream = File.OpenRead(pathString))
{
BinaryFormatter deserializer = new BinaryFormatter();
config = (Config)deserializer.Deserialize(testFileStream);
testFileStream.Close();
}

Silverlight - read and write to XML-file with DataGrid

I'm trying to bind xml-data to a Silverlight data-grid. At the moment I have been "playing" with the DevExpress tools for Silverlight. I don't know how different it is using these tools compared to the standard way, since I'm fairly new to Silverlight.
On their website I've found the following example to retrieve data from one XML-file.
Now I want to be able to save modifications I made within the datagrid to this xml-file and also add and delete rows that will also affect the XML-file.
namespace XMLReadWrite {
public partial class MainPage : UserControl {
public MainPage() {
InitializeComponent();
grid.ItemsSource = GetData();
}
XDocument doc = XDocument.Load("Contacts.xml", LoadOptions.None);
ObservableCollection<Contact> GetData() {
var items = from item in doc.Descendants("Contacts")
select new Contact() {
FirstName = item.Element("FirstName").Value,
LastName = item.Element("LastName").Value,
Company = item.Element("Company").Value,
City = item.Element("City").Value
//ID = int.Parse(item.Element("ID").Value)
};
ObservableCollection<Contact> contacts = new ObservableCollection<Contact>();
foreach (Contact contact in items) {
contacts.Add(contact);
}
return contacts;
}
private void SaveToXML()
{ }
private void Save_Button_Click(object sender, RoutedEventArgs e)
{
SaveToXML();
}
}
public class Contact {
public int ID {
get;
set;
}
public string FirstName {
get;
set;
}
public string LastName {
get;
set;
}
public string Company {
get;
set;
}
public string City
{
get;
set;
}
}
}
This is probably an easy task but I'm stuck since I don't have a clue what needs to be done here.
Hopefully someone can help me out.
Thank you in advance!
You dont mention how you are getting your xml file, so there are few options, one save it to local storage and load it from local storage, heres a article on the subject, two retrieve it from the server and save it to the server, heres an article on the subject or three some combo of either. As far as working with XML, it looks like you are using linq to xml to read the data into a collection of Contacts, I recommend reading and going through these links
Linq to Xml Samples
Using LINQ to XML to Add Data to XML File in C#
Creating and Saving XML tree using LINQ to XML
The thing about Linq to Xml is that its not specific to silverlight, these skills also transfer well to server programming.
Now the task of adding and removing items from the datagrid, well depending on how you deside to architect your solution, theres MVVM or straightforward code-behind, id recommend spending the time to get to know MVVM, its a design pattern that fits very well in the world of silverlight, it will save you hair in the long run. Heres a article on this problem domain using MVVM.
EDIT
grid.ItemsSource as ObservableCollection<Contact>();
will give you back the collection.

Categories