IMetadataAware.OnMetadataCreated is never called - vb.net

I created attribute class to attach metadata to properties, so I can display tooltips for the form's input fields.
HelpAttribute implements IMetadataAware:
Public Class HelpAttribute
Inherits Attribute
Implements System.Web.Mvc.IMetadataAware
Public Sub New(text As String)
_text = text
End Sub
Private _text As String
Public ReadOnly Property Text As String
Get
Return _text
End Get
End Property
Public Sub OnMetadataCreated(metadata As System.Web.Mvc.ModelMetadata) Implements System.Web.Mvc.IMetadataAware.OnMetadataCreated
metadata.AdditionalValues.Add("HelpText", _text)
End Sub
End Class
I utilize this metadata in my extension method:
<Extension()>
Public Function HelpFor(Of TModel, TProperty)(ByVal htmlHelper As HtmlHelper(Of TModel), expression As Expression(Of Func(Of TModel, TProperty))) As MvcHtmlString
Dim metaData = ModelMetadata.FromLambdaExpression(Of TModel, TProperty)(expression, htmlHelper.ViewData)
If metaData.AdditionalValues.ContainsKey("HelpText") Then
Dim helpText = metaData.AdditionalValues("HelpText")
Return MvcHtmlString.Create(String.Format("<span class=""help""></span><div class=""tooltip"" style=""display: none""><div class=""border-top""></div><div class=""close"">close</div><br class=""clear""><div class=""content"">{1}</div></div>", htmlHelper.ViewContext.ViewData.TemplateInfo.GetFullHtmlFieldId(metaData.PropertyName), helpText, metaData.DisplayName))
End If
Return MvcHtmlString.Create(String.Format("<span class=""no_help""></span>", htmlHelper.ViewContext.ViewData.TemplateInfo.GetFullHtmlFieldId(metaData.PropertyName), metaData.DisplayName))
End Function
So I can call Html.HelpFor for any of my model's properties and if it has appropriate metadata I display a help icon which shows the tooltip on click (js).
That all works fine as long as HelpAttribute is defined in the same assembly as the classes that I decorate their properties with. Today I had to move HelpAttribute to a separate dll (different namespace as well), so I did that, I referenced the project and expected it to work. I do not get any compiler errors, the app works fine, but it does not display the help icons. I debuggedd the code and I see that the constructor of HelpAttribute is called for different properties with a proper text, but OnMetadataCreated is never called. Does anyone have an idea why that is and how to fix it?

Another reason this may not be called is if you have the wrong namespace referenced. So
using System.Web.ModelBinding;
will compile and not be hit, but you should be using
using System.Web.Mvc;

Once again I will answer my question myself. Apparently posting things on SO helps me structure the problem in my head. When I moved HelpAttribute to a seperate class library I had to reference System.Web.Mvc for IMetadataAware interface. I use .NET 4.0 and I automatically referenced MVC4 that I installed some time ago for testing purposes. It didn't get me any errors, it just did not work. When I changed System.web.Mvc to ver. 3.0 everything works smoothly.

using System;
using System.Collections.Generic;
using System.Globalization;
using System.Web;
using System.Web.Mvc;
namespace ColorPickerAttributeExample.Attributes
{
[AttributeUsage(AttributeTargets.Property, AllowMultiple = false)]
public sealed class ColorPickerAttribute : Attribute, IMetadataAware
{
private const string Template =
"$('#{0}').ColorPicker({{onSubmit: function(hsb, hex, rgb, el) {{" +
"var self = $(el); self.val(hex);self.ColorPickerHide();}}, onBeforeShow: function () " +
"{{$(this).ColorPickerSetColor(this.value);}}}}).bind('keyup', function(){{ $(this).ColorPickerSetColor(this.value); }});";
public const string ColorPicker = "_ColorPicker";
private int _count;
// if using IoC container, you could inject this into the attribute
internal HttpContextBase Context
{
get { return new HttpContextWrapper(HttpContext.Current); }
}
public string Id
{
get { return "jquery-colorpicker-" + _count; }
}
#region IMetadataAware Members
public void OnMetadataCreated(ModelMetadata metadata)
{
IList<string> list = Context.Items["Scripts"] as IList<string> ?? new List<string>();
_count = list.Count;
metadata.TemplateHint = ColorPicker;
metadata.AdditionalValues[ColorPicker] = Id;
list.Add(string.Format(CultureInfo.InvariantCulture, Template, Id));
Context.Items["Scripts"] = list;
}
#endregion
}
}
using ColorPickerAttributeExample.Attributes;
using System;
namespace ColorPickerAttributeExample.Models
{
public class HomeModel
{
[ColorPicker]
public string ColorPicker { get; set; }
[ColorPicker]
public DateTime ColorPicker2 { get; set; }
}
}
#model dynamic
#{
var picker = ViewData.GetModelAttribute<ColorPickerAttribute>();
if (picker != null)
{
#Html.LabelForModel()
#Html.TextBoxFor(m => m, new {id = ViewData.ModelMetadata.AdditionalValues[ColorPickerAttribute.ColorPicker]})
}
}

Related

How works extensions method in VB.net with an interface as a object

I am struggling with an extension method in VB.NET, but I can't find why I can't implement this (I came from C# and I still having issues like this between language specification and how the language works, sorry if this question is too newbie):
namespace FakeBusiness.BusinessInterface
{
public interface IBusiness
{
int Id { get; set; }
string Name { get; set; }
}
}
namespace FakeBusiness.SharedExtensions
{
using FakeBusiness.BusinessInterface;
public static class ExtensionsMethods
{
public static bool IsEmpty(this IBusiness theObject)
{
return theObject != null && theObject.Id != 0;
}
}
}
namespace FakeBusiness.BusinessLogic
{
using FakeBusiness.BusinessInterface;
using FakeBusiness.SharedExtensions;
public class Business : IBusiness
{
public int Id { get; set; }
public String Name { get; set; }
}
public static class Program
{
public static void Main(string[] args)
{
IBusiness obj = new Business();
Console.WriteLine($"The object is: {(obj.IsEmpty() ? "empty" : "not empty")}");
}
}
}
And I want to achieve the same logic but for a reason VB.NET says 'Cannot resolve the symbol' even if I import explicitly the namespace from the extension method:
Namespace FakeBusiness.SharedExtensions {
<Extension()>
Public Function IsBusinessObjectEmtpy(ByVal business As IBusiness) As Boolean
Return (business IsNot Nothing AndAlso business.Id = 0)
End Function
End Namespace
Namespace FakeBusiness.BusinessLogic
Public Class BusinessObject : Implements IBusiness
Property Id As Integer Implements IBusiness.Id
Property Name As String Implements IBusiness.Name
Public Sub New()
End Sub
End Class
Public Interface IBusiness
Property Id As Integer
Property Name As String
End Interface
Public Class FakeBusiness.SomeLogic
Public Sub SomeLogic()
Dim newObject = New BusinessObject()
If newObject.IsBusinessObjectEmpty() Then
' Do something...
End If
End Sub
End Class
End Namespace
Also I read that a possible solution is to use the instructions Option Strict On/Off and also Option Infer On/Off.
I tried every possible combination between these pair of compiler instructions but it didn't work.
Anyone knows how to fix this issue with an extension method?
Another possible solution is to put the same method into a base class and implement with an interface, but I want to achieve this in this way
because it will be useful to use in every class if I use a TypeParameter in the extension method.
Any suggestion?
In a Console Application project with a root namespace of FakeBusiness, the following is the direct equivalent of that C# code:
Namespace BusinessInterface
Public Interface IBusiness
Property Id As Integer
Property Name As String
End Interface
End Namespace
Imports System.Runtime.CompilerServices
Imports FakeBusiness.BusinessInterface
Namespace SharedExtensions
Public Module ExtensionMethods
<Extension>
Public Function IsEmpty(theObject As IBusiness) As Boolean
Return theObject IsNot Nothing AndAlso theObject.Id <> 0
End Function
End Module
End Namespace
Imports FakeBusiness.BusinessInterface
Imports FakeBusiness.SharedExtensions
Namespace BusinessLogic
Public Class Business
Implements IBusiness
Public Property Id As Integer Implements IBusiness.Id
Public Property Name As String Implements IBusiness.Name
End Class
Module Program
Sub Main(args As String())
Dim obj As IBusiness = New Business()
Console.WriteLine($"The object is: {If(obj.IsEmpty(), "empty", "Not empty")}")
End Sub
End Module
End Namespace

Protobuf-net / NetCore2: Deserialization ignores annotated private fields

Edit: The problem was with Nancy. Protobuf-net (de)serializes marked private fields just fine.
I am running a NetCore 2.0 unit test project. Protobuf-net appears to be ignored private fields even though the have the [ProtoMember] attribute.
[ProtoContract]
internal class Model
{
[ProtoMember(1)]
public int Example { get; private set; } // Works
[ProtoMember(2)]
private List<int> _a; // Not deserialized unless made public
public IEnumerable<int> A => this._a;
public Model(int example, IEnumerable<int> a)
{
this.Example = example;
this._a = a.ToList(); // Copy prevents mutation
}
private Model() // For deserialization
{
}
}
I have used a public IEnumerable<int> to avoid mutability and hide implementation details. It is backed by a private List<int> to allow serialization. However, protobuf-net will only deserialize the field if I make it public. The serialization, on the other hand, will actually include the data even if the field is private.
Is this intended behavior? Is there are a clean way to make protobuf-net honor the marked private field when deserializing?
P.S. The same behavior is seen for non-collection members, but I have demonstrated with IEnumerable/List because it shows the reason for this approach.
The following works identically (apart from the first line of the output) when targetting netcoreapp2.0 or net45. I'd be happy to help, but I'd need to see an example that fails. I'm using:
<PackageReference Include="protobuf-net" Version="2.3.6" />
Code:
using System;
using System.Collections.Generic;
using System.Linq;
using ProtoBuf;
[ProtoContract]
internal class Model
{
[ProtoMember(1)]
public int Example { get; private set; } // Works
[ProtoMember(2)]
private List<int> _a; // Not deserialized unless made public
public IEnumerable<int> A => this._a;
public Model(int example, IEnumerable<int> a)
{
this.Example = example;
this._a = a.ToList(); // Copy prevents mutation
}
private Model() // For deserialization
{
}
}
static class Program
{
static void Main()
{
#if NETCOREAPP2_0
Console.WriteLine(".NET Core 2.0");
#elif NET45
Console.WriteLine(".NET 4.5");
#endif
var obj = new Model(123, new int[] { 4, 5, 6 });
var clone = Serializer.DeepClone(obj);
Console.WriteLine(clone.Example);
foreach (var val in clone.A)
{
Console.WriteLine(val);
}
}
}

JAXB: serialize private fields and deserialize without a parameter-less contructor

The problem I have involves a pretty complex class structure but I managed to summarize the gist of it in the following simpler example. I need to be able to serialize an object of class MyItem (including private property 'text') and subsequently deserialize it without having a parameter-less constructor available and without being able to create one because it would totally mess up the current logic.
class MyCollection:
#XmlRootElement(name="collection")
public class MyCollection {
public MyCollection() {
this.items = new ArrayList<MyItem>();
}
#XmlElement(name="item")
private List<MyItem> items;
public void addItem(String text) {
this.items.add(new MyItem(text));
}
}
class MyItem:
public class MyItem {
public MyItem(String text) {
this.text = text;
}
#XmlAttribute
private String text;
}
The first requirement (serialize MyItem including private property) is met out of the box and I get the following xml as a result:
<collection>
<item text="FIRST"/>
<item text="SECOND"/>
<item text="THIRD"/>
</collection>
In order to meet the second requirement I decorated class MyItem with attribute #XmlJavaTypeAdapter
#XmlJavaTypeAdapter(MyItemAdapter.class)
public class MyItem {
...
and introduced classes AdaptedMyItem
public class AdaptedMyItem {
private String text;
public void setText(String text) { this.text = text; }
#XmlAttribute
public String getText() { return this.text; }
}
and MyItemAdapter
public class MyItemAdapter extends XmlAdapter<AdaptedMyItem, MyItem> {
#Override
public MyItem unmarshal(AdaptedMyItem adaptedMyItem) throws Exception {
return new MyItem(adaptedMyItem.getText());
}
#Override
public AdaptedMyItem marshal(MyItem item) throws Exception {
AdaptedMyItem result = new AdaptedMyItem();
result.setText("???"); // CANNOT USE item.getText()
return result;
}
}
but this is where I get stuck because in method marshal I cannot access MyItem.text and so I cannot use the standard approach for dealing with immutable classes in JAXB.
Bottomline: I would like to use the class adapter mechanism only when deserializing (because I need to invoke a non-parameterless constructor) but not when serializing (because I cannot access private properties). Would that be possible?

Ninject: How to resolve collection from object type

Just wanted to know if there is a way bind a type and resolve a collection. I dont know if Ninject can do this out of the box. I'm using MVC4 with Ninject3 so I have the NinjectWebCommon.cs where I register the services. There is nowhere I can get the kernel (I read that it was bad practice to access the kernel from elsewhere, but that can certainly be the solution to this).
For example, I'm having this class:
public class CacheManager
{
public IEnumerable<SelectListItem> Get<T>() where T : INameValue
I want to be able to send
CacheManager.Get<City>
and obtain the CityRepository class.
Is it this you want to do? :
using System.Collections.Generic;
using System.Linq;
using Ninject;
using Ninject.Modules;
using Ninject.Syntax;
public class Temp
{
public interface ICity { }
public class SelectListItem
{
}
public class FooCity : SelectListItem, ICity { }
public class BarCity : SelectListItem, ICity {}
public class CityModule : NinjectModule
{
public override void Load()
{
this.Bind<ICity>().To<FooCity>();
this.Bind<ICity>().To<BarCity>();
}
}
public class CacheManager
{
private readonly IResolutionRoot resolutionRoot;
public CacheManager(IResolutionRoot resolutionRoot)
{
this.resolutionRoot = resolutionRoot;
}
public IEnumerable<SelectListItem> Get<T>()
{
return this.resolutionRoot.GetAll<T>().OfType<SelectListItem>();
}
}
}
I'm unclear as to whether you have multiple implementations of T (ICity) or one implementation but several instances (like retrieving a list of city names from the database and creating one instance per name). The later you could solve by a this.Bind>().ToProvider(...) binding.
I ended up doing:
In NinjectWebCommon.cs:
kernel.Bind(typeof(CacheManager))
.ToSelf()
.InSingletonScope();
kernel.Bind<IDataListRepository<Locale>>()
.To<LocaleRepository>();
In CacheManager.cs:
public class CacheManager: IDisposable
{
private IKernel kernel;
public CacheManager(IKernel kernel)
{
this.kernel = kernel;
}
public IEnumerable<T> GetAsEnumerable<T>()
{
var rep = kernel.Get<IDataListRepository<T>>();
return rep.GetAll();
}
I don't know if this is bad-practice (since kernel in theory should only be used in the initialization phase), but I didn't find any other way to do it.
If better options exist, please let me know.

How to access custom attributes defined in WCF service using C#?

First question is, how can I get the type of an object stored in a variable? Generally we do:
Type t = typeof(ClassName); //if I know the class
but, how can I say something:
Type t = typeof(varClassName); //if the class name is stored in a variable
Second question, a broader picture is, I have a WCF service that contains a DataContract class say "MyClass" and I have defined a custom attribute called "MyAttribute" to it. There is one method say "GetDataUsingDataContract" with a parameter of type MyClass. Now on client, I invoke the webservice. I use MethodInfo and ParameterInfo classes to get the parameters of the method in question. But how can I access the attributes of the method parameter which is actually a class Myclass? Here is the code that I tried:
MyService.Service1Client client = new MyService.Service1Client();
Type t = typeof(MyService.Service1Client);
MethodInfo members = t.GetMethod("GetDataUsingDataContract");
ParameterInfo[] parameters = members.GetParameters();
foreach (var parameter in parameters)
{
MemberInfo mi = parameter.ParameterType; //Not sure if this the way
object[] attributes;
attributes = mi.GetCustomAttributes(true);
}
Above code doesn't retrieve me the custom attribute "MyAttribute". I tried the concept in the class that is defined in the same project and it works. Please HELP!
but, how can I say something:
Type t = typeof(varClassName); //if the class name is stored in a variable
Try
Type.GetType("varClassName", false, true);
As to your second question:
Above code doesn't retrieve me the
custom attribute "MyAttribute". I
tried the concept in the class that is
defined in the same project and it
works. Please HELP!
Just guessing, I'm not sure that attributes are exposed to the client, by default. I think its the same issue as an untrusted assembly. Some attributes are sensitive info. See this:
http://blogs.msdn.com/b/haibo_luo/archive/2006/02/21/536470.aspx
But you could try linking the service project types into your app by first referencing the service assembly in your client project, then going to your service reference -> "Configure Service Reference" and selecting "Reuse types in all referenced assemblies". I'm not sure this option will affect the service interface classes, but I use it often with my domain objects. Worth a try.
Type mi = parameter.ParameterType; //Not sure if this the way
object[] attributes;
attributes = mi.GetCustomAttributes(true);
Ensure your proxy class has knowledge on attributes
Hope this will help
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.ServiceModel;
using System.Runtime.Serialization;
using System.Reflection;
namespace WindowsFormsApplication1
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
StartService();
}
string url = "http://localhost:234/MyService/";
private void StartClient()
{
IMyService myService = ChannelFactory<IMyService>.CreateChannel(new BasicHttpBinding(), new EndpointAddress(url));
Type t = typeof(IMyService);
MethodInfo members = t.GetMethod("MyMethod");
ParameterInfo[] parameters = members.GetParameters();
foreach (var parameter in parameters)
{
Type mi = parameter.ParameterType;
object[] attributes;
attributes = mi.GetCustomAttributes(true);
}
}
private void StartService()
{
ServiceHost host = new ServiceHost(typeof(MyService), new Uri(url));
host.AddServiceEndpoint(typeof(IMyService), new BasicHttpBinding(), "");
host.Open();
}
private void button1_Click(object sender, EventArgs e)
{
StartClient();
}
}
[AttributeUsage(AttributeTargets.Interface)]
public class MyAttrib : Attribute
{
}
[MyAttrib]
public interface IMyContract
{
string Name { get; set; }
}
[DataContract]
public class MyContract : IMyContract
{
[DataMember]
public string Name { get; set; }
}
[ServiceContract]
public interface IMyService
{
[OperationContract]
bool MyMethod(IMyContract dummy);
}
[ServiceBehavior(UseSynchronizationContext = false)]
public class MyService : IMyService
{
public bool MyMethod(IMyContract dummy)
{
return true;
}
}
}