WorkItemChangedEvent and AddedRelations field - api

I am trying to capture links that were added to a work item in TFS by catching WorkItemChangedEvent via TFS services. Here is the relevant XML part of the message that comes through:
<AddedRelations><AddedRelation><WorkItemId>8846</WorkItemId></AddedRelation></AddedRelations>
This is declared as a field in WorkItemChangedEvent class that should be deserialized into object upon receiving the event:
public partial class WorkItemChangedEvent
{
private string[] addedRelations;
/// <remarks/>
[XmlArrayItemAttribute("WorkItemId", IsNullable = false)]
public string[] AddedRelations
{
get { return this.addedRelations; }
set { this.addedRelations = value; }
}
}
I cannot figure out why the AddedRelations does not get deserialized properly.
I can only suspect that the object structure does not match the XML schema.

I have changed the structure of my WorkItemChangedEvent class a little bit to match the XML:
public partial class WorkItemChangedEvent
{
private AddedRelation[] addedRelations;
/// <remarks/>
[XmlArrayItemAttribute("AddedRelation", IsNullable = false)]
public AddedRelation[] AddedRelations
{
get { return this.addedRelations; }
set { this.addedRelations = value; }
}
[GeneratedCodeAttribute("xsd", "2.0.50727.42")]
[SerializableAttribute()]
[DebuggerStepThroughAttribute()]
[DesignerCategoryAttribute("code")]
[XmlTypeAttribute(Namespace = "")]
public partial class AddedRelation
{
#region Fields
private string workItemId;
#endregion
/// <remarks/>
public string WorkItemId
{
get { return this.workItemId; }
set { this.workItemId = value; }
}
}
}
I still think that there must be some logic behind the original solution since it was designed by TFS authors (MS)? Anyway I am glad it works now and that I answered my question first ;]

Related

Json Serialize an interface's properties which have non primitive types [duplicate]

With a simple class/interface like this
public interface IThing
{
string Name { get; set; }
}
public class Thing : IThing
{
public int Id { get; set; }
public string Name { get; set; }
}
How can I get the JSON string with only the "Name" property (only the properties of the underlying interface) ?
Actually, when i make that :
var serialized = JsonConvert.SerializeObject((IThing)theObjToSerialize, Formatting.Indented);
Console.WriteLine(serialized);
I get the full object as JSON (Id + Name);
The method I use,
public class InterfaceContractResolver : DefaultContractResolver
{
private readonly Type _InterfaceType;
public InterfaceContractResolver (Type InterfaceType)
{
_InterfaceType = InterfaceType;
}
protected override IList<JsonProperty> CreateProperties(Type type, MemberSerialization memberSerialization)
{
//IList<JsonProperty> properties = base.CreateProperties(type, memberSerialization);
IList<JsonProperty> properties = base.CreateProperties(_InterfaceType, memberSerialization);
return properties;
}
}
// To serialize do this:
var settings = new JsonSerializerSettings() {
ContractResolver = new InterfaceContractResolver (typeof(IThing))
};
string json = JsonConvert.SerializeObject(theObjToSerialize, settings);
Improved version with nested interfaces + support for xsd.exe objects
Yet another variation here. The code came from http://www.tomdupont.net/2015/09/how-to-only-serialize-interface.html with the following improvements over other answers here
Handles hierarchy, so if you have an Interface2[] within an Interface1 then it will get serialized.
I was trying to serialize a WCF proxy object and the resultant JSON came up as {}. Turned out all properties were set to Ignore=true so I had to add a loop to set them all to not being ignored.
public class InterfaceContractResolver : DefaultContractResolver
{
private readonly Type[] _interfaceTypes;
private readonly ConcurrentDictionary<Type, Type> _typeToSerializeMap;
public InterfaceContractResolver(params Type[] interfaceTypes)
{
_interfaceTypes = interfaceTypes;
_typeToSerializeMap = new ConcurrentDictionary<Type, Type>();
}
protected override IList<JsonProperty> CreateProperties(
Type type,
MemberSerialization memberSerialization)
{
var typeToSerialize = _typeToSerializeMap.GetOrAdd(
type,
t => _interfaceTypes.FirstOrDefault(
it => it.IsAssignableFrom(t)) ?? t);
var props = base.CreateProperties(typeToSerialize, memberSerialization);
// mark all props as not ignored
foreach (var prop in props)
{
prop.Ignored = false;
}
return props;
}
}
Inspired by #user3161686, here's a small modification to InterfaceContractResolver:
public class InterfaceContractResolver<TInterface> : DefaultContractResolver where TInterface : class
{
protected override IList<JsonProperty> CreateProperties(Type type, MemberSerialization memberSerialization)
{
IList<JsonProperty> properties = base.CreateProperties(typeof(TInterface), memberSerialization);
return properties;
}
}
You can use conditional serialization. Take a look at this link. Basicly, you need to implement the IContractResolver interface, overload the ShouldSerialize method and pass your resolver to the constructor of the Json Serializer.
An alternative to [JsonIgnore] are the [DataContract] and [DataMember] attributes. If you class is tagged with [DataContract] the serializer will only process properties tagged with the [DataMember] attribute (JsonIgnore is an "opt-out" model while DataContract is "op-in").
[DataContract]
public class Thing : IThing
{
[DataMember]
public int Id { get; set; }
public string Name { get; set; }
}
The limitation of both approaches is that they must be implemented in the class, you cannot add them to the interface definition.
You can add the [JsonIgnore] annotation to ignore an attribute.
I'd like to share what we ended up doing when confronted with this task. Given the OP's interface and class...
public interface IThing
{
string Name { get; set; }
}
public class Thing : IThing
{
public int Id { get; set; }
public string Name { get; set; }
}
...we created a class that is the direct implementation of the interface...
public class DirectThing : IThing
{
public string Name { get; set; }
}
Then simply serialized our Thing instance, deserialized it as a DirectThing, then Serialized it as a DirectThing:
var thing = new Thing();
JsonConvert.SerializeObject(
JsonConvert.DeserializeObject<DirectThing>(JsonConvert.SerializeObject(thing)));
This approach can work with a long interface inheritance chain...you just need to make a direct class (DirectThing in this example) at the level of interest. No need to worry about reflection or attributes.
From a maintenance perspective, the DirectThing class is easy to maintain if you add members to IThing because the compiler will give errors if you haven't also put them in DirectThing. However, if you remove a member X from IThing and put it in Thing instead, then you'll have to remember to remove it from DirectThing or else X would be in the end result.
From a performance perspective there are three (de)serialization operations happening here instead of one, so depending on your situation you might like to evaluate the performance difference of reflector/attribute-based solutions versus this solution. In my case I was just doing this on a small scale, so I wasn't concerned about potential losses of some micro/milliseconds.
Hope that helps someone!
in addition to the answer given by #monrow you can use the default [DataContract] and [DataMember]
have a look at this
http://james.newtonking.com/archive/2009/10/23/efficient-json-with-json-net-reducing-serialized-json-size.aspx
Finally I got when it will not work...
If you want to have inside another complex object it will not be properly serialized.
So I have made version which will extract only data stored in specific assembly and for types which have the same base interface.
So it is made as .Net Core JsonContractResolver.
In addition to data extraction it solves:
a) camelCase conversion before sending data to client
b) uses top most interface from allowed scope (by assembly)
c) fixes order of fields: field from most base class will be listed first and nested object will meet this rule as well.
public class OutputJsonResolver : DefaultContractResolver
{
#region Static Members
private static readonly object syncTargets = new object();
private static readonly Dictionary<Type, IList<JsonProperty>> Targets = new Dictionary<Type, IList<JsonProperty>>();
private static readonly Assembly CommonAssembly = typeof(ICommon).Assembly;
#endregion
#region Override Members
protected override IList<JsonProperty> CreateProperties(Type type, MemberSerialization memberSerialization)
{
if (type.Assembly != OutputJsonResolver.CommonAssembly)
return base.CreateProperties(type, memberSerialization);
IList<JsonProperty> properties;
if (OutputJsonResolver.Targets.TryGetValue(type, out properties) == false)
{
lock (OutputJsonResolver.syncTargets)
{
if (OutputJsonResolver.Targets.ContainsKey(type) == false)
{
properties = this.CreateCustomProperties(type, memberSerialization);
OutputJsonResolver.Targets[type] = properties;
}
}
}
return properties;
}
protected override string ResolvePropertyName(string propertyName)
{
return propertyName.ToCase(Casing.Camel);
}
#endregion
#region Assistants
private IList<JsonProperty> CreateCustomProperties(Type type, MemberSerialization memberSerialization)
{
// Hierarchy
IReadOnlyList<Type> types = this.GetTypes(type);
// Head
Type head = types.OrderByDescending(item => item.GetInterfaces().Length).FirstOrDefault();
// Sources
IList<JsonProperty> sources = base.CreateProperties(head, memberSerialization);
// Targets
IList<JsonProperty> targets = new List<JsonProperty>(sources.Count);
// Repository
IReadOnlyDistribution<Type, JsonProperty> repository = sources.ToDistribution(item => item.DeclaringType);
foreach (Type current in types.Reverse())
{
IReadOnlyPage<JsonProperty> page;
if (repository.TryGetValue(current, out page) == true)
targets.AddRange(page);
}
return targets;
}
private IReadOnlyList<Type> GetTypes(Type type)
{
List<Type> types = new List<Type>();
if (type.IsInterface == true)
types.Add(type);
types.AddRange(type.GetInterfaces());
return types;
}
#endregion
}

How do you handle relationships to other tables with PetaPoco?

As an example, if I have a POCO for Forum and one for ForumPost, how can I elegantly return a list of ForumPosts and specify this right in the model. I'm doing it manually now by adding my db.Query call directly in the List property, but is there a better way?
[TableName("Forum")]
[PrimaryKey("ForumID")]
[ExplicitColumns]
public class Forum : BaseModel
{
[Column]
public int ForumID { get; set; }
[Column]
public string ForumName { get; set; }
[Column]
public string ForumDescription { get; set; }
[ResultColumn]
public List<ForumPost> {
get
{
// return list of ForumPost - what's a good way to handle this?
}
}
}
I wrote an static extension to this, so I can set this in my property like this (using some of my classes). This property would be in your POCO model:
private List<AssessmentAssignment> _AssessmentAssignments;
[ResultColumn]
public List<AssessmentAssignment> AssessmentAssignments
{
get
{
return ServiceExtensions<AssessmentAssignment>.GetRelatedList(this.GetType());
}
set
{
_AssessmentAssignments = value;
}
}
Here is the ServiveExtension that I wrote up to do it, but not sure i like this yet. It works, but meh. Anyone have any better ideas?
public static class ServiceExtensions<T> where T : class, new()
{
/// <summary>
/// Experimental way to retrieve a foreign key related list with PetaPoco
/// </summary>
/// <param name="PrimaryTableType"></param>
/// <returns></returns>
public static List<T> GetRelatedList(Type PrimaryTableType)
{
var primaryTableData = PocoData.ForType(PrimaryTableType);
var relationshipTableData = PocoData.ForType(typeof(T));
using (PetaPoco.Database db = new PetaPoco.Database("ConnStringName"))
{
PetaPoco.Sql sql = new PetaPoco.Sql("SELECT A.* FROM " + relationshipTableData.TableInfo.TableName + " A")
.Append("JOIN " + primaryTableData.TableInfo.TableName + " B ON A." + primaryTableData.TableInfo.PrimaryKey + " = B." + primaryTableData.TableInfo.PrimaryKey);
return db.Fetch<T>(sql);
}
}
}

Fluent nHibernate Automapping not creating Plural table name

I have two tables, Locations and Facilities
They map to two classes,
public Location : Entity
{
//properties
}
public Facility : Entity
{
public virtual Location Location { get; set; }
}
Everything works just dandy, until I change facility to this
public Facility : Location
{
}
Now I get an exception from nHibernate saying
NHibernate.ADOException was unhandled by user code
Message=could not execute query
InnerException: System.Data.SqlClient.SqlException
Message=Invalid object name 'Facility'.
For some reason it is not creating the plural name of the table into the sql string.
Thanks for any help!
EDIT
This is my current TableNameConvention
public class TableNameConvention : IClassConvention
{
public void Apply(FluentNHibernate.Conventions.Instances.IClassInstance instance)
{
instance.Table(Inflector.Net.Inflector.Pluralize(instance.EntityType.Name));
}
}
When Facility inherits from Entity, the Facility does run through this method. When it inherits from Location, it does not
Edit 2
Figured I'd post everything...
public class AutoPersistenceModelGenerator : IAutoPersistenceModelGenerator
{
#region IAutoPersistenceModelGenerator Members
public AutoPersistenceModel Generate()
{
var mappings = new AutoPersistenceModel();
mappings.AddEntityAssembly(typeof(Person).Assembly).Where(GetAutoMappingFilter);
mappings.Conventions.Setup(GetConventions());
mappings.Setup(GetSetup());
mappings.IgnoreBase<Entity>();
mappings.IgnoreBase(typeof(EntityWithTypedId<>));
mappings.UseOverridesFromAssemblyOf<AutoPersistenceModelGenerator>();
return mappings;
}
#endregion
private Action<AutoMappingExpressions> GetSetup()
{
return c =>
{
c.FindIdentity = type => type.Name == "Id";
};
}
private Action<IConventionFinder> GetConventions()
{
return c =>
{
c.Add<BHP.DEC.Data.NHibernateMaps.Conventions.ForeignKeyConvention>();
c.Add<BHP.DEC.Data.NHibernateMaps.Conventions.HasManyConvention>();
c.Add<BHP.DEC.Data.NHibernateMaps.Conventions.HasManyToManyConvention>();
c.Add<BHP.DEC.Data.NHibernateMaps.Conventions.ManyToManyTableNameConvention>();
c.Add<BHP.DEC.Data.NHibernateMaps.Conventions.PrimaryKeyConvention>();
c.Add<BHP.DEC.Data.NHibernateMaps.Conventions.ReferenceConvention>();
c.Add<BHP.DEC.Data.NHibernateMaps.Conventions.TableNameConvention>();
};
}
/// <summary>
/// Provides a filter for only including types which inherit from the IEntityWithTypedId interface.
/// </summary>
private bool GetAutoMappingFilter(Type t)
{
return t.GetInterfaces().Any(x =>
x.IsGenericType &&
x.GetGenericTypeDefinition() == typeof(IEntityWithTypedId<>));
}
}
Have you set a convention?
public class TableNameConvention : IClassConvention
{
public void Apply(FluentNHibernate.Conventions.Instances.IClassInstance instance)
{
string typeName = instance.EntityType.Name;
instance.Table(Inflector.Net.Inflector.Pluralize(typeName));
}
}
This is an old question, but for the sake of others who stumble upon this looking for an answer, you can also create a convention that uses the built-in PluralizationService that comes with EF:
public class TableNameConvention : IClassConvention
{
public void Apply(IClassInstance instance)
{
string typeName = instance.EntityType.Name;
instance.Table(PluralizationService.CreateService(CultureInfo.CurrentCulture).Pluralize(typeName));
}
}

Designing Business Objects to indicate constraints such as Max Length

Is there a standard convention when designing business objects for providing consumers with a way to discover constraints such as a property's maximum length?
It could be used up in the UI layer to, for example, set a Textbox's MaxLength property according to the maximum length limit back in the business object.
Is there a standard design approach for this?
Validation frameworks often contain parts for integrating with UI technologies in communicating the errors. Microsoft Enterprise Library Validation Application Block for instance contains a ValidationProvider extender control for WinForms that binds with the WinForms ErrorProvider control.
Your wish is different though. You want to communicate the constraints before they turn in to errors. Because this is not a standard requirement, I don't believe most validation frameworks have something for this out of the box. However, depending on the chosen framework creating this might be achievable. The Validation Application Block for instance, allows you to analyze the rules that you have registered / configured on a entity. So it is possible to build a control that will do this for you.
[Edit]
What you could also do is validate a form immediately upon startup and after each keystroke. This causes error icons or messages to show up immediately, which allows users to directly see what the constraints are (when you use icons, the user can hover an icon to see the error message). This isn't perhaps as nice as creating your own control, but it much easier to implement.
I have my own validation framework that lets me validate each field with the help of designated ValidationAttribute. It uses Attributes to automate most of the validations.
A sample business object would look like this in my application.
Each business object would inherit from EntityBase abstract class that has a public method called "Validate()". When this method is called on the given instance of the business object it will iterate through all properties of its own having Attributes that are derived from ValidationAttribute can call ValidationAttriubte's IsValid method to validate the value of associated proerty and return true/false with err. msg if any.
User.cs
[TableMapping("Users")]
public class User : EntityBase
{
#region Constructor(s)
public AppUser()
{
BookCollection = new BookCollection();
}
#endregion
#region Properties
#region Default Properties - Direct Field Mapping using DataFieldMappingAttribute
private System.Int32 _UserId;
private System.String _FirstName;
private System.String _LastName;
private System.String _UserName;
private System.Boolean _IsActive;
[DataFieldMapping("UserID")]
[DataObjectFieldAttribute(true, true, false)]
[NotNullOrEmpty(Message = "UserID From Users Table Is Required.")] // VALIDATION ATTRIBUTE
public override int Id
{
get
{
return _UserId;
}
set
{
_UserId = value;
}
}
[DataFieldMapping("UserName")]
[Searchable]
[NotNullOrEmpty(Message = "Username Is Required.")] // VALIDATION ATTRIBUTE
public string UserName
{
get
{
return _UserName;
}
set
{
_UserName = value;
}
}
[DataFieldMapping("FirstName")]
[Searchable]
public string FirstName
{
get
{
return _FirstName;
}
set
{
_FirstName = value;
}
}
[DataFieldMapping("LastName")]
[Searchable]
public string LastName
{
get
{
return _LastName;
}
set
{
_LastName = value;
}
}
[DataFieldMapping("IsActive")]
public bool IsActive
{
get
{
return _IsActive;
}
set
{
_IsActive = value;
}
}
#region One-To-Many Mappings
public BookCollection Books { get; set; }
#endregion
#region Derived Properties
public string FullName { get { return this.FirstName + " " + this.LastName; } }
#endregion
#endregion
public override bool Validate()
{
bool baseValid = base.Validate();
bool localValid = Books.Validate();
return baseValid && localValid;
}
}
BookCollection.cs
/// <summary>
/// The BookCollection class is designed to work with lists of instances of Book.
/// </summary>
public class BookCollection : EntityCollectionBase<Book>
{
/// <summary>
/// Initializes a new instance of the BookCollection class.
/// </summary>
public BookCollection()
{
}
/// <summary>
/// Initializes a new instance of the BookCollection class.
/// </summary>
public BookCollection (IList<Book> initialList)
: base(initialList)
{
}
}
Custom Attributes might serve your need.

Accessing a Custom Configuration Section in .Net

I am trying to access settings in my config file, which is a series of xml elements listed as such:
<databases>
<database name="DatabaseOne" Value="[value]" />
<database name="DatabaseTwo" Value="[value]" />
</databases>
Now I want to access it. I have set up classes like so:
Public Class DatabaseConfigurationHandler
Inherits ConfigurationSection
<ConfigurationProperty("Databases", IsDefaultCollection:=True)> _
Public ReadOnly Property Databases() As DatabaseCollection
Get
Return CType(Me("Databases"), DatabaseCollection)
End Get
End Property
End Class
Public Class DatabaseCollection
Inherits ConfigurationElementCollection
Protected Overloads Overrides Function CreateNewElement() As ConfigurationElement
Return (New Database())
End Function
Protected Overloads Overrides Function GetElementKey(ByVal element As ConfigurationElement) As Object
Return (CType(element, Database).DatabaseName)
End Function
End Class
Public Class Database
Inherits ConfigurationElement
<ConfigurationProperty("name", IsKey:=True, IsRequired:=True)> _
Public Property DatabaseName() As String
Get
Return Me("name").ToString()
End Get
Set(ByVal Value As String)
Me("name") = Value
End Set
End Property
<ConfigurationProperty("value", IsRequired:=True)> _
Public Property DatabaseValue() As String
Get
Return Me("value").ToString()
End Get
Set(ByVal Value As String)
Me("value") = Value
End Set
End Property
End Class
I want to be able get the element by it's name and return the value but I can't see to do that:
Dim config As New DatabaseConfigurationHandler
config = System.Configuration.ConfigurationManager.GetSection("databases/database")
Return config.Databases("DatabaseOne")
Am I missing some code, what am I doing wrong? Any other errors in the above?
Thanks.
Here's a cut and paste from something very similar I did a few days ago.
Config:
<ListConfigurations>
<lists>
<add Name="blah" EndpointConfigurationName="blah" ListName="blah" ConnectionString="blah" TableName="blah" FieldsCsv="blah" DbFieldsCsv="blah"/>
<add Name="blah2" EndpointConfigurationName="blah" ListName="blah" ConnectionString="blah" TableName="blah" FieldsCsv="blah" DbFieldsCsv="blah"/>
</lists>
</ListConfigurations>
Config section C#:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Configuration;
namespace App
{
/// <summary>
/// Individual list configuration
/// </summary>
class ListConfiguration : ConfigurationElement
{
[ConfigurationProperty("Name", IsKey = true, IsRequired = true)]
public string Name
{
get { return (string)this["Name"]; }
}
[ConfigurationProperty("EndpointConfigurationName", IsRequired = true)]
public string EndpointConfigurationName
{
get { return (string)this["EndpointConfigurationName"]; }
}
[ConfigurationProperty("ListName", IsRequired = true)]
public string ListName
{
get { return (string)this["ListName"]; }
}
[ConfigurationProperty("ConnectionString", IsRequired = true)]
public string ConnectionString
{
get { return (string)this["ConnectionString"]; }
}
[ConfigurationProperty("TableName", IsRequired = true)]
public string TableName
{
get { return (string)this["TableName"]; }
}
[ConfigurationProperty("FieldsCsv", IsRequired = true)]
public string FieldsCsv
{
get { return (string)this["FieldsCsv"]; }
}
[ConfigurationProperty("DbFieldsCsv", IsRequired = true)]
public string DbFieldsCsv
{
get { return (string)this["DbFieldsCsv"]; }
}
}
/// <summary>
/// Collection of list configs
/// </summary>
class ListConfigurationCollection : ConfigurationElementCollection
{
protected override ConfigurationElement CreateNewElement()
{
return new ListConfiguration();
}
protected override object GetElementKey(ConfigurationElement element)
{
return ((ListConfiguration)element).Name;
}
}
/// <summary>
/// Config section
/// </summary>
class ListConfigurationSection : ConfigurationSection
{
[ConfigurationProperty("lists")]
public ListConfigurationCollection Lists
{
get { return (ListConfigurationCollection)this["lists"]; }
}
}
}
And the code to pick it up from the main app:
ListConfigurationSection configSection = null;
try
{
configSection = ConfigurationManager.GetSection("ListConfigurations") as ListConfigurationSection;
}
catch (System.Configuration.ConfigurationErrorsException)
{
}
There isn't any good reason to design this kind of stuff by hand anymore. Rather, you should be using the Configuration Section Designer on CodePlex:
http://csd.codeplex.com/
Once installed, you can just add a new item to your project (a configuration section designer) and then add the elements and the constraints. I've found it VERY easy to use, and I will probably never write a piece of code for configuration files again.
You can use this configuration handler instead.. It will work for ALL custom configuration sections
public class XmlConfigurator : IConfigurationSectionHandler
{
public object Create(object parent,
object configContext, XmlNode section)
{
if (section == null)
throw new ArgumentNullException("section",
"Invalid or missing configuration section " +
"provided to XmlConfigurator");
XPathNavigator xNav = section.CreateNavigator();
if (xNav == null)
throw new ApplicationException(
"Unable to create XPath Navigator");
Type sectionType = Type.GetType((string)
(xNav).Evaluate("string(#configType)"));
XmlSerializer xs = new XmlSerializer(sectionType);
return xs.Deserialize(new XmlNodeReader(section));
}
}
Your config file then has to include a reference to the type that the represents the root element
<ConnectionConfig
configType="MyNamespace.ConnectionConfig, MyNamespace.AssmblyName" >
A sample config file might look like this:
<?xml version="1.0" encoding="utf-8" ?>
<ConnectionConfig
configType="MyNamespace.ConnectionConfig, MyNamespace.AssmblyName" >
<ConnCompanys>
<ConnCompany companyName="CompanyNameHere">
<ConnApps>
<ConnApp applicationName="Athena" vendorName="Oracle" >
<ConnSpecs>
<ConnSpec environments="DEV"
serverName="Athena"
port="1521"
catalog="DatabaseName"
logon="MyUserName"
password="%%552355%8234^kNfllceHGp55X5g==" />
<!-- etc...
And you will need to define the classes that each xml element maps to... using the appropriate XmlSerialization attributes ...
[XmlRoot("ConnectionConfig")]
public class ConnectionConfig
{
private ConnCompanys comps;
[XmlArrayItem(ElementName = "ConnCompany")]
public ConnCompanys ConnCompanys
{
get { return comps; }
set { comps = value; }
}
public ConnApp this[string CompanyName, string AppName]
{ get { return ConnCompanys[CompanyName][AppName]; } }
public ConnSpec this[string CompanyName, string AppName, APPENV env]
{
get
{
return ConnCompanys[CompanyName][AppName, env];
}
}
}
public class ConnCompanys : List<ConnCompany>
{
public ConnCompany this[string companyName]
{
get
{
foreach (ConnCompany comp in this)
if (comp.CompanyName == companyName)
return comp;
return null;
}
}
public bool Contains(string companyName)
{
foreach (ConnCompany comp in this)
if (comp.CompanyName == companyName)
return true;
return false;
}
}
public class ConnCompany
{
#region private state fields
private string compNm;
private ConnApps apps;
#endregion private state fields
#region public properties
[XmlAttribute(DataType = "string", AttributeName = "companyName")]
public string CompanyName
{
get { return compNm; }
set { compNm = value; }
}
[XmlArrayItem(ElementName = "ConnApp")]
public ConnApps ConnApps
{
get { return apps; }
set { apps = value; }
}
#endregion public properties
#region indexers
public ConnApp this[string applicationName]
{ get { return ConnApps[applicationName]; } }
public ConnSpec this[string applicationName, APPENV environment]
{
get
{
foreach (ConnSpec con in this[applicationName].ConnSpecs)
if (con.Environment == environment)
return con;
return null;
}
}
#endregion indexers
}
etc...
My VB isn't up to much sorry but here's how you do it in C#.
C#
public class AppState : IConfigurationSectionHandler
{
static AppState()
{
xmlNode myConfigNode = (XmlNode)ConfigurationManager.GetSection("databases");
}
public object Create(object parent, object context, XmlNode configSection) {
return configSection;
}
}
App.Config
<configuration>
<configSections>
<section name="databases" type="MyAssembly.AppState, MyAssembly" />
</configSections>
<databases>
<database name="DatabaseOne" Value="[value]" />
<database name="DatabaseTwo" Value="[value]" />
</databases>
</configuration>
You might be interested in using a ConfigurationElementCollection where T is the type of the child ConfigurationElements. You can find sample code in C# at http://devpinoy.org/blogs/jakelite/archive/2009/01/10/iconfigurationsectionhandler-is-dead-long-live-iconfigurationsectionhandler.aspx
Cheers!
There's a nice simple way of doing this demonstrated here also:
codeproject.com/KB/XML/xml_config_section.aspx