11

I have a 3rd party application that provides an object with many "attributes", which are simply pairs of (string) keys and values. The value types can be either strings, DateTime, Int32 or Int64.

I need to create my own class to represent this object, in a convenient way. I'm creating a WCF service that provides this object to clients, so I need it to be very easy and clean.

The keys of the attributes will be presented as an Enum for the clients (to hide the information of the specific key strings of the 3rd party application). However, I'm not sure how to represent the values. Here are some of the options:

Option 1: Have different collection per attribute values, seems ugly but will be very easy for clients to use

public class MyObject
{
    public Dictionary<MyTextAttributeKeysEnum, string> TextAttributes { get; set; }
    public Dictionary<MyDateAttributeKeysEnum, DateTime> DateAttributes { get; set; }
    public Dictionary<MyNumAttributeKeysEnum, long> NumericAttributes { get; set; }

    public string Name { get; set; }
    public string Id{ get; set; }

Option 2: Convert all of the attributes to strings

public class MyObject
{
    public Dictionary<MyAttributeKeysEnum, string> MyAttributes { get; set; }

    public string Name { get; set; }
    public string Id{ get; set; }

Option 3: Keep them as objects, let the clients bother with casting and converting

public class MyObject
{
    public Dictionary<MyAttributeKeysEnum, object> MyAttributes { get; set; }

    public string Name { get; set; }
    public string Id{ get; set; }
yellowblood
  • 1,604
  • 2
  • 17
  • 32
  • 4
    The *dynamic* keyword and the ExpandoObject class were made to solve this. Find pre-4.0 solutions by googling "c# property bag". – Hans Passant Dec 20 '11 at 13:28
  • I've read some articles about property bags and ExpandoObject, but it doesn't seem like the best way to go, since they're gonna need to have so much knowledge about the types I'm giving them, unlike when I use Enums – yellowblood Dec 20 '11 at 13:57
  • @yellowblood: to me it seems that you need that knowledge by the way, so you should create kind of "mapping" between 3rd party object and yuor. – Tigran Dec 20 '11 at 14:20
  • I need that knowledge in my service, but I want to hide it from the clients of my service. As of now I don't see any alternative to option 1 which I don't like at all. – yellowblood Dec 20 '11 at 14:39
  • Go with dynamic like @Hans Passant tipped, or DynamicObject if you want to put some type constraints on it. There are a few downsides, but there's much much less code, so you can test the little you need a lot more. – Tony Hopkinson Dec 20 '11 at 17:27
  • It seems like the `dynamic` solution gives me easy time developing the service, but leaving lots of hard work for the clients of my service - they will need to parse it manually, identify the types of objects, and make assumption about my service that will lead to a lot of "coupling" – yellowblood Dec 21 '11 at 09:57

2 Answers2

5

Using several dictionaries just doesn't look nice :) But might work in some scenarios.

If you are absolutely sure that string is enough for all - go with strings. But if some other code would need to parse it - that's going to be expensive.

If you want a really simple straightforward solution - just go with objects. Even though it would introduce boxing/unboxing for value types (forget it if you don't operate thousands of objects) and you'd lose type information on values this solution might still work just fine.

Also you might consider introducing an intermediate class for a value. Something like

public Dictionary<MyAttributeKeysEnum, PropertyBagValue> MyAttributes { get; set; }

public class PropertyBagValue
{
    public object AsObject { get; set; }
    public string AsString { get; set; }
    public int AsInt { get; set; }
    // ...
}

Internally you could store your value in a variable of the original type (int in an int variable, string in a string variable, etc., i.e. have a separate variable for each type) and then you can avoid type conversion. Also you could wrap your dictionary in another class, add some usefull accessors and make it look nicer. I don't know how does this fit into your infrastructure though.

ttil
  • 176
  • 3
  • Sweet! Your solution is great, as it prevents boxing/unboxing while maintaining a single collection and still type-safe. Though as opposed to option 1, here the client will have to know the type of each attribute ahead. I think your answer is the best so far and should satisfy anyone who sees this page, however option 1 is more fitting for me :) Thanks! – yellowblood Dec 21 '11 at 08:16
0

How about making you DataContract class abstract and provide dictionaries with types you need in derived classes:

[DataContract]
[KnownType(typeof(My3dPartyObjectString))]
[KnownType(typeof(My3dPartyObjectInt64))]
public abstract class My3dPartyObjectBase
{
// some common properties
}

[DataContract]
public class My3dPartyObjectString : My3dPartyObjectBase
{
public Dictionary<3PAttributeKeysEnum, string> MyStringAttributes { get; set; }
}

[DataContract]
public class My3dPartyObjectInt64 : My3dPartyObjectBase
{
public Dictionary<3PAttributeKeysEnum, long> MyStringAttributes { get; set; }
}

Then client will have to analyse real type of returned object and get collection of attributes based on type. That would be close to your 3d option, but client will at least have some type safety at response-object level.

Maxim Zabolotskikh
  • 3,091
  • 20
  • 21
  • Either you didn't understand my question or I didn't understand your answer :) I need to return an object with all of the attributes - strings, dates, and numeric alike, not just one of the types. So if I have a method like `GetObjectById`, it will return an object with all of the attributes, not only attributes of a certain type. – yellowblood Dec 20 '11 at 13:27
  • Got it now, I thought first each object will have only one type of attributes. I would go with option 3. If client gets an attribute from your dictionary he already knows what type to expect and can do the casting himself (like with option one, client first should know the type and then pick a dictionary to get attribute from). Now imagine client wants to iterate through all attributes and do sth. With O.3 it's easy, with O.1 it's 3 cycles (and if new type will be added in future - then more). – Maxim Zabolotskikh Dec 20 '11 at 13:48
  • You're right - when it comes to iterating, the third option is the best one. However, with option 1, the client doesn't need any documentation or knowledge about the types - she will use the enums (there's a different Enum for each type of attributes) and know the types during development, completely type-safe :) To be honest, I was hoping for a new option, maybe using generics ;) – yellowblood Dec 20 '11 at 13:54
  • Unfortunatelly generics are not really an option, for you cannot return a generic object from WCF method. I.e. you cannot declare your WCF contract like "MyList MyMethod();" because it is not supported by WSDL. Only use of generic might be "MyList MyMethod;" but that is obviously not what you want. – Maxim Zabolotskikh Dec 20 '11 at 14:06