Declaring static list of strings and accessing by names [duplicate] - asp.net

I am using Window Application for my project. There is situation where i need to define string enum and using it in my project.
i.e.
Dim PersonalInfo As String = "Personal Info"
Dim Contanct As String = "Personal Contanct"
Public Enum Test
PersonalInfo
Contanct
End Enum
Now i want value of that variable PersonalInfo and Contract as "Personal Info" and "Personal Contanct".
How can i get this value using ENUM? or anyother way to do it.
Thanks in advance...

For non-integer values, Const in a Structure (or Class) can be used instead:
Structure Test
Const PersonalInfo = "Personal Info"
Const Contanct = "Personal Contanct"
End Structure
or in a Module for direct access without the Test. part:
Module Test
Public Const PersonalInfo = "Personal Info"
Public Const Contanct = "Personal Contanct"
End Module
In some cases, the variable name can be used as a value:
Enum Test
Personal_Info
Personal_Contanct
End Enum
Dim PersonalInfo As String = Test.Personal_Info.ToString.Replace("_"c, " "c)
' or in Visual Studio 2015 and newer:
Dim Contanct As String = NameOf(Test.Personal_Contanct).Replace("_"c, " "c)

You could just create a new type
''' <completionlist cref="Test"/>
Class Test
Private Key As String
Public Shared ReadOnly Contact As Test = New Test("Personal Contanct")
Public Shared ReadOnly PersonalInfo As Test = New Test("Personal Info")
Private Sub New(key as String)
Me.Key = key
End Sub
Public Overrides Function ToString() As String
Return Me.Key
End Function
End Class
and when you use it, it kinda looks like an enum:
Sub Main
DoSomething(Test.Contact)
DoSomething(Test.PersonalInfo)
End Sub
Sub DoSomething(test As Test)
Console.WriteLine(test.ToString())
End Sub
output:
Personal Contanct
Personal Info

How about using Tagging. Something like:
Public Enum MyEnum
<StringValue("Personal Contact")>Contact
<StringValue("My PersonalInfo")>PersonalInfo
End Enum
You would have to write the StringValue attribute as:
Public Class StringValueAttribute
Inherits Attribute
Public Property Value As String
Public Sub New(ByVal val As String)
Value = val
End Sub
End Class
To get it out:
Public Function GetEnumByStringValueAttribute(value As String, enumType As Type) As Object
For Each val As [Enum] In [Enum].GetValues(enumType)
Dim fi As FieldInfo = enumType.GetField(val.ToString())
Dim attributes As StringValueAttribute() = DirectCast(fi.GetCustomAttributes(GetType(StringValueAttribute), False), StringValueAttribute())
Dim attr As StringValueAttribute = attributes(0)
If attr.Value = value Then
Return val
End If
Next
Throw New ArgumentException("The value '" & value & "' is not supported.")
End Function
Public Function GetEnumByStringValueAttribute(Of YourEnumType)(value As String) As YourEnumType
Return CType(GetEnumByStringValueAttribute(value, GetType(YourEnumType)), YourEnumType)
End Function
And then a call to get the Enum (using string attribute):
Dim mEnum as MyEnum = GetEnumByStringValueAttribute(Of MyEnum)("Personal Contact")
To get the "Attribute" value out (removed handling 'Nothing' for clarity):
Public Function GetEnumValue(Of YourEnumType)(p As YourEnumType) As String
Return DirectCast(Attribute.GetCustomAttribute(ForValue(p), GetType(StringValueAttribute)), StringValueAttribute).Value
End Function
Private Function ForValue(Of YourEnumType)(p As YourEnumType) As MemberInfo
Return GetType(YourEnumType).GetField([Enum].GetName(GetType(YourEnumType), p))
End Function
And the call to get the string attribute (using Enum):
Dim strValue as String = GetEnumValue(Of MyEnum)(MyEnum.Contact)

How can i get this value using ENUM? or anyother way to do it.
There are three common ways of mapping enum values to strings:
Use a Dictionary(Of YourEnumType, String)
Decorate the enum values with attributes (e.g. DescriptionAttribute) and fetch them with reflection
Use a Switch statement
The first of these options is probably the simplest, in my view.

I know this is an old post put I found a nice solution that worth sharing:
''' <summary>
''' Gives acces to strings paths that are used often in the application
''' </summary>
Public NotInheritable Class Link
Public Const lrAutoSpeed As String = "scVirtualMaster<.lrAutoSpeed>"
Public Const eSimpleStatus As String = "scMachineControl<.eSimpleStatus>"
Public Const xLivebitHMI As String = "scMachineControl<.xLivebitHMI>"
Public Const xChangeCycleActive As String = "scMachineControl<.xChangeCycleActive>"
End Class
Usage:
'Can be anywhere in you applicaiton:
Link.xChangeCycleActive
This prevents unwanted extra coding, it's easy to maintain and I think this minimizes extra processor overhead.
Also visual studio shows the string attributes right after you type "Link"
just like if it is a regular Enum

If all you want to do is display the enums in a list or combo, you can use tagging such as
Private Enum MyEnum
Select_an_option___
__ACCOUNTS__
Invoices0
Review_Invoice
__MEETINGS__
Scheduled_Meetings0
Open_Meeting
Cancelled_Meetings0
Current_Meetings0
End Enum
Then pull the MyEnum into a string and use Replace (or Regex) to replace the tags: "___" with "...", "__" with "**", "_" with " ", and remove trailing numbers. Then repack it up into an array and dump it into a combobox which will look like:
Select an option...
**ACCOUNTS**
Invoices
Review Invoice
**MEETINGS**
Scheduled Meetings
Open Meeting
Cancelled Meetings
Current Meetings
(You can use the numbers to, say, disable a text field for inputting an invoice number or meeting room. In the example, Review Invoice and Open Meeting might be expecting additional input so a text box might be enabled for those selections.)
When you parse the selected combo item, the enumeration will work as expected but you only really need to add a single line of code - the text replacement - to get the combo to look as you wish.
(The explanation is about 10 times as involved as the actual solution!)

This technique from Microsoft - "How to: Determine the String Associated with an Enumeration Value (Visual Basic)" - will be useful in some situations (it didn't help with mine unfortunately though :( ). Microsoft's example:
VB:
Public Enum flavorEnum
salty
sweet
sour
bitter
End Enum
Private Sub TestMethod()
MsgBox("The strings in the flavorEnum are:")
Dim i As String
For Each i In [Enum].GetNames(GetType(flavorEnum))
MsgBox(i)
Next
End Sub

Related

Adding one to many data to Lucene Index

I am new to Lucene. I am trying to create an index of records. So far, I have just been adding one-to-one data to my index, and this seems fine. But, I have scenarios where I need to add one to many relationship data, and I am not sure what is the best way to handle this. I have tried adding each of the individual relationship, concacting feilds into CSV values, adding the field multiple times, but nothing seems to work. Here is my code for when the data is indexed:
Private Shared Sub _addToLuceneIndex(ByVal sampleData As LuceneSearchData, ByVal writer As IndexWriter)
Dim searchQuery = New TermQuery(New Term("Id", sampleData.Id.ToString()))
writer.DeleteDocuments(searchQuery)
Dim doc = New Document()
doc.Add(New Field("Id", sampleData.Id.ToString(), Field.Store.YES, Field.Index.NOT_ANALYZED))
doc.Add(New Field("Name", sampleData.Name, Field.Store.YES, Field.Index.ANALYZED))
doc.Add(New Field("Description", sampleData.Description, Field.Store.YES, Field.Index.ANALYZED))
For Each item As Integer In sampleData.HomeStates
doc.Add(New Field("Home_State", item, Field.Store.YES, Field.Index.ANALYZED))
Next
'i have also tried
'doc.Add(New Field("HomeStates ", String.Join(",", sampleData.HomeStates ), Field.Store.YES, Field.Index.ANALYZED))
writer.AddDocument(doc)
End Sub
While the above code seems to index the one-to-one data, it does not for the HomeStates list of integers. Do I have to add the same document for every item in the list of integers? If so, how do you best manage this? I have several "one-to-many" relationships I need to include. I can see this getting unwieldy quickly. Or, is there a better way?
EDIT
I updated to add the one to may value as a field like this:
doc.Add(New Field("Geo_Locations", String.Join(" ", sampleData.Geo_Location), Field.Store.YES, Field.Index.ANALYZED))
This is how I search for that field:
Private Shared Function _search(ByVal searchQuery As String, ByVal Optional searchField As String = "") As IEnumerable(Of LuceneSearchData)
If String.IsNullOrEmpty(searchQuery.Replace("*", "").Replace("?", "")) Then Return New List(Of LuceneSearchData)()
Using searcher = New IndexSearcher(_directory, False)
Dim hits_limit = 1000
Dim analyzer = New StandardAnalyzer(Version.LUCENE_30)
If Not String.IsNullOrEmpty(searchField) Then
Dim parser = New QueryParser(Version.LUCENE_30, searchField, analyzer)
Dim query = parseQuery(searchQuery, parser)
Dim hits = searcher.Search(query, hits_limit).ScoreDocs
Dim results = _mapLuceneToDataList(hits, searcher)
analyzer.Close()
searcher.Dispose()
Return results
End If
End Using
End Function
Private Shared Function _mapLuceneToDataList(ByVal hits As IEnumerable(Of ScoreDoc), ByVal searcher As IndexSearcher) As IEnumerable(Of LuceneSearchData)
Dim listOfResults As List(Of LuceneSearchData)
Try
listOfResults = hits.[Select](Function(hit) _mapLuceneDocumentToData(searcher.Doc(hit.Doc))).ToList()
Catch ex As Exception
Return Nothing
End Try
Return listOfResults
End Function
Private Shared Function _mapLuceneDocumentToData(ByVal doc As Document) As LuceneSearchData
Return New LuceneSearchData With {
.Id = Convert.ToInt32(doc.[Get]("Id")),
.Mechanism_Name = doc.[Get]("Name"),
.Mechanism_Purpose = doc.[Get]("Description"),
.Geo_Location = doc.[Get]("Home_State")
}
End Function
I then call the search bu:
LuceneData = LuceneSearch.Search("5451", "HomeStates")
Creating a new document for each item will result in duplicates when searching for other fields.
You should design your documents & fields according to your needs.
If you don't need to search for these fields, store them as you wish.
If you need to search these multiple valued fields then create a field that can be searched for each item.
For integer lists you can merge them with spaces.
For string lists you should replace white spaces with some custom character while indexing and searching to avoid matching substrings (eg "stack overflow" => stack_overflow)
One to many using a term
As you have figured out, integers can be concatenated using a variety of separators including spaces, resulting in something like:
"234 12342 345 5476456 234"
The StandardAnalyzer will tokenise the above string into separate tokens just as it would any other string of words. So you can search them and it will give you the expected results.
One to many using a phrase
If you need to tokenise phrases, such as:
"Control Support Engineer|Technical Support Engineer|Maintenance Technician"
into individual phrases, not down to words (terms), then you'll need to inherit from a few classes. A tokenizer that inherits from CharTokenizer and an analyzer that inherits from Analyzer.
Tokenizer
public sealed class PipeTokenizer: CharTokenizer
{
public PipeTokenizer(LuceneVersion matchVersion, TextReader input) :
base(matchVersion, input)
{
}
public PipeTokenizer(LuceneVersion matchVersion, AttributeFactory factory, TextReader input) :
base(matchVersion, factory, input)
{
}
protected override bool IsTokenChar(int c)
{
return !((char)c).Equals('|'); //<-- the only line that matters
}
}
Pretty simple stuff so far.
Analyzer
public class PipeAnalyzer: Analyzer
{
protected override TokenStreamComponents CreateComponents(string fieldName, TextReader reader)
{
var tokenizer = new PipeTokenizer(LuceneVersion.LUCENE_48, reader);
var lowerCaseFilter = new LowerCaseFilter(LuceneVersion.LUCENE_48, tokenizer);
return new TokenStreamComponents(tokenizer, lowerCaseFilter);
}
}
This is based on what the KeywordAnalyzer does but with a twist. The KeywordAnalyzer is very simple. It takes the entire string, or phrase, and indexes it as is. No further breaking down into smaller tokens. The analyzer above uses the PipeTokenizer to break phrases down by the | separator, and converts to lower case.
Querying with any of the following phrases will return the doc:
"control support engineer"
"technical support engineer"
"maintenance technician"

Can i use a single session variable to store all the field values

I have 9 pages with 10 fields in each page. Can i use a single session variable to store all the field(textbox,drop downlist,radiobuttons) values of 9 pages? If so could you give me small example inorder to proceed. Im kind of stuck.
Could you? Yes. Should you? Most likely not - though I can't say for sure without understanding what problem you are intending to solve.
Update with one sample solution
OK, I'm going to assume you want to store the values from the controls and not the controls themselves. If so, the easiest solution is stuff them in using some meaningful token to separate them. Like:
Session("MyControlValueList") = "name='txt1',value='hello'|name='txt2', value'world'"
To retrieve you would split them into a string array:
myArray = Session("MyControlValueList").Split("|")
And then iterate through to find the control/value you want.
So strictly speaking that's an answer. I still question whether it is the best answer for your particular scenario. Unfortunately I can't judge that until you provide more information.
Create a custom class with all the fields you want to save, then populate an instance of that and save that instance as a session variable.
I have something similar, but not identical - I'm saving various shipping address fields for an order, and I'm allowing the admins to update the order, either the shipping information or the order line items. Since that information is kept on separate tables, I store the shipping information in a session variable, and then compare it to what's on the form when they hit the "Update" button. If nothing has changed, I skip the update routine on the SQL Server database.
The easiest way for me to do this was to create a "OrderInfo" class. I saved the shipping information to this class, then saved that class to a session variable. Here's the code showing the class -
Public Class OrderInfo
Private v_shipname As String
Private v_add1 As String
Private v_add2 As String
Private v_city As String
Private v_state As String
Private v_zipcd As String
Private v_dateneeded As Date
Private v_billingmeth As Integer
Public Property ShipName() As String
Get
Return v_shipname
End Get
Set(value As String)
v_shipname = value
End Set
End Property
Public Property Add1() As String
Get
Return v_add1
End Get
Set(value As String)
v_add1 = value
End Set
End Property
Public Property Add2() As String
Get
Return v_add2
End Get
Set(value As String)
v_add2 = value
End Set
End Property
Public Property City() As String
Get
Return v_city
End Get
Set(value As String)
v_city = value
End Set
End Property
Public Property State() As String
Get
Return v_state
End Get
Set(value As String)
v_state = value
End Set
End Property
Public Property ZipCd() As String
Get
Return v_zipcd
End Get
Set(value As String)
v_zipcd = value
End Set
End Property
Public Property DateNeeded() As Date
Get
Return v_dateneeded
End Get
Set(value As Date)
v_dateneeded = value
End Set
End Property
Public Property BillingMeth() As Integer
Get
Return v_billingmeth
End Get
Set(value As Integer)
v_billingmeth = value
End Set
End Property
End Class
Here's the code for when I tested the concept to see if I could store a custom class in a session variable. This routine gets the order record, populates the fields in an instance of the custom class, and on the web form, as well. I save that instance to a session variable, then I initialize another new instance of that custom class, load the session variable to it. I then display the field values from the "retrieved" custom class, and what showed on the label matched what it should be -
Protected Sub LoadOrderInfo(ByVal ordID As Integer)
Dim connSQL As New SqlConnection
connSQL.ConnectionString = ConfigurationManager.ConnectionStrings("sqlConnectionString").ToString
Dim strProcName As String = "uspGetOrderInfoGeneral"
Dim cmd As New SqlCommand(strProcName, connSQL)
cmd.CommandType = CommandType.StoredProcedure
cmd.Parameters.AddWithValue("OrderID", ordID)
If connSQL.State <> ConnectionState.Open Then
cmd.Connection.Open()
End If
Dim drOrderInfo As SqlDataReader
drOrderInfo = cmd.ExecuteReader
If drOrderInfo.Read Then
Dim orgOrder As New OrderInfo
orgOrder.ShipName = drOrderInfo("shipName")
orgOrder.Add1 = drOrderInfo("ShipAdd1")
orgOrder.Add2 = drOrderInfo("ShipAdd2")
orgOrder.City = drOrderInfo("ShipCity")
orgOrder.State = drOrderInfo("ShipState")
orgOrder.ZipCd = drOrderInfo("ShipZip")
orgOrder.DateNeeded = drOrderInfo("DateNeeded")
orgOrder.BillingMeth = drOrderInfo("BillingMethodID")
If Session.Item("orgOrder") Is Nothing Then
Session.Add("orgOrder", orgOrder)
Else
Session.Item("orgOrder") = orgOrder
End If
' I could just as easily populate the form from the class instance here
txtShipName.Text = drOrderInfo("shipName")
txtAdd1.Text = drOrderInfo("ShipAdd1")
txtAdd2.Text = drOrderInfo("ShipAdd2")
txtCity.Text = drOrderInfo("ShipCity")
txtState.Text = drOrderInfo("ShipState")
txtZipCd.Text = drOrderInfo("ShipZip")
selDate.Value = drOrderInfo("DateNeeded")
ddlBillMeth.SelectedValue = drOrderInfo("BillingMethodID")
End If
cmd.Connection.Close()
Dim retOrder As New OrderInfo
retOrder = Session.Item("orgOrder")
lblWelcomeMssg.Text = retOrder.ShipName & ", " & retOrder.Add1 & ", " & retOrder.City & ", " & retOrder.DateNeeded.ToShortDateString & ", " & retOrder.BillingMeth.ToString
End Sub
This might not be practical or desirable, given the number of fields you are trying to hold onto that way, but I'm not here to judge, so this is one possibility. I've worked with other projects where you create a table, and save that table as a session variable, so whatever structure you put into an object is retained if you save that object as a session variable.

Creating a Web Service Request

I would like to feed data to a proxy which creates a soap XML request formatted as below:
<dat:MusicCollection>
<!--Zero or more repetitions:-->
<dat:Song>
<dat:songUserkey>TakemeHome</dat:songUserkey>
</dat:Song>
</dat:MusicCollection>
I have written the file to call the service and provide the details as below:
dim ucizi1 as SongRequest 'this is the request class in the proxy
dim Songs as Song = New Song
Songs.songUserKey = "TakeMeHome"
dim ucz
ucz = Songs.SongUserKey
ucizi1.SongCollection.Add(ucz)
The MusicCollection class is as follows:
<System.Diagnostics.DebuggerStepThroughAttribute(), _
System.CodeDom.Compiler.GeneratedCodeAttribute("System.Runtime.Serialization", "4.0.0.0"), _
System.Runtime.Serialization.CollectionDataContractAttribute(Name:="ProductCollection", [Namespace]:="http://ucizi.Musicservice/DataContracts", ItemName:="Song")> _
Public Class SongCollection
Inherits System.Collections.Generic.List(Of ucizi.Music.DataContracts.Song)
End Class
The song class is as follows:
<System.Diagnostics.DebuggerStepThroughAttribute(), _
System.CodeDom.Compiler.GeneratedCodeAttribute("System.Runtime.Serialization", "4.0.0.0"), _
System.Runtime.Serialization.DataContractAttribute(Name:="Product", [Namespace]:="http://Ucizi.Music/DataContracts")> _
Partial Public Class Product
Inherits Object
Implements System.Runtime.Serialization.IExtensibleDataObject
Private extensionDataField As System.Runtime.Serialization.ExtensionDataObject
Private SongUserkeyField As String
Public Property ExtensionData() As System.Runtime.Serialization.ExtensionDataObject Implements System.Runtime.Serialization.IExtensibleDataObject.ExtensionData
Get
Return Me.extensionDataField
End Get
Set
Me.extensionDataField = value
End Set
End Property
<System.Runtime.Serialization.DataMemberAttribute(IsRequired:=true)> _
Public Property SongUserkey() As String
Get
Return Me.SongUserkeyField
End Get
Set
Me.SongUserkeyField = value
End Set
End Property
End Class
However, when I run this code, I get an error: unable to cast object of type 'system.string' to type 'ucizi.music.DataContracts.Song'.
I cant see where this error is coming from, can some1 please help me and advise how I can correct this.
In lines
Songs.songUserKey = "TakeMeHome"
dim ucz
ucz = Songs.SongUserKey
ucizi.SongCollection.Add(ucz)
you set ucz to be SongUserKey - which is string.
Then, you add it to collection SongCollection
Public Class SongCollection
Inherits System.Collections.Generic.List(Of ucizi.Music.DataContracts.Song)
End Class
Which is expected Song
added the following code after the Songs.songUserKey = "TakeMeHome"
dim ucizi2 as songCollection
ucizi2.Add(song)
ucizi1.songcollection = ucizi2
This sorted the problem smoothly. You guys opened my eyes.

Do I Need a Class if I only need 1 Property (at the moment)?

Update: I didn't make it clear but I meant this to be a question about where/how I would use a function to return a list of strings when I'm trying to just work with classes.
I have a class called Account.
I have data access class called AccountDAO.
I have various functions that return lists of objects like GetAllAccounts, GetAccountByID etc.
I want to populate a drop down list with just the account names and nothing else. It's proving rather slow when using lists of objects and databinding them to the dropdownlist.
I feel like I should be using a simple "Select Account_Name From blah" type statement and returning a list of strings but I don't know how to work this into my class and data access class.
How should I handle this predicament?
You can use a list of string,s and bind the list of strings to a dropdownlist no problem... the DDL can support that, just leave out DataTextField and DataValueField props, and it will display the account name as is, which that name would be accessible through the ListItem's Text and Value property.
I like to use objects to be consistent with the rest of the app (which other areas might need a class), and if for some reason you want to add AccountKey later, if you use an object, all you need to do is add a property. Otherwise, if using strings, you'd have to switch up the binding later to point to the object.
HTH.
There is nothing wrong by making a function that only returns a list of strings. YOu could however wonder if it's not better to restrict the number of records you want to put in the list and use some kind of paging.
Assuming that you're using a List<>, you can try something like this:
IEnumerable<string> nameList = accountList.Select(t => t.AccountName);
Or if you need a List:
List<string> nameList = accountList.Select(t => t.AccountName).ToList();
Go with your feelings. Use a datareader to select the list and then load them into an arraylist which can then be bound to the dropdown. Alternately, use something like this method I use to provide both a DisplayMember and a ValueMember which uses a class (with both values) as members of the arraylist. This should give you the general idea. (Note: I normally include this code in a data access class (MyBase) where StartReader, _datRdr, ReadNext and_ReaderValid are a members. But the general idea is intact.)
Public Sub LoadDataSource(ByRef PlantDataSource As PlantSource, Optional ByVal Filter As String = "", Optional ByVal IncludeBlankItem As Boolean = False)
PlantDataSource = New PlantSource
If IncludeBlankItem Then
PlantDataSource.Add(0, "")
End If
If Filter = String.Empty Then
Call StartReader(" Order by PlantName")
Else
Call StartReader(String.Concat(" Where ", Filter, " Order by PlantName"))
End If
If _DatRdr.HasRows Then
While MyBase._ReaderValid
PlantDataSource.Add(PlantId, PlantName)
ReadNext()
End While
End If
Call CloseReader()
End Sub
Private Class PlantListing
Private _PlantList As New ArrayList
Public Sub Add(ByVal PlantId As Integer, ByVal PlantName As String)
_PlantList.Add(New PlantDataItem(PlantId, PlantName))
End Sub
Public ReadOnly Property List() As ArrayList
Get
Return _PlantList
End Get
End Property
End Class
Private Class PlantDataItem
Private _PlantId As Integer
Private _PlantName As String
Public Sub New(ByVal pPlantId As Integer, ByVal pPlantName As String)
Me._PlantId = pPlantId
Me._PlantName = pPlantName
End Sub
Public ReadOnly Property PlantName() As String
Get
Return _PlantName
End Get
End Property
Public ReadOnly Property PlantId() As Integer
Get
Return _PlantId
End Get
End Property
Public ReadOnly Property DisplayValue() As String
Get
Return CStr(Me._PlantId).Trim & " - " & _PlantName.Trim
End Get
End Property
Public Overrides Function ToString() As String
Return CStr(Me._PlantId).Trim & " - " & _PlantName.Trim
End Function
End Class

ASP.NET MVC, LINQ and UpdateModel issues

Ok - I have two tables in the database, employees and companies. Here are the basic schemas for each
Table Companies
CompanyID - GUID - PK
CompanyName - NVarChar(100)
Other Descriptive Fields....
Table Employees
EmployeeID - GUID - PK
CompanyID - GUID - FK
Employee Descriptive Fields...
So now I have a one to many relationship as each company can have multiple employees. I have also created a dataRepository for my employee class with the following functions:
Public Function GetEmployee(ByVal company As String, ByVal id As Guid) As DB.EmpWithComp.employee
Dim emp As DB.EmpWithComp.employee = (From e In db.employees Where e.employeeID = id And e.company.companyName= company Select e).Single
Return emp
End Function
Public Sub save()
db.SubmitChanges()
End Sub
Everything so far is working great. The issue comes when I need to edit an employee. Here are my controller functions
<AcceptVerbs(HttpVerbs.Post)> _
Function Edit(ByVal id As Guid, ByVal company As String, ByVal formValues As FormCollection) As ActionResult
Dim e As New DB.EmpWithComp.employee
e = db.GetEmployee(company, id)
Try
UpdateModel(e)
db.save()
Return RedirectToAction("Index")
Catch ex As Exception
Return View(e)
End Try
End Function
Function Edit(ByVal id As Guid, ByVal company As String) As ActionResult
Dim e As New DB.EmpWithComp.employee
e = db.GetEmployee(company, id)
If Not e Is Nothing Then
Return View(e)
Else
Return View("~/Views/Admin/Employees/NotFound.aspx")
End If
End Function
The employee is found, the edit form is populated and the post function is firing as it should. However, my updateModel fails with no real explanation. I traced the code going through the updateModel and all attributes are being assigned the correct values. However, when the updateModel gets to the following section in the LinqToSql class, the value for company is Nothing and this is what is causing the failure
<Association(Name:="company_employee", Storage:="_company", ThisKey:="companyID", IsForeignKey:=true)> _
Public Property company() As company
Get
Return Me._company.Entity
End Get
Set
Dim previousValue As company = Me._company.Entity
**If ((Object.Equals(previousValue, value) = false) _
OrElse** (Me._company.HasLoadedOrAssignedValue = false)) Then
Me.SendPropertyChanging
If ((previousValue Is Nothing) _
= false) Then
Me._company.Entity = Nothing
previousValue.employees.Remove(Me)
End If
Me._company.Entity = value
If ((value Is Nothing) _
= false) Then
value.employees.Add(Me)
Me._companyID = value.companyID
Else
**Me._companyID = CType(Nothing, System.Guid)**
End If
Me.SendPropertyChanged("company")
End If
End Set
End Property
What am I missing with the FK relationship? The previous value for company is set to the correct value going into the UpdateModel, but the current value is not set. If I manually set each property (e.firstname = request("firstname"), etc) and call my save method on the dataRepository or remove the relationship and use UpdateModel, everything works properly. I would rather use the UpdateModel as it makes the code cleaner, etc. Sorry for the lengthy post, but this is driving me nuts. Any ideas?
BTW, I am not trying to change the FK, just simply update the employee name.
how does your view look like?
like this?
if yes, UpdateModel would try to write the string in request("Company") to e.Company which isn`t a string but an object.
what you really want to change is the Employee.CompanyID right? you can do this by using a Hidden Input with the new ID or if you want to find the CompanyID by the CompanyName you could write your own ModelBinder.
EDIT
oh you dont event want to update the company, sry my fault.
in that case you need to tell UpdateModel to ignore the company which is present in your POST
Function Edit(ByVal id As Guid, ByVal company As String...
you can do this by setting company as excluded property like this
Dim exclude() As String = {"company"}
UpdateModel(e, String.Empty, Nothing, exclude)
or what i prefer, you could start using prefixs for your fields like this
<%=Html.Textbox("Employee.Firstname", ViewData.Model.Firstname)>%
and call UpdateModel like this
UpdateModel(e, "Employee")
hth

Resources