Dynamic Data Custom Field Template updating a list - c#

I have a website using dynamic data and linq to sql. This website runs 3 'subsites' and has a list of categories with a many to many relationship.
I have 3 tables and hence 3 objects in my dbml; Website, Categories, and CategoriesToWebsites
What I am trying to do is create a field template such that on my Categories/Edit.aspx page I can edit a category and specify which website the category belongs in.
The field template is CategoriesToWebsites_Edit.ascx, which is basically a checkbox list bound to the list of websites.
Code below:
public partial class CategoriesToWebsitesEdit : FieldTemplateUserControl
{
protected override void OnLoad(EventArgs e)
{
var dataSource = (LinqDataSource)this.FindDataSourceControl();
dataSource.Inserting += OnInserting;
dataSource.Updating += OnUpdating;
}
private void OnUpdating(object sender, LinqDataSourceUpdateEventArgs e)
{
var newCategory = (Category)e.NewObject;
var oldCategory = (Category)e.OriginalObject;
foreach(var listItem in WebsiteList.Items.Cast<ListItem>())
{
//check if website category already exists
var categoryToWebsite = oldCategory.CategoriesToWebsites.FirstOrDefault(x => x.WebsiteId == Convert.ToInt32(listItem.Value));
//website category exists
if (categoryToWebsite != null)
{
// check if selected for removal, remove
if (!listItem.Selected)
{
newCategory.CategoriesToWebsites.Remove(categoryToWebsite);
}
}
//we want to insert
if (listItem.Selected)
{
//website category does not exist, add
if (categoryToWebsite == null)
{
//add selected website if not already exists
newCategory.CategoriesToWebsites.Add(new CategoriesToWebsite
{
WebsiteId = Convert.ToInt32(listItem.Value)
});
}
}
}
}
private void OnInserting(object sender, LinqDataSourceInsertEventArgs e)
{
var category = (Category)e.NewObject;
foreach(var listItem in WebsiteList.Items.Cast<ListItem>())
{
if(!listItem.Selected)
continue;
category.CategoriesToWebsites.Add(new CategoriesToWebsite
{
WebsiteId = Convert.ToInt32(listItem.Value)
});
}
}
protected override void OnDataBinding(EventArgs e)
{
var websiteRepository = new WebsiteRepository();
var websites = websiteRepository.GetAll();
var websiteCategories = (IEnumerable<CategoriesToWebsite>)FieldValue;
foreach(var website in websites)
{
var currentWebsite = website;
var listItem = new ListItem(website.Name, website.Id.ToString())
{
Selected = websiteCategories == null ? false : websiteCategories.Any(w => w.WebsiteId == currentWebsite.Id)
};
WebsiteList.Items.Add(listItem);
}
}
}
When I go to Categories/Insert.aspx to create a new category, it runs through the OnInserting code fine and saves it to db just fine, everything seems to be working here.
On Categories/Edit.aspx it goes through the code just as I expect, but does not seem to save anything.
What am I missing? - I'm not too familiar with Dynamic Data Field Templates so any guidance will be much appreciated

Apparently I was going about this slightly wrong. I was simply updating the object in the linq data source, which wasn't being saved. So instead I go straight to the repository:
private void OnUpdating(object sender, LinqDataSourceUpdateEventArgs e)
{
var newCategory = (Category)e.NewObject;
var oldCategory = (Category)e.OriginalObject;
var repository = new Repository<CategoriesToWebsite>();
var ctw = repository.GetAll().Where(x => x.CategoryId == newCategory.Id);
foreach (var listItem in WebsiteList.Items.Cast<ListItem>())
{
var current = ctw.FirstOrDefault(x => x.WebsiteId == Convert.ToInt32(listItem.Value));
//current categoriesToWebsite exists
if (current != null)
{
//if not selected, remove
if (!listItem.Selected)
repository.Delete(current);
}
//does not exist
else
{
//if selected, add
if (listItem.Selected)
repository.Save(new CategoriesToWebsite()
{
CategoryId = newCategory.Id,
WebsiteId = Convert.ToInt32(listItem.Value)
}
);
}
}
UnitOfWork.Current.SubmitChanges();
}
I'm not sure if this is the proper way to do this since the field template here is doing some updating directly to the db. But it works.

Related

Finding names in List

I am new to programming and to this forum. I have searched the forum for answers but havent found anything that works for me. I have created a adressbook in WinForms with a search function. The search finds the the specified contact, but when I click on the contact the information it loads belongs to the first contact in the List. This happens for every contact in the list exept for the first contact.
var TempVar = People.Where(a => a.Namn.ToLower().Contains(txtSearchbar.Text.ToLower()) ||
a.PostOrt.ToLower().Contains(txtSearchbar.Text.ToLower())).ToList();
foreach (var item in TempVar)
{
ListBoxOne.Items.Add(TempVar);
}
Tell me if you need more information. Thanks for all the help!!
EDIT1:
Dont know if this is the right way to answer, but the comment section didnt let me post a long answer.
For starters. Thanks for being so helpful. I tried your code for adding to the list, but got the errorcode: Cannot convert System.Collecion.Generic.List to System.Windows.Forms.ListBox.ObjectCollection. Searched the web for a solution but came up short. Shall I post my entire code? Also tried your search code it works great. But my main problem is still there. When I click on the search results the name in the listbox shows the contact information of the first contact in the entire contactlist. Its as if the index of the contacts is still in the listbox but when contacts in the Listbox are sorted out by the search function the first contacts index is given to the contact the search found. Shall I post my entire code to make everything clearer.? Thanks for all the help!
EDIT2: Here is the code. I got a class with person propertys. Name, adress, postnummber, city, telephone annd email that links to the list.
I know some of the comments are in swedish and it is a hassle. Ask me if there is anything you donte understand.
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
List<Person> People = new List<Person>();//Skapar en lista med alla variabler i Person
private void Form1_Load(object sender, EventArgs e)//Reads file on start up.
{
//string path = Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData);
if (Directory.Exists("C:\\visualFolder\\Adressbok"))
{
Directory.CreateDirectory("C:\\visualFolder\\Adressbok");
}
if (!File.Exists("C:\\visualFolder\\Adressbok\\settings.xml"))
{
XmlTextWriter XW = new XmlTextWriter("C:\\visualFolder\\Adressbok\\settings.xml", Encoding.UTF8);
XW.WriteStartElement("People");
XW.WriteEndElement();
XW.Close();
}
XmlDocument xDoc = new XmlDocument();
xDoc.Load("C:\\visualFolder\\Adressbok\\settings.xml");
foreach (XmlNode XNode in xDoc.SelectNodes("People/Person"))
{
Person p = new Person();
p.Namn = XNode.SelectSingleNode("Namn").InnerText;
p.GatuAdress = XNode.SelectSingleNode("Adress").InnerText;
p.PostNummer = XNode.SelectSingleNode("Postnummer").InnerText;
p.PostOrt = XNode.SelectSingleNode("Postort").InnerText;
p.Telefon = XNode.SelectSingleNode("Telefon").InnerText;
p.Email = XNode.SelectSingleNode("Email").InnerText;
People.Add(p);
ListBoxOne.Items.Add(p.Namn);
}
}//----
private void cmdRegistrera_Click(object sender, EventArgs e)//Adds contact
{
Person LäggTillPerson = new Person();
LäggTillPerson.Namn = txtNamn.Text;
LäggTillPerson.GatuAdress = txtAdressText.Text;
LäggTillPerson.PostNummer = txtPostNummer.Text;
LäggTillPerson.PostOrt = txtPostOrt.Text;
LäggTillPerson.Telefon = txtTelefonnummer.Text;
LäggTillPerson.Email = txtEpost.Text;
People.Add(LäggTillPerson);
ListBoxOne.Items.Add(LäggTillPerson.Namn);
txtNamn.Clear();
txtAdressText.Clear();
txtPostNummer.Clear();
txtPostOrt.Clear();
txtTelefonnummer.Clear();
txtEpost.Clear();
}
private void cmdTaBort_Click(object sender, EventArgs e)//Deletes contact.
{
if (ListBoxOne.SelectedItem != null)
{
People.RemoveAt(ListBoxOne.SelectedIndex);
ListBoxOne.Items.Remove(ListBoxOne.SelectedItems[0]);
}
txtNamn.Clear();
txtAdressText.Clear();
txtPostNummer.Clear();
txtPostOrt.Clear();
txtTelefonnummer.Clear();
txtEpost.Clear();
}
private void ListboxOne_SelectedIndexChanged(object sender, EventArgs e)//
{
if (ListBoxOne.SelectedItem != null)
{
txtNamn.Text = People[ListBoxOne.SelectedIndex].Namn;
txtAdressText.Text = People[ListBoxOne.SelectedIndex].GatuAdress;
txtPostNummer.Text = People[ListBoxOne.SelectedIndex].PostNummer;
txtPostOrt.Text = People[ListBoxOne.SelectedIndex].PostOrt;
txtTelefonnummer.Text = People[ListBoxOne.SelectedIndex].Telefon;
txtEpost.Text = People[ListBoxOne.SelectedIndex].Email;
}
}
private void cmdSpara_Click(object sender, EventArgs e)//Saves changes.
{
if (ListBoxOne.SelectedItem != null)
{
People[ListBoxOne.SelectedIndex].Namn = txtNamn.Text;
People[ListBoxOne.SelectedIndex].GatuAdress = txtAdressText.Text;
People[ListBoxOne.SelectedIndex].PostNummer = txtPostNummer.Text;
People[ListBoxOne.SelectedIndex].PostOrt = txtPostOrt.Text;
People[ListBoxOne.SelectedIndex].Telefon = txtTelefonnummer.Text;
People[ListBoxOne.SelectedIndex].Email = txtEpost.Text;
ListBoxOne.Items.Clear();
foreach (var item in People)
{
ListBoxOne.Items.Add(item.Namn);
}
}
MessageBox.Show("Ändringarna är sparade");
}
private void Form1_FormClosing(object sender, FormClosingEventArgs e)//Saves to file on when closin the application.
{
XmlDocument xDoc = new XmlDocument();
xDoc.Load("C:\\visualFolder\\Adressbok\\settings.xml");
XmlNode xNode = xDoc.SelectSingleNode("People");
xNode.RemoveAll();
foreach (Person p in People)
{
XmlNode xTop = xDoc.CreateElement("Person");
XmlNode Xnamn = xDoc.CreateElement("Namn");
XmlNode Xadress = xDoc.CreateElement("Adress");
XmlNode XPostnummer = xDoc.CreateElement("Postnummer");
XmlNode XpostOrt= xDoc.CreateElement("Postort");
XmlNode Xtelefon = xDoc.CreateElement("Telefon");
XmlNode XeMAil = xDoc.CreateElement("Email");
Xnamn.InnerText = p.Namn;
Xadress.InnerText = p.GatuAdress;
XPostnummer.InnerText = p.PostNummer;
XpostOrt.InnerText = p.PostOrt;
Xtelefon.InnerText = p.Telefon;
XeMAil.InnerText = p.Email;
xTop.AppendChild(Xnamn);
xTop.AppendChild(Xadress);
xTop.AppendChild(XPostnummer);
xTop.AppendChild(XpostOrt);
xTop.AppendChild(Xtelefon);
xTop.AppendChild(XeMAil);
xDoc.DocumentElement.AppendChild(xTop);
}
xDoc.Save("C:\\visualFolder\\Adressbok\\settings.xml");
}
private void cmdSök_Click(object sender, EventArgs e)//Search function. This is where the problem is.
{
if (txtSearchbar.Text != "")
{
var term = txtSearchbar.Text;
var results = People.Where(a => ContainsCI(a.Namn, term)
|| ContainsCI(a.PostOrt, term));
foreach (var item in results)
{
ListBoxOne.Items.Add(item);
}
ListBoxOne.Items.Clear();
foreach (var item in results)
{
ListBoxOne.Items.Add(item.Namn);
}
txtSearchbar.Clear();
}
else
{
ListBoxOne.Items.Clear();
foreach (var item in People)
{
ListBoxOne.Items.Add(item.Namn);
}
}
}
private void cmdClearSearch_Click(object sender, EventArgs e)//Clears the searchebar and ListBox and loads the contacts again.
{
ListBoxOne.Items.Clear();
txtSearchbar.Clear();
foreach (var item in People)
{
ListBoxOne.Items.Add(item.Namn);
}
txtNamn.Clear();
txtAdressText.Clear();
txtPostNummer.Clear();
txtPostOrt.Clear();
txtTelefonnummer.Clear();
txtEpost.Clear();
}
public bool ContainsCI(string input, string term)//Search function. courtesy of Panagiotis Kanavos
{
if (String.IsNullOrWhiteSpace(input))
{
return false;
}
//Returns true even if `terms` is empty, just like String.Contains
return input.IndexOf(term, StringComparison.CurrentCultureIgnoreCase) != -1;
}
}
}
Your code has a typo. Instead of adding individual items you keep adding the list itself to the listbox. You should add the individual items:
foreach (var item in TempVar)
{
ListBoxOne.Items.Add(item);
}
A better option though would be to use AddRange to add the entire list at once:
ListBoxOne.Items.AddRange(TempVar);
You can improve the rest of the code as well. Instead of Contains which is case sensitive, you can use IndexOf with a case-insensitive StringComparison parameter. To make the code a bit cleaner, I created a separate ContainsCI method:
public bool ContainsCI(string input, string term)
{
if (String.IsNullOrWhitespace(input))
{
return false;
}
//Returns true even if `terms` is empty, just like String.Contains
return input.IndexOf(term,StringComparison.CurrentCultureIgnoreCase)!= -1);
}
...
var term=txtSearchbar.Text;
var results= People.Where(a => ContainsCI(a.Namn, term)
|| ContainsCI(a.PostOrt,term));
ListBoxOne.Items.AddRange(results);
By using IndexOf instead of Contains and ToLower() the code avoids generating temporary strings which end up wasting memory for no gain.
Note that both String.Contains and ContainsCI return true if the term is empty. This makes it easy to return all items if the search box is empty.

checkboxes list status with linq

Consider this scenario where you want to retrieve a List or IEnumerable of the values of all the unchecked checkboxes in checkboxlist. this code gives me the value of all checked (now) or (previously). how can I get the values of unchecked (now) and (previously) with linq. thank you
private IEnumerable <string > selectedValues
{
get
{
if (ViewState ["selectedValues"] == null && chapp . SelectedIndex >=-1 )
{
ViewState ["selectedValues"]= chapp .Items.Cast <ListItem >()
.Where (li=>li.Selected )
.Select (li=>li.Value )
.ToList ();
}else
ViewState ["selectedValues"]= Enumerable .Empty <string >();
return (IEnumerable <string >)ViewState ["selectedValues"];
}
set {ViewState ["selectedValues"] = value ;}
}
protected void chapp_SelectedIndexChanged(Object sender, EventArgs e)
{
CheckBoxList c = (CheckBoxList)sender;
var oldselection = this.selectedValues;
var newSelection = c.Items.Cast<ListItem>()
.Where(li => li.Selected )
.Select(li => li.Value);
var unchangedItems = newSelection.Except(oldselection );
}
Based on you comment:
Let's say that you have aspx page which load all the applications you have (from active directory, DB or whatever), after that assigning all the applications which the current user have (also getting those applications from what ever) ... so in your Page_Load you will have something like below:
protected void Page_Load(object sender, EventArgs e)
{
if (!IsPostBack)
{
//Load all applications
var apps = loadAllApplications();
//Load user applications
var myApps = loadUserApplications();
//Bind to checkboxlist, assuming my checkboxlist ID "chksApps"
chksApps.DataSource = apps.Select(x => new ListItem
{
Value = x.Id.ToString(),
Text = x.Name,
Selected = myApps.Any(a => x.Id == a)
});
chksApps.DataBind();
}
}
//lets say I have a Application class like that
public class Application
{
public int Id { get; set; }
public string Name { get; set; }
}
private List<int> loadUserApplications()
{
// if user already have "Paint", "Chrome" assigned
var myApps = new List<int>() { 2, 4 };
return myApps;
}
private List<Application> loadAllApplications()
{
//for testing I will create a dummy list of applications
var applications = new List<Application>() {
new Application { Id = 1, Name = "Visual Studio" },
new Application { Id = 2, Name = "Paint" },
new Application { Id = 3, Name = "Notepad" },
new Application { Id = 4, Name = "Chrome" }
};
return applications;
}
so till now the user will be able to check, uncheck what ever he want. so your checkboxlist AutoPostBack = False
so when he finish editing, assuming he will submit that to the server so he will have a submit button (lets say it's ID is "btnSave"
protected void btnSave_Click(object sender, EventArgs e)
{
//Load user applications
var myApps = loadUserApplications();
//get selected Application
var selectedApps = chksApps.Items.Cast<ListItem>()
.Where(x => x.Selected)
.Select(x => int.Parse(x.Value)).ToList();
// send request to owner to add those below apps for user "newSelectedApps"
var newSelectedApps = selectedApps.Except(myApps).ToList();
// send request to owner to remove those below apps "newUnSelectedApps"
var newUnSelectedApps = myApps.Except(selectedApps).ToList();
// those below are the unchanged apps "unChangedApps"
var unChangedApps = myApps.Intersect(selectedApps).ToList();
}
this is better than using ViewState for performance

C# entity framework storing values returns null exception

Here is my code :
protected void btnShow_Click(object sender, EventArgs e)
{
foreach (Control control in Panel1.Controls)
{
var textBox = control as TextBox;
if (textBox != null)
{
if (string.IsNullOrEmpty(textBox.Text))
{
textBox.Style["visibility"] = "hidden";
}
// textBox.Enabled = false;
var id = from t in textBox.Text
where t != null
select textBox.ID;
var text = from t in textBox.Text
where t != null
select t;
foreach (var x in id)
{
Model.crossword insert = new Model.crossword();
insert.TextBoxID = x;
daoCrossword.Insert(insert);
}
foreach (var a in text)
{
Model.crossword insert = new Model.crossword();
insert.TextBoxValue = a.ToString();
daoCrossword.Insert(insert);
}
daoCrossword.Save();
}
}
}
daoCrossword is a class file which have CRUD codes in it , i am using EF to do this , i am new to this , it gives me an error : System.NullReferenceException: Object reference not set to an instance of an object.
CRUD class file (partial) :
public void Insert(Model.crossword exe)
{
context.crosswords.AddObject(exe);
}
public void Save()
{
context.SaveChanges();
}
I have no idea what you think you're doing with these two statements, but it's probably not what you think it is.
var id = from t in textBox.Text
where t != null
select textBox.ID;
var text = from t in textBox.Text
where t != null
select t;
Then your foreach statements really make no sense because you will only ever have one item in the collections. It seems like you're trying to only save the data if there is something in the textbox, for which you should probably just do a simple if statement.
Next, you create new crossword objects in each foreach, but you only assign an id in one, and text in the other.. this is also probably not what you want. More than likely, you just want to do this:
if (!string.IsNullOrEmpty(textbox.Text))
{
Model.crossword insert = new Model.crossword();
insert.TextBoxID = textbox.ID;
insert.TextBoxValue = textbox.Text;
daoCrossword.Insert(insert);
}

cannot be attached to this EntityContainer because it is already attached to another EntityContainer. Lightswitch Update a collection automatically

partial void updateRecords_Execute()
{
// Write your code here.
using (var tempWorkspace = new DataWorkspace())
{
Saving NewSavings = tempWorkspace.ApplicationData.Savings.AddNew();
var koo = from a in FromMainCompanies
select a;
foreach (var i in koo)
{
if (i.Member != null)
{
NewSavings.CaptureDate = DateTime.Now;
NewSavings.Amount = i.Member.Savings.Select(a => a.Amount).FirstOrDefault();
NewSavings.FinancialYear = tempWorkspace.ApplicationData.FinancialYears.FirstOrDefault();
NewSavings.Member = i.Member;
NewSavings.NewSavingsAmount = i.Member.Savings.Select(a => a.NewSavingsAmount).FirstOrDefault();
try
{
tempWorkspace.ApplicationData.SaveChanges();
}
catch (Exception e)
{
this.ShowMessageBox(e.Message);
}
}
}
}
}
Am trying to update records automatically in Lightswitch and I am getting the following error:
Entity 'Member : 6' cannot be attached to this EntityContainer because it is already attached to another EntityContainer.
I have the following tables, Member(s), Reconcilliation(s), FromMainCompany, ToMainCompany & Saving(s). Member is related to Savings i.e. A member has savings. And Member is also related FromMainCompany (FromMainCompany is a collection of Members and their total salary account deduction - NOT that important)
I have a button in my Reconcilliation screen called UpdateRecords (which has the code above on its Execute() method) that I want when clicked to add Member monthly savings automatically to all the Members Saving table but now I get this error - Entity 'Member : 6' cannot be attached to this EntityContainer because it is already attached to another EntityContainer.
-Thanks.
partial void updateRecords_Execute()
{
// Write your code here.
using (var tempWorkspace = new DataWorkspace())
{
var mymembers = tempWorkspace.ApplicationData.Members;
//var myscreen = this.Reconcilliations1.SelectedItem.FromMainCompanies.Where(a => a.Member != null).Select(b => b.Member);
//Member myMember = new Member();
foreach (Member item in mymembers)
{
Saving NewSavings = tempWorkspace.ApplicationData.Savings.AddNew();
////var koo = from a in FromMainCompanies
//// select a.Member;
NewSavings.CaptureDate = DateTime.Now;
NewSavings.Amount = item.Savings.Select(a=>a.Amount).LastOrDefault();
NewSavings.FinancialYear = tempWorkspace.ApplicationData.FinancialYears.FirstOrDefault();
NewSavings.Member = item;
NewSavings.NewSavingsAmount = item.Savings.Select(a=>a.NewSavingsAmount).LastOrDefault();
}
try
{
tempWorkspace.ApplicationData.SaveChanges();
}
catch (Exception e)
{
this.ShowMessageBox(e.Message);
}
}
}
Figured it out, the AddNew() was supposed to be in the foreach loop. - cheers

Select from dropdown then treeview - not holding position

I am having problems with postbacks.
I have a dropdownlist that i add items to at runtime.
When i select a item in the dropbox a treeview is filled with items that have the same pID value as the object selected in the dropdownlist.
But when i select a node in the treeview everything goes back to "normal" state. The dropbox will go to selectindex -1 and the treeview disappear.
I have theese controllers in a master page if that matters.
This is my code.
public partial class Nemanet : System.Web.UI.MasterPage
{
nemanetDataContext dc = new nemanetDataContext();
Guid userGuid = (Guid)Membership.GetUser().ProviderUserKey;
bool reloadPeriod = true;
protected void Page_Load(object sender, EventArgs e)
{
if (!IsPostBack)
{
if (reloadPeriod == true)
{
reloadPeriod = false;
var query = from n in dc.Nemanet_Navigations
where n.UserId == userGuid && n.Nav_pID == null
orderby n.Nav_Name ascending
select n;
foreach (var period in query)
{
ListItem period_listitem = new ListItem(period.Nav_Name, period.Nav_ID.ToString());
dropdown_navigation.Items.Add(period_listitem);
}
}
}
}
protected void dropdown_navigation_SelectedIndexChanged(object sender, EventArgs e)
{
treeview_Navigation.Nodes.Clear();
var query = from n in dc.Nemanet_Navigations
where n.UserId == userGuid
orderby n.Nav_Name ascending
select n;
foreach (var course in query)
{
if (course.Nav_pID.ToString() == dropdown_navigation.SelectedValue)
{
TreeNode course_node = new TreeNode(course.Nav_Name, course.Nav_ID.ToString());
course_node.NavigateUrl = "Default.aspx?navigateID=" + course.Nav_ID;
treeview_Navigation.Nodes.Add(course_node);
foreach (var chapter in query)
{
if (chapter.Nav_pID.ToString() == course_node.Value)
{
TreeNode chapter_node = new TreeNode(chapter.Nav_Name, chapter.Nav_ID.ToString());
chapter_node.NavigateUrl = "Default.aspx?navigateID=" + chapter.Nav_ID;
course_node.ChildNodes.Add(chapter_node);
foreach (var subject in query)
{
if (subject.Nav_pID.ToString() == chapter_node.Value)
{
TreeNode subject_node = new TreeNode(subject.Nav_Name, subject.Nav_ID.ToString());
subject_node.NavigateUrl = "editor.aspx?navigateID=" + subject.Nav_ID;
chapter_node.ChildNodes.Add(subject_node);
}
}
}
}
}
}
}
}
Any dynamically added elements will be gone after any postback, so you have add all of them again after every postback (your page is rebuild from the ground using the frontpage and page load).
To avoid reloading all the data from the database, store it in the Session.
Session["items"] = query;
if(IsPostBack) foreach(var period in (Collection)Session["items"]) dropdown_navigation.Items.Add(new ListItem(period.Nav_Name, period.Nav_ID.ToString()));

Categories