get list from based on another list - c#

I asked this question earlier, but I over simplified it, and I still don't know how to do it in a better way than I'm doing (for).
I got two lists.
One, a list of facebook friends, a simple object, consider facebook.id the only property.
Second, a list of users, it's a more complex object, because each user gets a lists of providers inside (facebook, twitter, etc), but the providers list can be null, and if not null, not necessarily the provider is a facebook one. So:
public class EFacebook
{
public long Id { get; set; }
}
public class EUser
{
public long Id { get; set; }
/// <summary>
/// Nullable
/// </summary>
public List<EProvider> EProviders { get; set; }
}
public class EProvider
{
public enum EnumProviderType
{
Facebook = 2,
Twitter = 3
}
public EnumProviderType ProviderType { get; set; }
public string Id { get; set; }
}
What I need is to filter the facebook list to get all the facebook friends that are users and get all the facebook friends that are not users.

Suppose that List<EFacebook> fbList is the first list and List<EUser> usersList is the second list.
You can do something like this:
fbList.Where(x=>usersList.Select(x=>x.Id).Contains(x.Id)) ==> this will return the list of facebook entities that are users.
The second list is the difference between this fbList and this one.
Let me know if I understood the question correctly!
Tamash

Assuming this:
List<EFacebook> listEFacebookFriends = new List<EFacebook>();
List<EUser> listEUsers = new List<EUser>();
Then you can get a list of all Facebook friends that are users here:
var listEUsersOnFacebook = from user in listEUsers
let fbProviders =
from provider in user.EProviders
where provider.ProviderType == EProvider.EnumProviderType.Facebook
select provider.Id
where fbProviders.Count() > 0
select user.Id;
// this next call will get facebook friends that are users
var friendsOnFacebook = listEFacebookFriends.Where(x =>
listEUsersOnFacebook.Contains(x.Id));
And here you can get your Facebook friends that are NOT users:
var listEUsersNotOnFacebook = from user in listEUsers
let fbProviders =
from provider in user.EProviders
where provider.ProviderType == EProvider.EnumProviderType.Facebook
select provider.Id
where fbProviders.Count() == 0
select user.Id;
// this call will get facebook friends that are not users
var friendsNotOnFacebook = listEFacebookFriends.Where(x =>
listEUsersNotOnFacebook.Contains(x.Id));

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.

Accessing a list of objects & how to claim a object for the fatest request

I'm developing an Asp.net mvc project:
There is a List store all the online Pupils
There are few methods:
- Login(Pupil p): if login ok, p will be added to Pupils list
- Logout(Pupil p): if logout ok, p will be removed out of Pupil list
-> 2 methods have a same potential issue is "cannot modify list while modifying" because there are a lot of Pupil login to system and logout at the same time. While adding a pupil the other are being removed from Pupils list -> exception throws
I tried to use lock to lock a list while modifying (insert/remove) but is it a good way to do? Do you have some better idea?
The last method is Claim(Book b)
Administrator put some books in the GUI and all the logged Pupils can see these books. They can claim any book they want. The fastest Pupil claimed is will own that book. So how can we know the fastest claimer? While updating a data row. At the same time there are many books are claimed by many pupils. But only one fastest pupil can own a particular book after claiming successfully
Do you have a solution for this? This solution just like you put a command to buy stocks. The fastest guy will own the stocks
Remember that there are many pupils will do the same thing at the same time. So we have to make sure that system works properly and exactly
Thanks you in advance,
Best regards
The issue I see here is that your design assumes that the application is always on, and that the lists are the absolute truth about who has what book. What happens when the server is reset? What if your library gets big enough to need a second server to the application?
You need to preserve your lists in a database or some other sort of persistent medium. Holding the lists in memory will give you a read/write buffer, but the lists must be populated from the persistence layer of your library database.
using System;
using System.Collections.Concurrent;
namespace TestArea
{
public class Pupil
{
public Guid Id { get; set; }
public string Name { get; set; }
public string UserName { get; set; }
}
public class Book
{
//Supports having more than one ISBN in the library... We may have more than one To Kill a Mockingbird in our school library
public Guid Id { get; set; }
public string ISBN { get; set; }
}
public class SchoolLibrary
{
private ConcurrentDictionary<Guid, Pupil> Pupils { get; set; }
private ConcurrentDictionary<Guid, Book> Books{ get; set; }
private ConcurrentDictionary<Guid, Guid> CheckOuts { get; set; }
public Pupil Login(string userName, string password)
{
//Call repository to authenticate pupil into library system
//Mocked return assuming password check success
var id = Guid.NewGuid();
return Pupils.GetOrAdd(id, (i) =>
{
//Replace with function to get student info
return new Pupil
{
Id = i,
Name = "Bac Clunky",
UserName = userName
};
});
}
public bool CheckOut(Guid pupilId, Guid bookId)
{
//If book exists
if (Books.ContainsKey(bookId))
{
Guid currentOwner;
//...is not currently checked out by anyone
if (CheckOuts.TryAdd(bookId, pupilId))
{
return true; //book is now checked out
}
if (CheckOuts.TryGetValue(bookId, out currentOwner))
{
return currentOwner == pupilId; //returns true if pupil already has the book, false if another student has it
}
}
return false; //all other cases fail to check out book
}
}
}

Getting IdConvention in RavenDB

Lets say we have a User class
public class User
{
public User() {
Created = DateTime.Now;
Tags = new List<string>();
}
public string FirstName { get; set; }
public string LastName { get; set; }
public DateTime Created {get;set;}
public IEnumerable<string> Tags {get; private set;}
}
And one might want a user to have an id like [FirstName]/[LastName] so we register an IdConvention like this:
_documentStore
.Conventions
.RegisterIdConvention<User>(
(dbname, commands, user) => user.FirstName +"/" + user.LastName ));
Now lets say you created a new user with a new set of tags attached. You want to store it in RavenDB if the User does not exist. However, if the User does exist you don't want to overwrite the existing Object as you want to keep the initial Created date. Therefore you only update the Tags enumeration with the values of the newly created User.
You might do something like this:
public void AddOrUpdateUser(User newUser) {
using (var session = _documentStore.OpenSession())
{
var existingUser = session.Load<User>("myFirstname/myLastname")
if(user != null) {
existingUser.Tags = user.Tags;
}
else {
session.Store(newUser);
}
session.SaveChanges();
}
}
However, if for some reason I changed my IdConvention, I have had to update the code above as well. Is there a way to reference the IdConvention registered in order to calculate the id for the newUser Object. With this id value you could check wether an item exists or not rather than creating the Id by yourself.
After registering an id convention, the GenerateDocumentKey method will use that convention instead of the default hilo generation scheme.
It needs some parameters, which are easiest to get at if you first cast the IDocumentSession to a real DocumentSession.
var s = ((DocumentSession) session);
var key = s.Conventions.GenerateDocumentKey(s.DatabaseName,
s.DatabaseCommands,
yourEntity);

Raven DB: How to create "UniqueVisitorCount by date" index

I have an application to track the page visits for a website.
Here's my model:
public class VisitSession {
public string SessionId { get; set; }
public DateTime StartTime { get; set; }
public string UniqueVisitorId { get; set; }
public IList<PageVisit> PageVisits { get; set; }
}
When a visitor go to the website, a visit session starts. One visit session has many page visits. The tracker will write a UniqueVisitorId (GUID) cookie when the first time a visitor go to the website. So we are able to know if a visitor is returning visitor.
Now, I want to know how many unique visitors visited the website in a date range. That is, I want to display a table in our webpage like this;
Date | Unique Visitors Count
------------+-----------------------
2012-05-01 | 100
2012-05-02 | 1000
2012-05-03 | 120
I want to create an index to do this in RavenDB. But I don't know how to write the Map/Reduce query. I though it can be like this:
public class UniqueVisitor_ByDate : AbstractIndexCreationTask<VisitSession, UniqueVisitorByDate>
{
public UniqueVisitor_ByDate()
{
Map = sessions => from s in sessions
select new
{
s.StartTime.Date,
s.UniqueVisitorId
};
Reduce = results => from result in results
group result by result.Date into g
select new
{
Date = g.Key,
UniqueVisitorCount = g.Distinct()
};
}
}
But it's not working. In Ayende's e-book, I know that the result of Map function should be same as the result of Reduce function. So how can I write the correct map/reduce functions?
This index should do what you want:
public class UniqueVisitor_ByDate : AbstractIndexCreationTask<VisitSession, UniqueVisitorByDate>
{
public UniqueVisitor_ByDate()
{
Map = sessions =>
from s in sessions
select new {
s.StartTime.Date,
s.UniqueVisitorId,
Count = 1,
};
Reduce = results =>
from result in results
group result by result.Date
into g
select new UniqueVisitorByDate {
Date = g.Key,
Count = g.Select(x => x.UniqueVisitorId).Distinct().Count(),
UniqueVisitorId = g.FirstOrDefault().UniqueVisitorId,
};
}
}
Note that it requires the extra 'UniqueVisitorId' property in the 'reduce' and the 'count' property in the map, but you can just ignore those.

Session State MVC3

I am having a problem I can not find a tutorial or some code where I am able to do some session state. For example I would like to create a application where when a user logs in, he or she can only view there information for example a student viewing his grades an no other student can see that information. I have achieved this in VB 2008 last year but need help in MVC3 as it is not the same as the language as i am using C#. In vb 2008 i achieved this connecting a table from the ASP.net database (users) and joined to my employee table by a foreign key. And added the following code:
Session("ID") = objUser
Dim db As New DataClassesDataContext
Dim info = From a In db.tblCourses _
Where a.CourseTitle = ddlCourseName.SelectedItem.Value _
Select a.CourseId Order By CourseId Descending
crseID = info.FirstOrDefault()
Session("Course") = crseID
sdsAddStudent.Insert()
FName.Text = ""
LName.Text = ""
Address.Text = ""
ddlCourseName.SelectedIndex = 0
Session("UserID2") = objUser
Session("RoleID2") = "f13b9bf3-593d-4170-bfaa-bc43655773e2"
sdsRoleStudent.Insert()
I know VB is different to C# MVC3 as this is 2008 and not MVC3 am just showing this code so people know that I am not after free code and have tried to make an effort which has not succeed.
Thank You For Your Kind Help People
I'm not clear what exactly are you trying to achieve.
If you want to limit users access exclusively to the data that belongs to them, you need to have this data related their IDs in Users table. There is very little that you have told about table structure, but I think it is safe to assume that you have Courses table and something like UsersInCourses that would map many users to a single Course. All you would have to do is to select courses have User's Id assigned to them. To get currently logged in user you just have refer to
User.Identity.Name
in your application's code.
So let's assume your class is cooking reciepe :
public class Recipe
{
public int Id { get; set; }
public string Name { get; set; }
public string Author { get; set; }
public string PreparationInstructions { get; set; }
public DateTime CreationDate { get; set; }
}
and you want to have an Action that returns recipes for currently logged in user
public ActionResult ShowMyRecipes()
{
var myRecipes = dbContext.Recipes.Where(recipe => recipe.Author.Equals(User.Identity.Name)).ToList();
return View(myRecipes);
}
public ActionResult CreateRecipe(Recipe recipe)
{
// set Author to curently logged in user's key
recipe.Author = Membership.GetUser().ProviderUserKey;
// save changes
dbContext.Recipes.Add(recipe);
}

Categories