Efficiently populating an ASP.Net Calendar control - c#

I have an ASP.Net Calendar control that, on DayRender, loops through a list of "CalendarEvents" (one of my classes). If the day that DayRender is on is in the list of CalendarEvents, then fill that calendar cell with the details of the event.
In my test environment, there is no problem, but I imagine with thousands of records, it might become a problem. Here is what I'm doing:
public partial class calendar : System.Web.UI.Page
{
CalendarEventList allevents = CalendarEventManager.GetListAll();
//Page_Load, etc...
protected void calmaincalendar_DayRender(object sender, DayRenderEventArgs e)
{
foreach (CalendarEvent x in allevents)
{
if (e.Day.Date == x.EventDate)
{
//logic to extract the data from x, and modify the e.Cell to display that data
}
}
}
}
My questions are:
1) I'm putting the events as a page level variable, so that the database only needs to be called once, and not on every single DayRender. Good, bad? Potential issues? This page is purely to read the Calendar Events, not edit or delete.
2) Looping through the "allevents" list on every DayRender, is that potentially dangerous/resource intensive? Does the Calendar control call DayRender for just the month that's being displayed? If so, meaning only 30 or 31 calls at a time, it might be fine. But if it does something like calls DayRender for the entire year, or the previous and next months as well...Anyway, hopefully you get my point. Advice on this would be appreciated.
3) Is there anything that I could be doing better? I realize I only posted the bare skeleton, but if anyone has any similar experience, or pitfalls they wished they had realized earlier, that advice would be appreciated.
As I was typing up the questions I realized that when I get the CalendarEventList at the page level, I could immediately loop through it and cut out everything except for the last 3 and future 3 months or something. Problem there is, somehow keeping track of it so that when the user goes back 4 months, I'd have to realize this and call the database again. I have a vague feeling that the answer is somewhere in that direction, but still not sure.

As you talk about records I guess you have an underlying database. If so, I will suggest to create a query for that, so you can do this:
foreach (CalendarEvent x in CalendarEventManager.GetForDate(e.Day.Date))
{
//logic to extract the data from x, and modify the e.Cell to display that data
}
This way, you will retrieve just the needed records.

Related

Is there a way to Clone one or many Entities (records) in Code

NOTE: at this time I am stuck on 2sxc v9.43.2 on this project.
After selecting a set of records from my Content Type, I need to be able to duplicate them changing 1 of the fields along the way. Here is my almost-working idea so far. The use case is simple, they have Programs that people can register for. They change each Season, but only a little (prices, dates/times, etc). And they need the Current Season live and unchanged while they edit the Next Season. So we are still in the fall season (EntityId 1732) with 97 active programs. We want to click a button and clone all 97 programs as is, but IN TO the new Next Season (1735 below).
Two questions:
if this way works, what syntax would work on ent/Attributes to delivery the "object" as needed in the fields.Add() line
is there another 2sxc way to do this? Some other variant of the App.Data.Create() method or some other method in the API? I just need to duplicate the record with 1 field (Season) changed?
is there a better way to do this in the latest versions of 2sxc, v11.7+ ?
// we are going to duplicate the current Season's programs in to the new season
// cheating for now, pre-made new 1735 in Seasons, current is 1732
var programs = AsDynamic(App.Data["Programs"])
.Where(p => ((List<DynamicEntity>)p.Season).First().EntityId == selectedSeason.EntityId);
// #programs.Count() // 97
foreach(var copy in programs)
{
var fields = new Dictionary<string, object>();
var ent = AsEntity(copy);
foreach(var attr in ent.Attributes)
{
if(attr.Key == "Season")
{
fields.Add(attr.Key, new List<int> { 1735 });
}
else
{
fields.Add(attr.Key, ent.GetBestValue(attr.Key)); // object??
}
}
App.Data.Create("Programs", fields);
}
There are at least 3 ways to clone
Simple way using edit-ui
hard way using c# / server api
Semi-hard way using REST api
The simple way is to use the edit ui. You can see an example in the replace-dialog, there is a copy button there. This would open the edit UI with an existing item, but tell it it's a copy, so on save it would create a new one.
Combine this with a prefill or something and I think you would be good to go.
The second way is using the App.Data.Create - your code looks fairly good. I assume it also works and you were just wondering if there was a 1-liner - or am I mistaken?
The last way is using JS REST. Basically write some JS that gets an item, changes the object (resets the id) and posts it back to the endpoint for saving.
Just stumbled upon situation where I needed to create entity and set its field value, which has type of another entity. If that's your question #1, you need to add there EntityGuid.
fields.Add(attr.Key, attr.EntityGuid);. That should bind one entity to another one.
And no, I didn't stumble upon better way to copy entity then just to create a new one. At least so far.

ComboBox List Add Items From Database During User Input

Explanation
Typing in Combobox adds items from the database that contain% the given string. It is too much data, so I'm having to limit the query else the program freezes. The limit prevents future updates to my Combobox, and any attempt to clear the items per text changed event causes a crash with the error: Attempted to read or write protected memory. This is often an indication that other memory is corrupt. In addition to the crash, I need the limit removed, but doing so causes the items added to flood the Combobox with too much data, e.g. the 10,000+ items.
Text Update Event
private void PartDescription_TextUpdate()
{
OdbcLink.ConnectODBC(this, $"Select DISTINCT PM_PRT, PM_DES, PM_NET " +
$"From PFWF0022.PARTMAST WHERE PM_PRT LIKE '{partNumberInput.Text}%' LIMIT 10");
}
Yes, I realize this query is susceptible to SQL injection but I need to work out the kinks and that fix will be coming afterward if I'm able to find a solution.
Data Handling From Query
if(queryString.Contains("WHERE PM_PRT LIKE"))
{
//DEBUG: This line of code causes the memory issue, but it was my best attempt
//at trying to get the data to update as the queries came in.
form.partNumberInput.Items.Clear();
while (reader.Read())
{
//INFO: This is my comboBox and this adds to it for each item
//returned from the database.
form.partNumberInput.Items.Add(reader[0].ToString());
}
}
Expected Solution
I'd like to be able to poll through my database queries dynamically without overloading the comboBox suggestions that are generated. The suggestions MUST be pulled from the database as items are continually changing minute to minute, and loading at application start isn't an option since it will take quite some time to return that data.

How to display banners equally? [closed]

Closed. This question needs to be more focused. It is not currently accepting answers.
Want to improve this question? Update the question so it focuses on one problem only by editing this post.
Closed 7 years ago.
Improve this question
I have a website with an admin session where a admin can add a banner. The banner entity has a int ClickLimit and a int VisualizationLimit. So every time this banner is displayed i increment +1 to the VisualizationLimit and the same for the click.
The problem is that i need some kind of FIFO queue. On the logic above it will not work, because if a user clicks 10 times and then some admin adds a new banner, the int ClickLimit and a int VisualizationLimit will be 0 for the new banner, so this banner will be displayed 9 times and the older banner will not be displayed.
Goals:
I need to show the banners equally, not randomly. Like a FIFO queue, when the less viewed banner is displayed, it will go to the end of the queue.
I tried to find some pattern to implement it but without lucky. Whats the best solution to this situation?
Why don't you just add a 'LastViewed' on the banner entity. You can show whatever hasn't been viewed recently.
Just increment ViewCount and set LastViewed time on the entity when it is viewed and then show whatever has the oldest date.
How about keeping a temporary counter (List) in am in-memory cache (Shared cache if load balancing).
So you could store something in cache:
imagecount-image1:19
imagecount-image2:17
imagecount-image3:18
^
use a prefix so you can identify these as part of the same set.
Each time you display an image increment the cached count, and DB count. (This way the DB Count is the lifetime count) the cache can then be deleted when a new image is added, so all the images will get displayed fairly.
You will need some code to manage the cache, so that you can work with a list of imageCounters rather than dealing with each one individual.
Just a suggestion - hope it helps
This seems like a sequencing problem. For instance, if you have a list of banners in memory, you could determine which to show by:
var banner = banners.OrderBy (_ => _.VisualizationLimit).First ();
Then you'll want to increment your values and update your database, or wherever you're persisting your information.
If my assumption is incorrect that you don't already have your list of banners, then you'll need to query them from your database. This presents an interesting concurrency problem that could occur in high-traffic sites...
A typical scenario might be to load the next banner based off a group by/having clause where you look for the smallest VisualizationLimit banner, then increment those values in memory, and update your database. The potential problem here is that under heavy load, the server may swap CPU time with another thread BEFORE you've updated your database. Now you're loading the same banner multiple times based on stale information.
Without writing a book of an answer, if you have a low-traffic site then you probably won't have to worry much about concurrency. If you are expecting a large amount of traffic then you might want to take this under consideration and plan your queries and updates appropriately or your numbers may not be accurate.
Edit: After re-reading your problem, it seems that you DON'T want to display banners repeatedly until they've caught up visually. In this case, you could add a timestamp to the table in your database and query the banner that hasn't been viewed for the longest. The same principles above still apply, including the potential concurrency issues.
Hope this helps and good luck!

Loading data from SQL database into a list in C#

I'm working on an application that displays (highlighted) the dates on which a certain event will occur.
In the database that I have to use there is a table that contains date intervals or collections of dates that have special meaning (like collection of holidays, school breaks...). When an event is defined, the user defines on which day(s) that event occurs (if it occurs periodically), but the user can also define that that particular event does or does not occur on special interval.
For instance - there can be an event that occurs every Sunday, if that days is not a holiday.
Now, I've solved the periodical part by reading data from the database for that event (determining on which day does that event occur) and then filling a list with dates that will be highlighted (for graphical representation I use MotnhCalendar and its BoldedDates array):
for (DateTime day = start; day < end;day = day.AddDays(1))
{
if (Sunday && day.DayOfWeek == DayOfWeek.Sunday)
{
MarkedDates.Add(day);
}
monthCalendar1.BoldedDates = MarkedDates.ToArray();
}
Now, for this code to work properly, I need to skip the dates that belong to a special interval (in this case if that date is a holiday). It could be easily done by adding another condition into the if clause:
!SpecialDates.Contains(day)
The problem is that I don't know how to fill the list with these special dates from the database. I can't simply "hard code" it, because these special date collections/intervals can be changed at any time. So, my question is - how can I, by using SQL and C# commands, read data from database and save it in the list.
Thanks
As it looks like your using SQL Server 2008 R2 I would recommend using an ORM tool like ADO.NET Entity Framework to access your database - best going with the latest release EF 5.
There are tons of tutorials online on how to get up & running with it - a good one being Creating Model Classes with the Entity Framework.
To give you an idea of how simple it makes it, here is an example of the minimal amount of code you would need to achieve what it is your looking to do:
using (var db = new MyDbContext())
{
var specialDates = db.SpecialDatesTable.ToList();
}

Displaying a single value from a one to many relationship in a GridView

I have 3 tables:
Order,
OrderStates,
OrderStateDefinition
An Order has many OrderStates which then has one OrderStateDefinition.
I have a gridview in which I am trying to display only one value inside the OrderStates collection - the latest OrderState that has been added.
I've read a little about subqueries but I'm unsure about how to go about achieving the result I want.
Sorry bout the lack of information, I had a nice picture all set up of the table structure but stackoverflow wouldn't let me upload it.
Edit -
OK I figured out how to do this. As the GridView was being populated I used the event OnRowCreated to then set the text of the field I required. To get to the control I needed I used the e.Row.FindControl.
The code for it was pretty simple in the end. I always seem to figure this stuff out when I finally ask for help.
try
{
int orderID = e.Row.RowIndex;
Order order = ShopEntities.Orders.Single(o => o.OrderStateID == orderID);
// I can now get the list of orderstates
OrderStateDefinition osd = order.OrderStates.OrderBy(o => o.Date).Last().OrderStateDefinition;
((Label)e.Row.FindControl("Label2")).Text = osd.State;
}
catch
{
}
I often find it's easier to create your own SQL that does this. the sql might be a little complex, but it's easier than mucking with the c#.

Categories

Resources