This is what I wanted to do:
public interface IGridColumn<TItem, TElement> { ... Linksoft.Forms.IGridColumn<TItem, TElement> Editable<TKey>(Func<Ajaxable<TKey>, Ajaxable<TElement>, string> editAction, ExtraFieldInfo info = null) where TItem : IHasId<TKey>; }
basically I have a library for grids built on top of Entity Framework defined like this:
Html.Grid(objectContext.EntitySet).Cols(c => { c.Column(o => o.Id); c.Column(o => o.Name).Editable(o => o.Id, (id,name) => MVC.ControllerName.Ajax_ChangeName(id,name)).Filter(); // or
c.Column( o => o.Description).Editable(o => o.Id, MVC.ControllerName.Ajax_ChangeDescription); c.Column(o => o.Created).Format("{0:}"); c.Column(o => o.Related.Title); });
Thanks to a lot of trickery, T4 code generation, expression rewriting, reflection etc. etc. etc. this works just great, with sorting, filters and it sends fairly optimal queries to the database. In the example above the only columns fetched are the Id, Name, Created and Title from the Related table. And the Editable() causes the grid to contain an inputbox with a javascript calling the method named "ChangeName" with AJAX.
What I don't like is to have to specifically ask for the Id I need for the edit actions. I added an IHasId<???> interface to the entities generated from .edmx and code for the static constructor that stores the needed LambdaExpression where I can find it later so I thought I could add a method like on top to the interface and this to the implementation:
public IGridColumn<TItem, TElement> Editable<TKey>(Func<Ajaxable<TKey>, Ajaxable<TElement>, string> editAction, ExtraFieldInfo info = null) where TItem : IHasId<TKey> { var selector = Linksoft.Forms.HasId.GetSelector<TItem>(); this.selectors["editable"] = new SelectorAndId { Selector = selector }; this._EditActionWithSelector = (obj, element) => editAction((TKey)obj, element); if (info != null) this.EditExtraFieldInfo.MergeWith(info); return this; }
So that if the model is an IQueryable<TItem> where TItem : IHasId<TKey>, I can write
c.Content(o => o.Name).Editable((id,name) => MVC.Controller.Ajax_ChangeName(id,name));
and let the library get the selector it needs, check the types etc.
The problem is that C# (at least the version in VS 2010) would not let me add a type constraint for the interface's type parameter to a single method and I don't want to restrict the library only to IQueryables of things that do have the IHasId interface.
I thought I could add the method to a subclass of the GridColumn<TItem,TElement> and duplicate the IGridColumn<TItem,TElement> interface with the copy having the additional constraint and method. The problem is that the c within the Cols() method is an IGridColumnBuilder<TItem>>, its Content() method builds a GridColumn and returns it as IGridColumn, but if the TItem does implement IHasId I'd need it to build GridColumnWithId and return it as IGridColumnWithId. The problem is that while I can have two methods that differ in the type constraints and number of type parameters like this:
IGridColumn<TItem, TElement> Content<TElement>(Expression<Func<TItem, TElement>> selector); IGridColumnWithId<TItem, TElement> Content<TElement,TKey>(Expression<Func<TItem, TElement>> selector) where TItem : IHasId<TKey>;
but C# doesn't seem to prefer the second when called without the type parameters as
c.Content(o => o.Name)
So, any clever ideas? Assuming anyone still follows. I'd really like something that would not complicate the interface.
Thanks, Jenda
----------------------------------
http://jendaperl.blogspot.com
A Perl developer in the world of C#