I have mission system in MVC. and I give a same mission or diffrent mission for a lot of users. and I show title, description, start date , finish date and who are/is in mission. These are showing view page in grid.mvc. but when I login I can see every mission. I dont want to every mission I just want to see only my mission. of course other users see their missions.
in my controller, I split names.
this is my Codes,
Controller:
TicketDbContext db = new TicketDbContext();
public ActionResult Index()
{
var result = db.Missions.OrderByDescending(x=>x.GivenDate).ToList();
string ad = string.Empty;
foreach (var item in result)
{
if (!string.IsNullOrEmpty(item.GivenUsers))
{
string[] Ids = item.GivenUsers.Split(',');
for (int i = 0; i < Ids.Length; i++)
{
int id = Convert.ToInt32(Ids[i]);
item.GivenUsers += db.Users.FirstOrDefault(x => x.Id == id).Name+ " " + db.Users.FirstOrDefault(x => x.Id == id).Surname+ ",";
}
}
}
return View(result);
}
ScreenShot of my Grid
firstly, I recommend that you keep the assigned users ids in separated table
This is can handle your case..
var currentUserId = "";// set current userId from Where are you holding (session,identity etc..)
var result = db.Missions.OrderByDescending(x=>x.GivenDate).ToList();
result = result.Where(x=> x.GivenUsers.Split(',').Contains(currentUserId));
foreach (var item in result)
{
if (!string.IsNullOrEmpty(item.GivenUsers))
{
int[] Ids = Array.ConvertAll(item.GivenUsers.Split(','),
delegate(string s) { return int.Parse(s); }) ;
string[] names = db.Users.Where(u => Ids.Contains(u.Id)).Select(u => u.Name+ " " + u.Surname).ToArray();
item.GivenUsers = string.Join(",",names);
}
}
Related
I am working on method that will send an email to each user. I have several lists where I am collecting information about items that should be fixed. After that I am Grouping by user and would like to send an email to each of them to take some actions.
Here is my current code:
public static void SendEmail()
{
string HTMLTableInString = "";
string PM = "";
var combined = List1
.Concat(List2)
.Concat(List3)
.Concat(List4)
.Concat(List5)
.Concat(List6)
.Concat(List7)
.Concat(List8)
.ToList();
if (combined.Any())
{
var GroupedList = combined.GroupBy(x => x.User)
.Select(grp => grp.ToList())
.ToList();
foreach (var item in GroupedList)
{
foreach (var pos in item)
{
HTMLTableInString = Other.CreateHTMLTable(GroupedList,
x => (pos.ProjectNumber, "Project number"),
x => (pos.SubProjectNumber, "Sub-project number"),
x => (pos.SubProjName, "Sub-project name"),
x => (pos.User, "User")
);
Console.WriteLine(pos.ProjectNumber + " " + pos.SubProjName + " " + pos.User);
PM = pos.User;
}
if (!string.IsNullOrWhiteSpace(PM))
{
Console.WriteLine("-------------------------------------------------------------");
Console.WriteLine(HTMLTableInString);
Console.WriteLine("-------------------------------------------------------------");
// Send an email
}
}
}
}
Currently this line produces what is expected
Console.WriteLine(pos.ProjectNumber + " " + pos.SubProjName + " " + pos.User);
However my HTML table does not look right, amount of rows is correct but they all have same values. This is because HTMLTableInString is overwritten numerous times (depends on User records). I understand where is an issue, but I am not able to figure out the right solution to this. Any suggestions?
CreateHTMLTable is a method, used to convert list into a string (=HTML table).
public static string CreateHTMLTable<T>(IEnumerable<T> list, params Func<T, (object, string)>[] fxns) // Used for generating HTML table
{
var sb = new StringBuilder();
sb.Append("<table>\n");
sb.Append("<thead>\n");
sb.Append("<tr>\n");
foreach (var fxn in fxns)
sb.Append("<th>").Append(fxn(default).Item2).AppendLine("</th>");
sb.Append("</tr>\n");
sb.Append("</thead>\n");
foreach (var item in list)
{
sb.Append("<tr>\n");
foreach (var fxn in fxns)
{
sb.Append("<td>");
sb.Append(fxn(item).Item1);
sb.Append("</td>");
}
sb.Append("\n</tr>\n");
}
sb.Append("</table>");
return sb.ToString();
}
I have figured it out, although I don't know is it the best possible solution. I have added one "intermediate" list to be created and writing to it/emptying it in loop.
public static void SendEmail()
{
string HTMLTableInString = "";
string PM = "";
List<ListDefaultStructure> ListForEmailTable = new List<ListDefaultStructure>();
var combined = List1
.Concat(List2)
.Concat(List3)
.Concat(List4)
.Concat(List5)
.Concat(List6)
.Concat(List7)
.Concat(List8)
.ToList();
if (combined.Any())
{
var GroupedList = combined.GroupBy(x => x.User)
.Select(grp => grp.ToList())
.ToList();
foreach (var item in GroupedList)
{
foreach (var pos in item)
{
ListForEmailTable.Add(new ListDefaultStructure
{
ProjectNumber = pos?.ProjectNumber,
SubProjectNumber = pos?.SubProjectNumber,
SubProjectName = pos?.SubProjectName,
User = pos?.User
});
PM = pos.User;
}
if (!string.IsNullOrWhiteSpace(PM))
{
HTMLTableInString = Other.CreateHTMLTable(GroupedList,
x => (pos.ProjectNumber, "Project number"),
x => (pos.SubProjectNumber, "Sub-project number"),
x => (pos.SubProjName, "Sub-project name"),
x => (pos.User, "User")
);
Console.WriteLine("-------------------------------------------------------------");
Console.WriteLine(HTMLTableInString);
Console.WriteLine("-------------------------------------------------------------");
// Send an email
}
}
}
}
I am trying to create a ViewModel list with info from Receipt table and then if related info exist in Reason table retreive the last Description inserted (ReceiptId related) and add it (if not just pass null) to the ViewModel Receipt list (RejectDescription). Here's the DB model:
Database Model
I tryied many ways to achieve this, at the moment this is the code that partially works for me, i say partially because in RejectDescription saves the Reason.Description if it exist else just pass null and it's ok.
The main problem is when there's many Reason.Descriptions it doesn't return and save the last one inserted (the most recent, is the one that i am looking for). Here is my code:
[HttpPost]
public ActionResult ReceiptList(string Keyword)
{
using (SEPRETEntities DBC = new SEPRETEntities())
{
long UserId = (long)Session["Id"];
IEnumerable<Receipt> receipts = DBC.Receipts.Where(x => x.PersonId == UserId && x.Active == true).ToList();
#region Search
if (!string.IsNullOrEmpty(Keyword))
{
Keyword = Keyword.ToLower();
receipts = receipts.Where(x => x.Person.Name.ToLower().Contains(Keyword) ||
x.Person.MiddleName.ToLower().Contains(Keyword) ||
x.Person.LastName.ToLower().Contains(Keyword) ||
x.Person.Email.ToLower().Contains(Keyword) ||
x.Person.Enrollment.ToLower().Contains(Keyword) ||
x.Person.Career.ToLower().Contains(Keyword) ||
x.Payment.Name.ToLower().Contains(Keyword) ||
x.Payment.Price.ToString().ToLower().Contains(Keyword) ||
x.Method.Name.ToLower().Contains(Keyword) ||
x.Phase.Name.ToLower().Contains(Keyword) ||
x.TimeCreated.ToString().ToLower().Contains(Keyword) ||
x.Voucher.ToString().ToLower().Contains(Keyword)
);
}
#endregion
List<ReceiptVM> ReceiptList = receipts.Select(x => new ReceiptVM
{
Id = x.Id,
PaymentId = x.PaymentId,
Enrollment = x.Person.Enrollment,
Career = x.Person.Career,
PersonName = string.Concat(x.Person.Name, " ", x.Person.MiddleName, " ", x.Person.LastName),
Email = x.Person.Email,
PaymentName = x.Payment.Name,
MethodName = x.Method.Name,
Voucher = x.Voucher,
Image = x.Image,
PhaseId = x.Phase.Id,
PriceFormatted = x.Payment.Price.ToString("C"),
Active = x.Active,
TimeCreatedFormatted = x.TimeCreated.ToString(),
RejectDescription = x.Rejections.FirstOrDefault(y => y.ReasonId == y.Reason.Id)?.Reason.Description
}).ToList();
return PartialView("~/Views/Receipt/_SearchReceipt.cshtml", ReceiptList);
}
}
For you information i am kinda newbie working on C# and ASP.NET MVC.Not sure if there's a better way to achieve this or something, any advice or tip is pretty appreciated.
Thank you and sorry for my bad english
You have to order reject reasons by Id which will fetch recent reason like below :
RejectDescription = x.Rejections.OrderByDescending(x=>x.Reason.Id).FirstOrDefault(y => y.ReasonId == y.Reason.Id)?.Reason.Description
Or you can use LastOrDefault to get most recent one like below:
RejectDescription = x.Rejections.LastOrDefault(y => y.ReasonId == y.Reason.Id)?.Reason.Description
List<ReceiptVM> ReceiptList = receipts.Select(x => new ReceiptVM
{
Id = x.Id,
PaymentId = x.PaymentId,
Enrollment = x.Person.Enrollment,
Career = x.Person.Career,
PersonName = string.Concat(x.Person.Name, " ", x.Person.MiddleName, " ", x.Person.LastName),
Email = x.Person.Email,
PaymentName = x.Payment.Name,
MethodName = x.Method.Name,
Voucher = x.Voucher,
Image = x.Image,
PhaseId = x.Phase.Id,
PriceFormatted = x.Payment.Price.ToString("C"),
Active = x.Active,
TimeCreatedFormatted = x.TimeCreated.ToString(),
RejectDescription = x.Rejections.OrderByDescending(x=>x.Reason.Id).FirstOrDefault(y => y.ReasonId == y.Reason.Id)?.Reason.Description
}).ToList();
I am calling a web api and saving the records on the database through the controller, i want each time im calling the api to check if the record exists in the database if yes then dont save, if not then save.
var client = new WebClient();
var text = client.DownloadString("https://www.test.com/api/all-users?name=testusername%20&pass=334432");
var wclients = JsonConvert.DeserializeObject<dynamic>(text);
List<apicli> list1 = new List<apicli>();
var clie = new apicli();
if (wclients.message == "success")
{
var data = wclients.data;
//var account = wclients.account;
ViewBag.test = data;
foreach(var item in ViewBag.test)
{
clie.Email = item.email;
clie.Name = item.name;
clie.Aff = item.affiliated_id;
foreach(var item1 in #item.account.real)
{
clie.Login = item1.login;
clie.password = item1.pass;
}
list1.Add(clie);
db.apiclis.AddRange(list1);
db.SaveChanges();
};
}
I would assume you need something like this, although you need to check what is the unique id of each record:
foreach(var item in data){
var c = new apicli {
Email = item.email,
Name = item.name,
Aff = item.affiliated_id
Login = item.account.real.LastOrDefault()?login??"",
Login = item.account.real.LastOrDefault()?pass??""
}
if(!db.apiclis.Any(a => a.Email == c.Email && a.Name == c.Name && a.Aff == c.Aff)){
db.apiclis.Add(c);
}
}
Here I assume that email+name+aff = unique identificator.
I have a cron-job method which constructs a user's story feed based on user's featured stories, followed categories and followed users.
The final feed is added in the below database table in correct order:
UserFeed table:
Uid StoryListIds
1 3, 23, 45, 3, 6, 234, .....
2 3, 23, 45, 6, 87, 44, .....
3 3, 23, 45, 32, 4, 62, .....
The method is below and contains comments.
The code:
public void ConstructUserFeed()
{
try
{
//get all user ids
var userIds = userBL.GetIds();
//get all featured stories
var featStories = storyBL.GetFeaturedStories();
if (userIds != null && userIds.Count > 0)
{
foreach (var userId in userIds)
{
//integer List to store the stories ids in correct order
List<int> storyIdsLst = new List<int>();
//integer List for excluding duplicates
List<int> exceptStoryIds = new List<int>();
//user feed List to store the stories
var userFeed = new List<UserFeedDTO>();
//string to store the id list so we can add it later in db
string storyIds = "";
if (featStories != null && featStories.Count > 0)
{
foreach (var featStory in featStories)
{
//first add all the ids of featured stories except own user stories, ordered by date
if (featStory.authorId != userId)
{
storyIdsLst.Add(featStory.id);
exceptStoryIds.Add(featStory.id);
}
}
}
//get user's followed categories ids
var followedCategoryIds = userCategoriesBL.GetFollowedCategoryIds(userId);
if (followedCategoryIds != null && followedCategoryIds.Count > 0)
{
foreach (var categoryId in followedCategoryIds)
{
//get the user's 5 latest stories for every followed category
//except own stories and previous stories
var storiesByCateg = storyBL.GetByCategory(5, categoryId, userId, exceptStoryIds);
if (storiesByCateg != null && storiesByCateg.Count > 0)
{
foreach (var storyByCateg in storiesByCateg)
{
userFeed.Add(storyByCateg);
exceptStoryIds.Add(storyByCateg.id);
}
}
}
}
//get user's followed users ids
var followedUserIds = userFollowersBL.GetFollowedUserIds(userId);
if (followedUserIds != null && followedUserIds.Count > 0)
{
foreach (var followedId in followedUserIds)
{
//get the user's 5 latest stories for every followed user
//except own stories and previous stories
var storiesByFollowedUsers = storyBL.GetByFollowedUser(5, followedId, userId, exceptStoryIds);
if (storiesByFollowedUsers != null && storiesByFollowedUsers.Count > 0)
{
foreach (var storyByFollowedUsers in storiesByFollowedUsers)
{
userFeed.Add(storyByFollowedUsers);
}
}
}
}
// order the stories by date
userFeed = userFeed.OrderByDescending(story => story.dateAdded).ToList();
if (userFeed != null && userFeed.Count > 0)
{
foreach (var story in userFeed)
{
//add the story ids after the featured story ids
storyIdsLst.Add(story.id);
}
}
//comma separated list of story ids as string so we can store it in db
storyIds = string.Join(",", storyIdsLst.Select(n => n.ToString()).ToArray());
//create the UserFeed model
UserFeed userFeedModel = new UserFeed();
userFeedModel.userId = userId;
userFeedModel.storyListId = storyIds;
userFeedModel.lastUpdateTime = DateTime.Now;
userFeedBL.AddOrUpdateUserFeed(userFeedModel);
}
uof.Save();
}
}
catch (Exception ex)
{
Console.WriteLine("Error occurred in processing job. Error : {0}", ex.Message);
}
}
The above method takes ~35 seconds to complete for 30 users.
Q: How can I improve my code and performance?
It's hard to say exactly what's causing it to execute so slowly, for that I recommend profiling your question with SQL Server Profiler. Check that your queries is properly asked and doesn't do anything unnecessary.
After that, I would consider asking fewer questions. Since you're doing it in a loop you may benefit from doing fewer, but heavier questions. For example (assuming userFollowersBL.* is querying the DB):
var followedCategoryIds = userCategoriesBL.GetFollowedCategoryIds(userId);
I assume that the signature is something like:
IEnumerable<int> GetFollowedCategoryIds(userId);
Consider changing it to:
IDictionary<int, IEnumerable<int>> GetFollowedCategoryIds(IEnumerable<int> userIds);
Then you'll have a dictionary with the userids and each of their followedCategoryIds in memory before you start your foreach. This way you can send all your result from userBL.GetIds() in one query. It may speed up performance to do it once, instead of 30 times. The same thing goes for userFollowersBL.GetFollowedUserIds(userId).
Now you have decreased the number of queries to you DB with approx 58 times.
public void ConstructUserFeed()
{
try
{
//get all user ids
var userIds = userBL.GetIds();
//get all featured stories
var featStories = storyBL.GetFeaturedStories();
// Fetch all in one query.
IDictionary<int,IEnumerable<int>> allFollowedCategoryIds= userCategoriesBL.GetFollowedCategoryIds(userIds);
// Fetch all in one query
IDictionary<int,IEnumerable<int>> allUserFollowers = userFollowersBL.GetFollowedUserIds(userIds);
if (userIds != null && userIds.Count > 0)
{
foreach (var userId in userIds)
{
//integer List to store the stories ids in correct order
List<int> storyIdsLst = new List<int>();
//integer List for excluding duplicates
List<int> exceptStoryIds = new List<int>();
//user feed List to store the stories
var userFeed = new List<UserFeedDTO>();
//string to store the id list so we can add it later in db
string storyIds = "";
if (featStories != null && featStories.Count > 0)
{
foreach (var featStory in featStories)
{
//first add all the ids of featured stories except own user stories, ordered by date
if (featStory.authorId != userId)
{
storyIdsLst.Add(featStory.id);
exceptStoryIds.Add(featStory.id);
}
}
}
//get user's followed categories ids
var followedCategoryIds = allFollowedCategoryIds[userId]
if (followedCategoryIds != null && followedCategoryIds.Count > 0)
{
foreach (var categoryId in followedCategoryIds)
{
//get the user's 5 latest stories for every followed category
//except own stories and previous stories
var storiesByCateg = storyBL.GetByCategory(5, categoryId, userId, exceptStoryIds);
if (storiesByCateg != null && storiesByCateg.Count > 0)
{
foreach (var storyByCateg in storiesByCateg)
{
userFeed.Add(storyByCateg);
exceptStoryIds.Add(storyByCateg.id);
}
}
}
}
//get user's followed users ids
var followedUserIds = allUserFollowers[userId];
if (followedUserIds != null && followedUserIds.Count > 0)
{
foreach (var followedId in followedUserIds)
{
//get the user's 5 latest stories for every followed user
//except own stories and previous stories
var storiesByFollowedUsers = storyBL.GetByFollowedUser(5, followedId, userId, exceptStoryIds);
if (storiesByFollowedUsers != null && storiesByFollowedUsers.Count > 0)
{
foreach (var storyByFollowedUsers in storiesByFollowedUsers)
{
userFeed.Add(storyByFollowedUsers);
}
}
}
}
// order the stories by date
userFeed = userFeed.OrderByDescending(story => story.dateAdded).ToList();
if (userFeed != null && userFeed.Count > 0)
{
foreach (var story in userFeed)
{
//add the story ids after the featured story ids
storyIdsLst.Add(story.id);
}
}
//comma separated list of story ids as string so we can store it in db
storyIds = string.Join(",", storyIdsLst.Select(n => n.ToString()).ToArray());
//create the UserFeed model
UserFeed userFeedModel = new UserFeed();
userFeedModel.userId = userId;
userFeedModel.storyListId = storyIds;
userFeedModel.lastUpdateTime = DateTime.Now;
userFeedBL.AddOrUpdateUserFeed(userFeedModel);
}
uof.Save();
}
}
catch (Exception ex)
{
Console.WriteLine("Error occurred in processing job. Error : {0}", ex.Message);
}
}
But honestly, this is mostly speculations and may differ from time to time. In my experience you tend to benefit from asking for more data once, than the same data several times.
Look at the following code example.
What it does:
Iterates a bunch of customers. If it already knows the customer, it retrieves the existing database object for that customer (this is the problem-ridden part). Otherwise, it creates a new object (this works fine).
All loans where the social security number matches (CPR) will be added to the new or existing customer.
The problem: it works for new customer objects, but when I retrieve an existing customer object, the loans lose their relation to the customer when saved (CustomerID = null). They are still saved to the database.
Any ideas?
protected void BuildCustomerData()
{
Console.WriteLine(" Starting the customer build.");
var counter = 0;
var recycleCount = 100;
var reportingCount = 100;
var sTime = DateTime.Now;
var q = from c in db.IntermediaryRkos
select c.CPR;
var distincts = q.Distinct().ToArray();
var numbersToProcess = distincts.Count();
Console.WriteLine(" Identified " + numbersToProcess + " customers. " + (DateTime.Now - sTime).TotalSeconds);
foreach (var item in distincts)
{
var loans = from c in db.IntermediaryRkos
where c.CPR == item
select c;
var existing = db.Customers.Where(x => x.CPR == item).FirstOrDefault();
if (existing != null)
{
this.GenerateLoanListFor(existing, loans);
db.Entry(existing).State = System.Data.EntityState.Modified;
}
else
{
var customer = new Customer
{
CPR = item,
};
this.GenerateLoanListFor(customer, loans);
db.Customers.Add(customer);
db.Entry(customer).State = System.Data.EntityState.Added;
}
counter++;
if (counter % recycleCount == 0)
{
this.SaveAndRecycleContext();
}
if (counter % reportingCount == 0)
{
Console.WriteLine(" Processed " + counter + " customers of " + numbersToProcess + ".");
}
}
db.SaveChanges();
}
protected void GenerateLoanListFor(Customer customer, IQueryable<IntermediaryRko> loans)
{
customer.Loans = new List<Loan>();
foreach (var item in loans.Where(x => x.DebtPrefix == "SomeCategory").ToList())
{
var transformed = StudentLoanMap.CreateFrom(item);
customer.Loans.Add(transformed);
db.Entry(transformed).State = System.Data.EntityState.Added;
}
}
EDIT 1:
As pointed out, I am manually setting the state. This is due to the RecycleContext call, which is implemented for maximum db transaction performance:
protected void SaveAndRecycleContext()
{
db.SaveChanges();
db.Dispose();
db = new SolutionDatabase();
db.Configuration.AutoDetectChangesEnabled = false;
db.Configuration.ValidateOnSaveEnabled = false;
}
Existing loan or not, you wipe out the customer.Loans Property when you call
customer.Loans = new List<Loan>();