How to create session variables without knowing how many to create? - c#

First of all, I'm using Visual Studio 2010 (Visual C#) and ASP.NET.
I'm working with a GridView that displays the current open positions at my company. I have a column of checkboxes where applicants can check off the position(s) for which they want to apply. To eliminate duplicate data, I created a linking table between my three main tables (POSITION, APPLICANT, and APPLICATION). It's made up of just the primary keys from each of those tables, so if one person applies for 3 positions, we won't have 3 whole applications to sift through.
I need to select the PositionID's of the positions they selected and store them in session variables for later use.
My question is, how do I do that without knowing how many they have checked? I don't want to just create a bunch of unnecessary variables that won't be used. I figure I'll have to use a foreach loop, but within the loop, I don't know how to tell it to create a new session variable and store the ID in it.
I sure hope this question makes sense. If you need clarification, let me know.

You can just create a List<int> or whatever datatype PositionID is and store that in the session variable.
In fact, I would create a property in the control or page as
public List<int> SelectedPositionIDList
{
get
{
if(Session["SelectedPositions"] != null)
return Session["SelectedPositions"] as List<int>;
return new List<int>();
}
set
{
Session["SelectedPositions"] = value;
}
}
you can iterate through the list of position ids as ,
foreach(int positionId in SelectedPositionIdList)
{
//Do something.
}
Of course, you need to grab the ids from the gridview when they want to save, or do some action. You can probably do that by looping through the gridview rows based on your implementation. Something like below.
List<int> positions = new List<int>();
foreach(GridviewRow row in gdPositions)
{
CheckBox cb = row.FindControl("checkbox") as CheckBox;
if(cb != null && cb.Checked)
positions.Add(/*find position id from row here*/);
}
if(positions.Count > 0)
Session["SelectedPositionIdList"] = positions;

Related

C# List Sort vs Inserting

I have a list of objects which I sort multiple times throughout code and when the user interacts with the program. I was wondering if it would be better to insert new items into the list rather than add to the end of the list and resort the entire list.
The code below is for importing browser bookmarks - Here I add a bunch of bookmarks to the List (this._MyLinks) which are Link objects and then sort the final List - Which I think is probably best in this given scenario....
public void ImportBookmarks(string importFile)
{
using (var file = File.OpenRead(importFile))
{
var reader = new NetscapeBookmarksReader();
var bookmarks = reader.Read(file);
foreach (var b in bookmarks.AllLinks)
{
bool duplicate = this._MyLinks.Any(link => link._URL == b.Url);
if(duplicate)
{
continue;
}
Link bookmark = new Link();
bookmark._URL = b.Url;
bookmark._SiteName = b.Title;
bookmark.BrowserPath = "";
bookmark.BrowserName = "";
if (bookmark.AddToConfig(true))
{
this._MyLinks.Add(bookmark);
}
}
}
this._MyLinks = this._MyLinks.OrderBy(o => o._SiteName).ToList();
}
Now a user also has the option to add their own links (one at a time). Whenever the user adds a link the ENTIRE list is sorted again using
this._MyLinks = this._MyLinks.OrderBy(o => o._SiteName).ToList();
Is it better from a preformance standpoint (or just generally) to just insert the item directly into it's specified location? If so would you have suggestions on how I can go about doing that?
Thanks!
Since you want a sorted set of data you should be using a more appropriate data structure, specifically a sorted data structure, rather than using an unsorted data structure that you re-sort every time, or that forces you to inefficiently add items to the middle of a list.
SortedSet is specifically designed to maintain a sorted set of data efficiently.

LINQ to SharePoint 2010 getting error "All new entities within an object graph must be added/attached before changes are submitted."

I've been having a problem for some time, and I've exhausted all means of figuring this out for myself.
I have 2 lists in a MS Sharepoint 2010 environment that are holding personal physician data for a medical group...nothing special just mainly text fields and a few lookup choice fields.
I am trying to write a program that will migrate the data over from List A to List B. I am using LINQ to Sharepoint to accomplish this. Everything compiles just fine, but when it runs and hits the SubmitChanges() method, I get a runtime error that states:
"All new entities within an object graph must be added/attached before changes are submitted."
this issue must be outside of my realm of C# knowledge because I simply cannot find the solution for it. The problem is DEFINITELY stemming from the fact that some of the columns are of type "Lookup", because when I create a new "Physician" entity in my LINQ query, if I comment out the fields that deal with the lookup columns, everything runs perfectly.
With the lookup columns included, if I debug and hit breakpoints before the SubmitChanges() method, I can look at the new "Physician" entities created from the old list and the fields, including data from the lookup columns, looks good, the data is in there the way I want it to be, it just flakes out whenever it tries to actually update the new list with the new entities.
I have tried several methods of working around this error, all to no avail. In particular, I have tried created a brand new EntityList list and calling the Attach() method after each new "Physician" Entity is created, but to no avail, it just sends me around in a bunch of circles, chasing other errors such as "ID cannot be null", "Cannot insert entities that have been deleted" etc.,
I am no farther now than when I first got this error and any help that anyone can offer would certainly be appreciated.
Here is my code:
using (ProviderDataContext ctx = new ProviderDataContext("http://dev"))
{
SPSite sitecollection = new SPSite("http://dev");
SPWeb web = sitecollection.OpenWeb();
SPList theOldList = web.Lists.TryGetList("OldList_Physicians");
//Create new Physician entities.
foreach(SPListItem l in theOldList.Items)
{
PhysiciansItem p = new PhysiciansItem()
{
FirstName = (String)l["First Name"],
Title = (String)l["Last Name"],
MiddleInitial = (String)l["Middle Init"],
ProviderNumber = Convert.ToInt32(l["Provider No"]),
Gender = ConvertGender(l),
UndergraduateSchool =(String)l["UG_School"],
MedicalSchool = (String)l["Med_School"],
Residency = (String)l["Residency"],
Fellowship = (String)l["Fellowship"],
Internship = (String)l["Internship"],
PhysicianType = ConvertToPhysiciantype(l),
Specialty = ConvertSpecialties(l),
InsurancesAccepted = ConvertInsurance(l),
};
ctx.Physicians.InsertOnSubmit(p);
}
ctx.SubmitChanges(); //this is where it flakes out
}
}
//Theses are conversion functions that I wrote to convert the data from the old list to the new lookup columns.
private Gender ConvertGender(SPListItem l)
{
Gender g = new Gender();
if ((String)l["Sex"] == "M")
{
g = Gender.M;
}
else g = Gender.F;
return g;
}
//Process and convert the 'Physician Type', namely the distinction between MD (Medical Doctor) and
//DO (Doctor of Osteopathic Medicine). State Regualtions require this information to be attached
//to a physician's profile.
private ProviderTypesItem ConvertToPhysiciantype(SPListItem l)
{
ProviderTypesItem p = new ProviderTypesItem();
p.Title = (String)l["Provider_Title:Title"];
p.Intials = (String)l["Provider_Title"];
return p;
}
//Process and convert current Specialty and SubSpecialty data into the single multi-choice lookup column
private EntitySet<Item> ConvertSpecialties(SPListItem l)
{
EntitySet<Item> theEntityList = new EntitySet<Item>();
Item i = new Item();
i.Title = (String)l["Provider Specialty"];
theEntityList.Add(i);
if ((String)l["Provider SubSpecialty"] != null)
{
Item theSubSpecialty = new Item();
theSubSpecialty.Title = (String)l["Provider SubSpecialty"];
theEntityList.Add(theSubSpecialty);
}
return theEntityList;
}
//Process and add insurance accepted.
//Note this is a conversion from 3 boolean columns in the SP Environment to a multi-select enabled checkbox
//list.
private EntitySet<Item> ConvertInsurance(SPListItem l)
{
EntitySet<Item> theEntityList = new EntitySet<Item>();
if ((bool)l["TennCare"] == true)
{
Item TenncareItem = new Item();
TenncareItem.Title = "TennCare";
theEntityList.Add(TenncareItem);
}
if ((bool)l["Medicare"] == true)
{
Item MedicareItem = new Item();
MedicareItem.Title = "Medicare";
theEntityList.Add(MedicareItem);
}
if ((bool)l["Commercial"] == true)
{
Item CommercialItem = new Item();
CommercialItem.Title = "Commercial";
theEntityList.Add(CommercialItem);
}
return theEntityList;
}
}
So this may not be the answer you're looking for, but it's what's worked for me in the past. I've found that updating lookup fields using Linq to Sharepoint to be quite frustrating. It frequently doesn't work, or doesn't work efficiently (forcing me to query an item by ID just to set the lookup value).
You can set up the entity so that it has an int property for the lookup id (for each lookup field) and a string property for the lookup value. If, when you generate the entities using SPMetal, you don't generate the list that is being looked up then it will do this on it's own. What I like to do is (using your entity as an example)
Generate the entity for just that one list (Physicians) in some temporary folder
Pull out the properties for lookup id & value (there will also be private backing fields that need to come along for the ride too) for each of the lookups (or the ones that I'm interested in)
Create a partial class file for Physicians in my actual project file, so that regenerating the entire SPMetal file normally (without restricting to just that list) doesn't overwrite changes
Paste the lookup id & value properties in this partial Physicians class.
Now you will have 3 properties for each lookup field. For example, for PhysicianType there will be:
PhysicianType, which is the one that is currently there. This is great when querying data, as you can perform joins and such very easily.
PhysicianTypeId which can be occasionally useful for queries if you only need ID as it makes it a bit simpler, but mostly I use it whenever setting the value. To set a lookup field you only need to set the ID. This is easy, and has a good track record of actually working (correctly) in my experiences.
PhysicianTypeValue which could be useful when performing queries if you just need the lookup value, as a string (meaning it will be the raw value, rather than something which is already parsed if it's a multivalued field, or a user field, etc. Sometimes I'd rather parse it myself, or maybe just see what the underlying value is when doing development. Even if you don't use it and use the first property, I often bring it along for the ride since I'm already doing most of the work to bring the PhysicianTypeId field over.
It seems a bit hacky, and contrary to the general design of linq-to-SharePoint. I agree, but it also has the advantage of actually working, and not actually being all that hard (once you get the rhythm of it down and learn what exactly needs to be copied over to move the properties from one file to another).

How do I persist checked rows on a grid that is populated from Cache instead of Session?

I get a large list of data to populate into a jqGrid on my clientside.
List<MyData> lotsOfRecords = getData();
Which I then store in cache, since a lot of people will be using it:
Cache["SharedData"].Add(lotsOfRecords);
This grid allows users to check records for processing. I want to persist which records are checked as a user sorts, filters, pages, etc.
My first thought was to add a property bool Selected { get; set; } to the MyData object, and toggle it whenever someone checks a field. Obviously, that won't work since this is a shared cache. I don't want Joe User checking things that Bill User didn't want checked.
Next idea was to store a Dictionary<int, bool> in session, that maps the id of a record to the checked status. This wasn't bad, but since there is no easy way to combine objects in .NET, I don't see a clean way to send that down to my grid without a clunky anonymous object:
return lotsOfRecords.Select(record => {
record.Id,
record.Name,
...
myDictionary[record.Id] // true/false for checked
};
That would be a solution, but I'm hoping there is a cleaner design pattern considering I have a lot of fields in my object and use it in a similar way across a few pages. Any suggestions are welcome. Thanks in advance!
Btw, my current environment is ASP.NET MVC 3 with jQuery/UI and jqGrid.
You may be caching the list, but the selections will need to be user-specific. I would suggest building a list of the selected indices each time the page is posted back, and store the list session.
Here's a function that we're using to remember selections as the user pages through results:
/// <summary>
/// Iterates through items in the grid and updates the selected vendor
/// list with any selections or deselections on the current page
/// </summary>
private void UpdateSelectedItems()
{
var selectedVendors = new List<int>();
foreach (GridItem Item in grdVendors.Items)
{
if (Item is GridDataItem)
{
int VendorID = (int)((GridDataItem)Item).GetDataKeyValue("SupplierID");
if (Item.Selected)
{
if (!selectedVendors.Contains(VendorID))
selectedVendors.Add(VendorID);
continue;
}
selectedVendors.Remove(VendorID);
}
}
}
I'm not sure why you think "combining" objects is tough. You can simply do this:
public class SelectableDataObject
{
public SelectableDataObject(MyDataObject obj)
{
this.DataObject = obj;
}
public MyDataObject DataObject { get; private set; }
public bool Selected {get;set;}
}
Then you can just do this:
return lotsOfRecords.Select(record => {
return new SelectableDataObject(record){Selected = myDictionary.ContainsKey(record.Id)}
};
Alternatively, in your view model you can have the list of objects and the dictionary as two separate properties, and when you iterate the list of objects in your view to populate your grid, you can check the dictionary if the Id exists and check/uncheck based on that. This way is a bit more clunky, but should work.
Either way, I think your dictionary idea is perfectly fine, the only thing I'd do different is just store the Id's of the ones that are selected, that way you only store a subset for each user.

How to get the order of dynamically created DropDownLists

I've created some drop down lists using JavaScript, ASP.NET.
A user can add as many drop down lists as he wants by clicking a "+" button and removing them by clicking a "-" button.
If it's hard to understand what I mean pls see " How to implement a list of dropboxes in C# ".
And now I'd like to implement the code behind and want to define the order of the drop down lists, but I don't know which one is my first drop down list, etc.
We assume that all <asp:DropDownList> contain the following for list elements: method1, method2, method3 and method4. If a user selects an element, a method in the codebehind is implemented.
Example:
dropboxlist1: select list item method2,
dropboxlist2: select list item method1,
dropboxlist3: select list item method3,
string txt= "";
if (dropboxlistID.Text == "method1"){
txt = method1Imp();
} else if (dropboxlistID.Text == "method2") {
txt = method2Imp();
} else if (dropboxlistID.Text == "method3") {
txt = method3Imp();
} else {
}
But at this moment I don't have any idea which drop down lists came first and which method should be performed on my string first.
Try enqueueing each method into a queue as a delegate, then draining (invoking each delegate) the queue once you're ready from a single thread. This will ensure that the order of execution matches the order of user choices.
Sorry I didn't initally include code. Here's a basic example to get you started:
Queue<Func<string>> actions = new Queue<Func<string>>();
if(dropboxListID.Text =="m1")
{
actions.Enqueue(method1Imp);
}
if(dropboxListID.Text = "m2")
{
action.Enqueue(method2Imp);
}
...
Sometime Later when you're ready to process these
...
string txt = "";
while(actions.Count >0)
{
var method = actions.Dequeue();
txt = method();
}
Here's a blog post that delves further into the concept of a work/task queue:
http://yacsharpblog.blogspot.com/2008/09/simple-task-queue.html
IMO your drop down lists will be contained in a parent.
Let us say (acc to your link) your parent is DropDownPlaceholder.
<div id="DropDownPlaceholder">
Use linq to get all children of it. Cast them as drop down lists and then your can loop on them to find your matter.
To get the order of dropdownlists:
First set the IDs/ClientIDs of hard-coded dropdownlists in aspx page and
count them (say 2 dropdownlists are present)
While creating dropdownlists dynamically, append a count integer at
the end of their IDs/ClientIDs like ddl3, ddl4 (start the count from 3)
Then in your code, you can find the dropdownlist of selected element:
if (ddl.ClientID.EndsWith("1")){
// 1st ddl
} else if (ddl.ClientID.EndsWith("2")) {
// 2nd ddl
} else if (ddl.ClientID.EndsWith("3")) {
// 3rd ddl
}
...

How do I obtain selected rows in a DataGridView from different pages

I have a windows forms DataGridView, where I have data and a checkbox for each row.
I will select check box for a particular row and all the selected rows will be populated in another page.
if (grdEmp.Rows.Count > 0)
{
var selectedEmpIDs= from DataGridViewRow coll in grdEmp.Rows
where Convert.ToBoolean(coll.Cells["Select"].Value) == true
select coll;
if (selectedEmpIDs.Count() > 0)
{
foreach (DataGridViewRow row in selectedEmpIDs)
{
selectedEmp+= row.Cells["EmpId"].Value + ",";
}
}
}
This works good only for one page.
When I navigate to another page, and click the selected rows, the previous one goes off.
How do I resolve it.
Thanks
cmrhema
Note :Sorry for the confusion, When I meant it works good for a page, I meant paging.
I think I need to add more inputs,
There are 10 pages in the gridview.
I select the first record from each page of the gridview, one after another by clicking next page( Page next button).
But only the record that was selected the last is getting displayed and others and ignored off.
What could be the prblm
You can use a List or Dictionary or any other collection type globally, using Program.cs or using a static class. And store the selected rows into the list before you leave the page.
Rather than using a comma delimited string string for your list of ids you can instead use a List.
Your code will then become something like this:
if (grdEmp.Rows.Count > 0)
{
var selectedEmpIDs= from DataGridViewRow coll in grdEmp.Rows
where Convert.ToBoolean(coll.Cells["Select"].Value) == true s
select coll;
if (selectedEmpIDs.Count() > 0)
{
foreach (DataGridViewRow row in selectedEmpIDs)
{
if (!listOfIds.Contains((int)row.Cells["EmpId"].Value))
{
listOfIds.Add(((int)row.Cells["EmpId"].Value));
}
}
}
}
You will need methods to remove items from this list so adding event handlers for the checkbox selected event will probably work better.
The List object itself can simple live as a class level object of the form that containst your DataGridView.
This gets a little bit more complicated if you are managing your paging across forms, but the same principles of maintaining a list of selected ids applies.

Categories