David Cruwys
David Cruwys

Reputation: 6842

How do I provide partial method as an optional Action

I am migrating some code and have nicely formatted Look-up Tables but they have to be converted into archaic structures for use because of backward compatibility.

I have a GetLookupTable() method which goes and gets a list of SelectItemList, all off my models and lookup calls are code generated but I need to manually alter some of the return SelectItem's so I have implemented a Visitor Pattern that the developer can implement if they wish.

The visitor is done as a Partial Method and if developer wants to implement they can, but I'm getting an error as shown below

Error 132 Cannot create delegate from method 'InfoChoice.Web.Admin.ViewModels.Product.ProductCreditCardViewModel.LookupTableViewModel.CardTypeVisit(System.Web.Mvc.SelectListItem)' because it is a partial method without an implementing declaration

Here is the calling code using Razor

<div class='row'>
    <label>Card Type</label>
    @Html.CheckBoxListFor(model => model.CardType, @Model.LookupTable.CardType)
    David Says: @Model.CardType
</div>

Here is a stripped down Model

// Generated model
public partial class ProductCreditCardViewModel
{
    [Required]
    [DisplayName("Account - Card Type")]
    [DefaultValue("")]
    public string CardType { get; set; }


    // ***************************************************************************************************
    // Lookup Table calls for this model (Generated)
    // ***************************************************************************************************
    public partial class LookupTableViewModel : BaseLookupTableViewModel
    {
        partial void CardTypeVisit(SelectListItem item);

        public SelectList CardType
        {
            get
            {
                return GetLookupItems("ProductCreditCardCardType", CardTypeVisit);
            }
        }
    }

}

public partial class ProductCreditCardViewModel
{
    // Custom Implementation Goes Here
    public partial class LookupTableViewModel : BaseLookupTableViewModel
    {
        //partial void CardTypeVisit(SelectListItem item)
        //{
        //    item.Value = "|" + item.Value + "|";
        //}
    }
}

Base LookupTable view model that will return Select Item List data

public class BaseLookupTableViewModel
{
    public SelectList GetLookupItems(string lookupName)
    {
        return GetLookupItems(lookupName, "Name", "Text", null, null);
    }
    public SelectList GetLookupItems(string lookupName, Action<SelectListItem> itemVisitor)
    {
        return GetLookupItems(lookupName, "Name", "Text", null, itemVisitor);
    }
    public SelectList GetLookupItems(string lookupName, string dataValueField, string dataTextField, object selectedValue, Action<SelectListItem> itemVisitor)
    {
        // Go get some data from DataStore
        var list = App.Data.Lookup.GetLookupList(lookupName);

        // Convert to IEnumerable<SelectItemList>
        var result = new SelectList(list, dataValueField, dataTextField, selectedValue);

        // If developer would like to alter that list before it is returned, then developer should implement an item visitor
        if (itemVisitor != null)
        {
            foreach (var item in result)
            {
                itemVisitor(item);
            }
        }

        return result;
    }

    public void SampleVisitor(SelectListItem item)
    {
        item.Value = "Modified: " + item.Value;
    }
}

Upvotes: 1

Views: 235

Answers (1)

Marc Gravell
Marc Gravell

Reputation: 1062770

The error is correct; partial methods do not exist if not implemented, so you can't bind a delegate to them in quite that way. Your best option here is either to call it manually based perhaps on a switch (or similar), or if you really need a delegate, look for the method with reflection (remembering to specify non-public binding-flags) and use Delegate.CreateDelegate if the method turns out to exist. Or a lazy option:

private void OnCardTypeVisit(SelectListItem item) { CardTypeVisit(item); }
partial void CardTypeVisit(SelectListItem item);

now bind your delegate to OnCardTypeVisit instead of CardTypeVisit.

In terms of delegates, this could also be as simple as changing it to an indirect delegate, which is very similar to the On* approach:

return GetLookupItems("ProductCreditCardCardType", x => CardTypeVisit(x));

(however, if that is an expression rather than a delegate I would expect failure)

Upvotes: 1

Related Questions