0

Let's say I have a class from a 3rd-party, which is a data-model. It has perhaps 100 properties (some with public setters and getters, others with public getters but private setters). Let's call this class ContosoEmployeeModel

I want to facade this class with an interface (INavigationItem, which has Name and DBID properties) to allow it to be used in my application (it's a PowerShell provider, but that's not important right now). However, it also needs to be usable as a ContosoEmployeeModel.

My initial implementation looked like this:

public class ContosoEmployeeModel
{
    // Note this class is not under my control. I'm supplied
    // an instance of it that I have to work with.

    public DateTime EmployeeDateOfBirth { get; set; }
    // and 99 other properties.
}

public class FacadedEmployeeModel : ContosoEmployeeModel, INavigationItem
{
    private ContosoEmployeeModel model;
    public FacadedEmployeeModel(ContosoEmployeeModel model)
    {
        this.model = model;
    }

    // INavigationItem properties
    string INavigationItem.Name { get; set;}

    int INavigationItem.DBID { get; set;}

    // ContosoEmployeeModel properties
    public DateTime EmployeeDateOfBirth
    {
        get { return this.model.EmployeeDateOfBirth; }
        set { this.model.EmployeeDateOfBirth = value; }
    }
    // And now write 99 more properties that look like this :-(
}

However, it's clear that this will involve writing a huge amount of boilerplate code to expose all the properties , and I'd rather avoid this if I can. I can T4 code-generate this code in a partial class, and will do if there aren't any better ideas, but I though I'd ask here to see if anyone had any better ideas using some super wizzy bit of C# magic

Please note - the API I use to obtain the ContosoEmployeeModel can only return a ContosoEmployeeModel - I can't extend it to return a FacededEmployeeModel, so wrapping the model is the only solution I can think of - I'm happy to be corrected though :)

RB.
  • 36,301
  • 12
  • 91
  • 131
  • 1
    How about using [AutoMapper](http://automapper.org/) ? – Matthew Watson Jul 02 '13 at 09:48
  • http://msdn.microsoft.com/en-us/library/fb3dyx26.aspx – spender Jul 02 '13 at 09:52
  • I'm confused - you inherit your facade class from `ContosoEmployeeModel` *AND* you have a `ContosoEmployeeModel` embedded in your facade via composition? Why? – Treb Jul 02 '13 at 09:55
  • @MatthewWatson Oooh - I'd never heard of that. This looks very promising - I'll try it out and post back – RB. Jul 02 '13 at 09:55
  • Maybe you could add an `object` property to `INavigationItem` which, when not `null`, is used instead of the `INavigationItem` when someone navigates to it? – dtb Jul 02 '13 at 09:56
  • Maybe you don't need to create the interface at all? If your class only has data and no operations what will you achieve by introducing an interface? – Ilya Chernomordik Jul 02 '13 at 09:56
  • @Treb The API I use for saving/retrieving data takes a ContosoEmployeeModel. However, the application that manipulates the model (PowerShell in this case) takes an INavigationItem. Hope that's clear? – RB. Jul 02 '13 at 09:58
  • @Spender I don't control the ContosoEmployeeModel so can't refactor it. – RB. Jul 02 '13 at 09:58
  • @MatthewWatson It looks like Auto-mapping won't help me here - my issue is that I'm too lazy to completely write the object in the first place. However, it's a good thing to know about, so cheers :) – RB. Jul 02 '13 at 10:03
  • @RB. Ah well, it was worth a shot. :) – Matthew Watson Jul 02 '13 at 10:05
  • 1
    @RB. : In which case, Resharper's delegating member feature is the droid you're looking for. http://www.jetbrains.com/resharper/webhelp/Code_Generation__Delegating_Members.html ... perfect for generation of facades/proxies. – spender Jul 02 '13 at 10:06

2 Answers2

1

The other approach may be suitable for you is to use AutoMapper to map base class to your facade here is sample code:

class Program
    {
        static void Main(string[] args)
        {
            var model = new Model { Count = 123, Date = DateTime.Now, Name = "Some name" };

            Mapper.CreateMap<Model, FacadeForModel>();
            var mappedObject = AutoMapper.Mapper.Map<FacadeForModel>(model);

            Console.WriteLine(mappedObject);

            Console.ReadLine();
        }

        class Model
        {
            public string Name { get; set; }

            public DateTime Date { get; set; }

            public int Count { get; set; }
        }

        interface INavigationItem
        {
            int Id { get; set; }

            string OtherProp { get; set; }
        }

        class FacadeForModel : Model, INavigationItem
        {
            public int Id { get; set; }

            public string OtherProp { get; set; }
        }
    }
Alexandr Mihalciuc
  • 2,537
  • 15
  • 12
  • Unfortunately, this won't work for me - I need my FacadedEmployeeModel to have the same values for all 100 properties as the ContosoEmployeeModel. – RB. Jul 02 '13 at 10:11
  • What are you using the FacadedEmployeeModel for? – Alexandr Mihalciuc Jul 02 '13 at 10:21
  • From an API I retrieve a Contoso model. However, I need to manipulate this as an INavigationItem. Therefore, the facade will turn a Contoso model into an INavigationItem. – RB. Jul 02 '13 at 10:24
  • Using the AutoMapper you will get the Facade populated with all the data from ContosoEmployeeModel, so you can use it as INavigationItem and ContosoEmployeeModel the same time, without a need to redeclare all the model properties – Alexandr Mihalciuc Jul 02 '13 at 11:04
  • You're right! I can't believe I missed that point! @MatthewWatson suggested using Automapper, but I obviously dismissed it too quickly. Frustratingly, there is a bug in the Contoso model (A property can be initialised to null, but can't be set to null) that I'll need to investigate, but AutoMapper is absolutely the right answer. – RB. Jul 02 '13 at 12:31
  • Brilliantly, AutoMapper allows you to work around issues like this using ForMember to apply property-specific mapping rules. It exactly answers my question - thanks :) – RB. Jul 02 '13 at 12:44
1

Resharper allows the creation of "delegating members", which copies the interface of a contained object onto the containing object and tunnels the method calls/property access through to the contained object.

http://www.jetbrains.com/resharper/webhelp/Code_Generation__Delegating_Members.html

Once you've done that, you can then extract an interface on your proxy class.

spender
  • 117,338
  • 33
  • 229
  • 351
  • I've just downloaded the free trial, and that will certainly work for me :) Weirdly, it doesn't create them with the "new" keyword, which is a shame, so I'd have to hand-edit it anyway (but obviously much quicker!!). I think I'll do it using T4 anyway, as it will be easier to generate new versions if the Contoso models change, but thanks very much for the suggestion. – RB. Jul 02 '13 at 10:19