Globalization issue with decimal and Nancy model binding - model-binding

I'm new to Nancy and I'm trying to bind a decimal property using the Nancy.ModelBinding namespace like this:
var model = this.Bind<AddPaymentModel>();
My app is using a non-US culture (pt-BR) where decimals are represented differently (4.50 would be 4,50) and the code above throws an error, even thought the CurrentCulture is correctly set to pt-BR. I'm posting JSON and I think it might be related to the JSON deserializer...
Part of my stacktrace:
Nancy.RequestExecutionException: Oh noes! ---> System.Reflection.TargetInvocationException: Exception has been thrown by the target of an invocation. ---> System.Exception: 450,00 is not a valid value for Decimal. ---> System.FormatException: Input string was not in a correct format.
at System.Number.StringToNumber(String str, NumberStyles options, NumberBuffer& number, NumberFormatInfo info, Boolean parseDecimal)
at System.Number.ParseDecimal(String value, NumberStyles options, NumberFormatInfo numfmt)
at System.Decimal.Parse(String s, NumberStyles style, IFormatProvider provider)
at System.ComponentModel.DecimalConverter.FromString(String value, NumberFormatInfo formatInfo)
at System.ComponentModel.BaseNumberConverter.ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, Object value)
--- End of inner exception stack trace ---
at System.ComponentModel.BaseNumberConverter.ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, Object value)
at System.ComponentModel.TypeConverter.ConvertFromInvariantString(String text)
at Nancy.Json.JavaScriptSerializer.ConvertToType(Type type, Object obj)
at Nancy.Json.JavaScriptSerializer.ConvertToObject(IDictionary`2 dict, Type type)
at Nancy.Json.JavaScriptSerializer.ConvertToType(Type type, Object obj)
at Nancy.Json.JavaScriptSerializer.ConvertToType[T](Object obj)
at Nancy.Json.JavaScriptSerializer.Deserialize[T](String input)
--- End of inner exception stack trace ---
at System.RuntimeMethodHandle.InvokeMethod(Object target, Object[] arguments, Signature sig, Boolean constructor)
at System.Reflection.RuntimeMethodInfo.UnsafeInvokeInternal(Object obj, Object[] parameters, Object[] arguments)
at System.Reflection.RuntimeMethodInfo.Invoke(Object obj, BindingFlags invokeAttr, Binder binder, Object[] parameters, CultureInfo culture)
at System.Reflection.MethodBase.Invoke(Object obj, Object[] parameters)
at Nancy.ModelBinding.DefaultBodyDeserializers.JsonBodyDeserializer.Deserialize(String contentType, Stream bodyStream, BindingContext context)
at Nancy.ModelBinding.DefaultBinder.DeserializeRequestBody(BindingContext context)
at Nancy.ModelBinding.DefaultBinder.Bind(NancyContext context, Type modelType, String[] blackList)
What am I missing?

you could try switcing to the Newtonsoft.JsonNet serializer for nancy instead.
http://nuget.org/packages/Nancy.Serialization.JsonNet
Here is a "catch all" example implementaion
using System;
using System.Collections.Generic;
using Nancy;
using Nancy.Bootstrapper;
using Nancy.Serialization.JsonNet;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
namespace Application
{
public class Bootstrapper : DefaultNancyBootstrapper
{
protected override NancyInternalConfiguration InternalConfiguration
{
get
{
return NancyInternalConfiguration.WithOverrides(
c => c.Serializers.Insert(0, typeof(ModifiedJsonNetJsonSerializer)));
}
}
public class ModifiedJsonNetJsonSerializer : JsonNetSerializer
{
public ModifiedJsonNetJsonSerializer()
: base(new List<JsonConverter> { new FloatConverter() })
{ }
public class FloatConverter : JsonConverter
{
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
var floatValue = (float)value;
writer.WriteValue(string.Format("{0:0.00}", floatValue));
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
var jobject = JToken.Load(reader);
var str = jobject.Value<string>();
float number;
try
{
number = float.Parse(str);
}
catch (FormatException)
{
str = str.IndexOf(".", StringComparison.Ordinal) > -1 ? str.Replace('.', ',') : str.Replace(',', '.');
number = float.Parse(str);
}
return number;
}
public override bool CanConvert(Type objectType)
{
return objectType == typeof(float) || objectType == typeof(Single);
}
}
}
}
}

Related

MVC project No parameterless constructor defined for this object

im getting this error. Im newbie on MVC .
Can you help me please.I found somethings but i didnt understand what i will do. Sorry for my english.
I have 4 projects in a solution
.Admin
.UI
.Core
.Data
I have a problem with admin side.
Im trying to LoginFilter in admin page. When i run the project the page forwarding to /Account/Login page but its giving this error.
Error Page:
No parameterless constructor defined for this object.
Description: An unhandled exception occurred during the execution of the current web request. Please review the stack trace for more information about the error and where it originated in the code.
Exception Details: System.MissingMethodException: No parameterless constructor defined for this object.
Source Error:
An unhandled exception was generated during the execution of the current web request. Information regarding the origin and location of the exception can be identified using the exception stack trace below.
Stack Trace:
[MissingMethodException: No parameterless constructor defined for this object.]
System.RuntimeTypeHandle.CreateInstance(RuntimeType type, Boolean publicOnly, Boolean noCheck, Boolean& canBeCached, RuntimeMethodHandleInternal& ctor, Boolean& bNeedSecurityCheck) +0
System.RuntimeType.CreateInstanceSlow(Boolean publicOnly, Boolean skipCheckThis, Boolean fillCache, StackCrawlMark& stackMark) +113
System.RuntimeType.CreateInstanceDefaultCtor(Boolean publicOnly, Boolean skipCheckThis, Boolean fillCache, StackCrawlMark& stackMark) +206
System.Activator.CreateInstance(Type type, Boolean nonPublic) +83
System.Activator.CreateInstance(Type type) +11
System.Web.Mvc.DefaultControllerActivator.Create(RequestContext requestContext, Type controllerType) +55
[InvalidOperationException: An error occurred when trying to create a controller of type 'WebHaber.Admin.Controllers.AccountController'. Make sure that the controller has a parameterless public constructor.]
System.Web.Mvc.DefaultControllerActivator.Create(RequestContext requestContext, Type controllerType) +178
System.Web.Mvc.DefaultControllerFactory.GetControllerInstance(RequestContext requestContext, Type controllerType) +76
System.Web.Mvc.DefaultControllerFactory.CreateController(RequestContext requestContext, String controllerName) +88
System.Web.Mvc.MvcHandler.ProcessRequestInit(HttpContextBase httpContext, IController& controller, IControllerFactory& factory) +194
System.Web.Mvc.MvcHandler.BeginProcessRequest(HttpContextBase httpContext, AsyncCallback callback, Object state) +50
System.Web.Mvc.MvcHandler.BeginProcessRequest(HttpContext httpContext, AsyncCallback callback, Object state) +48
System.Web.Mvc.MvcHandler.System.Web.IHttpAsyncHandler.BeginProcessRequest(HttpContext context, AsyncCallback cb, Object extraData) +16
System.Web.CallHandlerExecutionStep.System.Web.HttpApplication.IExecutionStep.Execute() +103
System.Web.HttpApplication.ExecuteStep(IExecutionStep step, Boolean& completedSynchronously) +155
Version Information: Microsoft .NET Framework Version:4.0.30319; ASP.NET Version:4.6.1098.0
AccountController.cs
using WebHaber.Core.Infrastructure;
using WebHaber.Data.Model;
namespace WebHaber.Admin.Controllers
{
public class AccountController : Controller
{
#region Kullanıcı
private readonly IKullaniciRepository _kullaniciRepository;
public AccountController(IKullaniciRepository kullaniciRepository)
{
_kullaniciRepository = kullaniciRepository;
}
#endregion
// GET: Account
public ActionResult Login()
{
return View();
}
[HttpPost]
public ActionResult Login(Kullanici kullanici)
{
var KullaniciVarmi = _kullaniciRepository.GetMany(x => x.Email == kullanici.Email && x.Sifre == kullanici.Sifre && x.Aktif == true).SingleOrDefault();
if (KullaniciVarmi != null)
{
if (KullaniciVarmi.Rol.RolAdi == "Admin")
{
Session["KullaniciEmail"] = KullaniciVarmi.Email;
return RedirectToAction("Index", "Home");
}
ViewBag.Mesaj = "Yetkisiz Kullanıcı";
return View();
}
ViewBag.Mesaj = "Kullanıcı Bulunamadı";
return View();
}
LoginFilter.cs
namespace WebHaber.Admin.CustomFilter
{
public class LoginFilter : FilterAttribute, IActionFilter
{
public void OnActionExecuted(ActionExecutedContext context)
{
HttpContextWrapper wrapper = new HttpContextWrapper(HttpContext.Current);
var SessionControl = context.HttpContext.Session["KullaniciEmail"];
if (SessionControl == null)
{
context.Result = new RedirectToRouteResult(
new RouteValueDictionary { { "Controller", "Account" }, { "action", "Login" } });
}
}
public void OnActionExecuting(ActionExecutingContext context)
{}
BootStrapper.cs
using Autofac;
using Autofac.Integration.Mvc;
using WebHaber.Core.Infrastructure;
using WebHaber.Core.Repository;
using WebHaber.Data.DataContext;
namespace WebHaber.Admin.Class
{
public class BootStrapper
{
//Boot aşamasında çalışacak.
public static void RunConfig()
{
BuilAutoFac();
}
private static void BuilAutoFac()
{
var builder = new ContainerBuilder();
builder.RegisterType<HaberRepository>().As<IHaberRepository>();
builder.RegisterType<ResimRepository>().As<IResimRepository>();
builder.RegisterType<KullaniciRepository>().As<IKullaniciRepository>();
builder.RegisterType<RolRepository>().As<IRolRepository>();
builder.RegisterControllers(typeof(MvcApplication).Assembly);
var container = builder.Build();
DependencyResolver.SetResolver(new AutofacDependencyResolver(container));
}
}
Global.asax
public class MvcApplication : System.Web.HttpApplication
{
protected void Application_Start()
{
AreaRegistration.RegisterAllAreas();
RouteConfig.RegisterRoutes(RouteTable.Routes);
BootStrapper.RunConfig();
}
}
KullaniciRepository.cs
namespace WebHaber.Core.Repository
{
public class KullaniciRepository : IKullaniciRepository
{
private readonly HaberContext _context = new HaberContext();
public IEnumerable<Kullanici> GetAll()
{
//Tüm haberler dönecek
return _context.Kullanici.Select(x => x);
}
public Kullanici GetByID(int id)
{
return _context.Kullanici.FirstOrDefault(x => x.ID == id);
}
public Kullanici Get(Expression<Func<Kullanici, bool>> expression)
{
return _context.Kullanici.FirstOrDefault(expression);
}
public IQueryable<Kullanici> GetMany(Expression<Func<Kullanici, bool>> expression)
{
return _context.Kullanici.Where(expression);
}
public void Insert(Kullanici obj)
{
_context.Kullanici.Add(obj);
}
public void Update(Kullanici obj)
{
_context.Kullanici.AddOrUpdate();
}
public void Delete(int id)
{
var kullanici = GetByID(id);
if (kullanici!= null)
{
_context.Kullanici.Remove(kullanici);
}
}
public int Count()
{
return _context.Kullanici.Count();
}
public void Save()
{
_context.SaveChanges();
}
}
}
IKullaniciRepository.cs
namespace WebHaber.Core.Infrastructure
{
public interface IKullaniciRepository : IRepository<Kullanici>
{
}
}
IRepository.cs
namespace WebHaber.Core.Infrastructure
{
public interface IRepository<T> where T: class
{
IEnumerable<T> GetAll();
T GetByID(int id);
T Get(Expression<Func<T, bool>> expression);
IQueryable<T> GetMany(Expression<Func<T, bool>> expression);
void Insert(T obj);
void Update(T obj);
void Delete(int id);
int Count();
void Save();
}
}
Kullanici.cs model
namespace WebHaber.Data.Model
{
[Table("Kullanici")]
public class Kullanici
{
public int ID { get; set; }
[Display(Name = "Ad Soyad")]
[MaxLength(150, ErrorMessage = "150 karakterden fazla girildi.")]
[Required]
public string AdSoyad { get; set; }
[Display(Name = "Email")]
[DataType(DataType.EmailAddress)]
[Required]
public string Email { get; set; }
public string KullaniciAdi { get; set; }
[Display(Name = "Şifre")]
[DataType(DataType.Password)]
[Required]
public string Sifre { get; set; }
..............
..............
.............
The issue is pretty straightforward — An error occurred when trying to create a controller of type 'WebHaber.Admin.Controllers.AccountController'. Make sure that the controller has a parameterless public constructor.
You're trying to use Autofac to inject IKullaniciRepository service into the AccountController but the compiler couldn't find one although you've the declared the service registration at BootStrapper.cs
Therefore it's likely that BootStrapper.cs's RunConfig never get invoked. Just place a call (e.g. BootStrapper.RunConfig()) to Application_Start() method in Global.asax and you're fine.
#Zephyr thank you for help.
I added new global.asax file.
Because my Application_Start() was not firing.
In global.asax
Old one :
public class MvcApplication : System.Web.HttpApplication
new one: `
public class Global : System.Web.HttpApplication`

JsonSerializer - serialize decimal places with 'N2' formatting

I'm serializing decimals using Newtonsoft.Json.JsonSerializer.
How can I set it to serialize decimal numbers with only 1 decimal place to use 0 at the end.
i.e. 3.5 serializes to "3.50"?
You'll have to write your own custom JsonConverter and use it to intercept the decimal type so you can change how it gets serialized. Here's an example:
public class DecimalFormatConverter : JsonConverter
{
public override bool CanConvert(Type objectType)
{
return (objectType == typeof(decimal));
}
public override void WriteJson(JsonWriter writer, object value,
JsonSerializer serializer)
{
writer.WriteValue(string.Format("{0:N2}", value));
}
public override bool CanRead
{
get { return false; }
}
public override object ReadJson(JsonReader reader, Type objectType,
object existingValue, JsonSerializer serializer)
{
throw new NotImplementedException();
}
}
To use it, simply pass in a new instance of this custom converter to the SerializeObject method:
var json = JsonConvert.SerializeObject(yourObject, new DecimalFormatConverter());
The accepted answer is correct, but expanding upon the comments on accepted answer:
If you want the decimals in your JSON to be numbers instead of strings, you need to use WriteRawValue and use :0.00 instead of :N2 for the string formatting (as N2 includes thousand separator commas and other culture specific number formatting that will break your JSON)
public class DecimalFormatConverter : JsonConverter
{
public override bool CanConvert(Type objectType)
{
return objectType == typeof(decimal);
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
writer.WriteRawValue($"{value:0.00}");
}
public override bool CanRead => false;
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
throw new NotImplementedException();
}
}
This is the difference in output compared to the accepted answer.
# writer.WriteRawValue($"{value:0.00}");
{
"MyDecimal": 3.50,
"MyBiggerDecimal": 12345.50
}
# writer.WriteValue($"{value:N2}");
{
"MyDecimal": "3.50",
"MyBiggerDecimal": "12,345.50"
}
Note - the accepted answer is correct for the OP's specific question i.e. serialize 3.5 to "3.50", but I got here wanting to serialize 3.5 to 3.50 (without the string quotes).
Hy!
The second answer is also correct, but not reflecting Cultures.
If you want to have really 0.00 (with .) you have to use:
public class DecimalFormatConverter : JsonConverter
{
public override bool CanConvert(Type objectType)
{
return objectType == typeof(decimal);
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
FormattableString formattableString = $"{value:0.00}";
writer.WriteRawValue(formattableString.ToString(CultureInfo.InvariantCulture));
}
public override bool CanRead => false;
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
throw new NotImplementedException();
}
}
With this it is garantueed to have dot as decimal separater.
The difference is:
FormattableString formattableString = $"{value:0.00}";
writer.WriteRawValue(formattableString.ToString(CultureInfo.InvariantCulture));

Entity Framework doesn't create Database

i'm creating a database with entity framework, in an mvc asp.net application, using code first.
I'm new to the argument so be patient... I've created the database for the first time and all seems to be right; but couse of i didn't create a DropCreateDatabaseIfModelChanges method to change the tables i decided to manually delete the database.
The problem is that the database is not recreating!
I've implemented the initializer and it is in a different class from the context...
public class WidgetDbInitializer : DropCreateDatabaseIfModelChanges<WidgetDbContext>
{
}
Setted it up in Global.asax.cs and forced to init that
protected void Application_Start()
{
AreaRegistration.RegisterAllAreas();
RegisterGlobalFilters(GlobalFilters.Filters);
RegisterRoutes(RouteTable.Routes);
Database.SetInitializer<Portale.Models.WidgetDbContext>(new Portale.Models.WidgetDbInitializer());
var _initer = new WidgetDbInitializer();
using (var db = new WidgetDbContext())
{
_initer.Seedit(db);
db.Database.Initialize(true);
}
}
I've just the default connection string couse now i don't care about it...
Please help me i've read tons of articles over the web and i can't get a solution...
The error i get:
System.ArgumentNullException non è stata gestita dal codice utente
Message=Il valore non può essere null.
Nome parametro: key
Source=mscorlib
ParamName=key
StackTrace:
in System.Collections.Generic.Dictionary`2.FindEntry(TKey key)
in System.Collections.Generic.Dictionary`2.TryGetValue(TKey key, TValue& value)
in System.Data.Entity.ModelConfiguration.Configuration.Mapping.SortedEntityTypeIndex.Add(EdmEntitySet entitySet, EdmEntityType entityType)
in System.Data.Entity.ModelConfiguration.Configuration.Mapping.EntityMappingService.Analyze()
in System.Data.Entity.ModelConfiguration.Configuration.Mapping.EntityMappingService.Configure()
in System.Data.Entity.ModelConfiguration.Configuration.ModelConfiguration.ConfigureEntityTypes(DbDatabaseMapping databaseMapping, DbProviderManifest providerManifest)
in System.Data.Entity.ModelConfiguration.Configuration.ModelConfiguration.Configure(DbDatabaseMapping databaseMapping, DbProviderManifest providerManifest)
in System.Data.Entity.DbModelBuilder.Build(DbProviderManifest providerManifest, DbProviderInfo providerInfo)
in System.Data.Entity.DbModelBuilder.Build(DbConnection providerConnection)
in System.Data.Entity.Internal.LazyInternalContext.CreateModel(LazyInternalContext internalContext)
in System.Data.Entity.Internal.RetryLazy`2.GetValue(TInput input)
in System.Data.Entity.Internal.LazyInternalContext.InitializeContext()
in System.Data.Entity.Internal.InternalContext.Initialize()
in System.Data.Entity.Internal.InternalContext.GetEntitySetAndBaseTypeForType(Type entityType)
in System.Data.Entity.Internal.Linq.InternalSet`1.Initialize()
in System.Data.Entity.Internal.Linq.InternalSet`1.get_InternalContext()
in System.Data.Entity.Infrastructure.DbQuery`1.System.Linq.IQueryable.get_Provider()
in System.Linq.Queryable.Join[TOuter,TInner,TKey,TResult](IQueryable`1 outer, IEnumerable`1 inner, Expression`1 outerKeySelector, Expression`1 innerKeySelector, Expression`1 resultSelector)
in Portale.Controllers.WidgetContainerController.Index() in C:\Users\doompro\Documents\Visual Studio 2010\Projects\Portale\Portale\Controllers\WidgetContainerController.cs:riga 56
in lambda_method(Closure , ControllerBase , Object[] )
in System.Web.Mvc.ActionMethodDispatcher.Execute(ControllerBase controller, Object[] parameters)
in System.Web.Mvc.ReflectedActionDescriptor.Execute(ControllerContext controllerContext, IDictionary`2 parameters)
in System.Web.Mvc.ControllerActionInvoker.InvokeActionMethod(ControllerContext controllerContext, ActionDescriptor actionDescriptor, IDictionary`2 parameters)
in System.Web.Mvc.ControllerActionInvoker.<>c__DisplayClass15.<InvokeActionMethodWithFilters>b__12()
in System.Web.Mvc.ControllerActionInvoker.InvokeActionMethodFilter(IActionFilter filter, ActionExecutingContext preContext, Func`1 continuation)
InnerException:
override Seed function in your WidgetDbInitializer class and try to add some data to your database .
protected override void Seed(WidgetDbContext context)
{
context.yourodel.add(new class() )
}
first check your overriden seed invoke correctly , then if your database dose not generated you will get an exception .
Solved the problem:
public class Widget
{
//This properties rapresent the primary key for entity framework
[Key]
public int WidgetID { get; set; }
//Foreing key to the column where this widget is stored
public virtual int ColumnID { get; set; }
//The title of the widget
public string Title { get; set; }
//Controller of the Widget, this property may be used on the RenderAction call
public string Controller { get; set; }
//ActionMethod of the Widget, this property may be used on the RenderAction call
public string ActionMethod { get; set; }
//The Type of the Model, used on deserialization
public Type ModelType { get; set; }
//The context of the widget
public string SerializedModel { get; set; }
}
The database just didn't accept the type "Type", all working fine as soon as I deleted that field... I just didn't look at it because it was working with the "Object" type, didn't expect it doesnt with Type..

Castle Windsor Typed Factory Facility with generics

I'm trying to register a factory that could resolve an array of event handlers defined as follow:
public interface IEvent { }
public class EventA : IEvent { }
public class EventB : IEvent { }
public class EventC : IEvent { }
public interface IHandler<TEvent> where TEvent : IEvent
{
void Handle(TEvent ev);
}
public class HandlerX : IHandler<EventA>, IHandler<EventB>
{
public void Handle(EventA ev)
{
throw new NotImplementedException("handle EventA");
}
public void Handle(EventB ev)
{
throw new NotImplementedException("handle EventB");
}
}
public class HandlerY : IHandler<EventB>, IHandler<EventC>
{
public void Handle(EventB ev)
{
throw new NotImplementedException("handle EventB");
}
public void Handle(EventC ev)
{
throw new NotImplementedException("handle EventC");
}
}
public interface HandlerFactory
{
object[] GetHandlersForEvent(IEvent ev);
}
Basically for each event I can have more handlers and each handler can handle multiple events. I also want the factory to return object[] because at runtime I don't know what closed generic types would be returned.
I tried the approach descirbed by Krzysztof Koźmic http://kozmic.pl/2010/03/11/advanced-castle-windsor-ndash-generic-typed-factories-auto-release-and-more/
but still have problems.
Basically my question boils down to what types to return from my custom type deriving from DefaultTypedFactoryComponentSelector.
I tried many variations of the following:
public class HandlerSelector : DefaultTypedFactoryComponentSelector
{
protected override TypedFactoryComponent BuildFactoryComponent(MethodInfo method, string componentName, Type componentType, System.Collections.IDictionary additionalArguments)
{
Type eventType = null;
foreach (var k in additionalArguments.Values)
{
eventType = k.GetType();
}
var handlerType = typeof(IHandler<>).MakeGenericType(eventType);
var handlerArrayType = handlerType.MakeArrayType();
//return handlerArrayType;
return new TypedFactoryComponentCollection(handlerType, additionalArguments);
}
protected override Type GetComponentType(MethodInfo method, object[] arguments)
{
return typeof (object);
/*
var message = arguments[0];
var handlerType = typeof(IHandler<>).MakeGenericType(message.GetType());
var handlerArrayType = handlerType.MakeArrayType();
return handlerArrayType;
*/
}
/*
public TypedFactoryComponent SelectComponent(MethodInfo method, Type type, object[] arguments)
{
var message = arguments[0];
var handlerType = typeof(IHandler<>).MakeGenericType(message.GetType());
var result = new TypedFactoryComponentCollection(handlerType.MakeArrayType(), new Arguments(arguments));
return result;
}*/
}
with Windsor installer defined as:
public class Installer : IWindsorInstaller
{
public void Install(IWindsorContainer container, IConfigurationStore store)
{
container.AddFacility<TypedFactoryFacility>()
.Register(
Component.For<HandlerSelector>().ImplementedBy<HandlerSelector>(),
Component.For<AutoReleaseHandlerInterceptor>(),
AllTypes.FromAssemblyContaining<Program>()
.BasedOn(typeof(IHandler<>))
.WithService.Base()
.Configure(c => c.LifeStyle.Is(LifestyleType.Transient)
.Interceptors<AutoReleaseHandlerInterceptor>()),
Component.For<HandlerFactory>().AsFactory(c => c.SelectedWith<HandlerSelector>()));
}
}
When calling factory.GetHandlersForEvent(ev); I get an exception complaining about array type mismatch:
"Attempted to access an element as a type incompatible with the array."
Stack trace:
at System.Collections.Generic.Dictionary2.ValueCollection.CopyTo(TValue[] array, Int32 index)
at System.Collections.Generic.Dictionary2.ValueCollection.System.Collections.ICollection.CopyTo(Array array, Int32 index)
at Castle.MicroKernel.DefaultKernel.ResolveAll(Type service, IDictionary arguments) in e:\OSS.Code\Castle.Windsor\src\Castle.Windsor\MicroKernel\DefaultKernel_Resolve.cs:line 285
at Castle.Facilities.TypedFactory.TypedFactoryComponentCollection.Resolve(IKernel kernel) in e:\OSS.Code\Castle.Windsor\src\Castle.Windsor\Facilities\TypedFactory\TypedFactoryComponentCollection.cs:line 39
at Castle.Facilities.TypedFactory.Internal.TypedFactoryInterceptor.Resolve(IInvocation invocation) in e:\OSS.Code\Castle.Windsor\src\Castle.Windsor\Facilities\TypedFactory\Internal\TypedFactoryInterceptor.cs:line 173
at Castle.Facilities.TypedFactory.Internal.TypedFactoryInterceptor.Intercept(IInvocation invocation) in e:\OSS.Code\Castle.Windsor\src\Castle.Windsor\Facilities\TypedFactory\Internal\TypedFactoryInterceptor.cs:line 83
at Castle.DynamicProxy.AbstractInvocation.Proceed()
at Castle.Proxies.HandlerFactoryProxy.GetHandlersForEvent(IEvent ev)
at CastleWindsorTests.Program.TryIt(HandlerFactory factory) in c:\users\user\documents\visual studio 2010\Projects
How to implement the HandlerSelector so that it works well with factory defined as returning object[] whereas the real objects at runtime are closed generic types?
I'll be happy to be pointed to some existing documentation with guidelines for implementors of ITypedFactoryComponentSelector / DefaultTypedFactoryComponentSelector. Yes, I tried the http://docs.castleproject.org/(S(kwaa14uzdj55gv55dzgf0vui))/Windsor.Typed-Factory-Facility-interface-based-factories.ashx but here's not much about the above types.
I really don't want to introduce a service locator (instead of factory) ;).
To answer my own question:
I must've been blind. After reading more closely the xmldoc of the methods I override, changing HandlerSelector to the following solved the problem:
public class HandlerSelector : DefaultTypedFactoryComponentSelector
{
protected override TypedFactoryComponent BuildFactoryComponent(MethodInfo method, string componentName, Type componentType, System.Collections.IDictionary additionalArguments)
{
return new TypedFactoryComponentCollection(componentType, additionalArguments);
}
protected override Type GetComponentType(MethodInfo method, object[] arguments)
{
var message = arguments[0];
var handlerType = typeof(IHandler<>).MakeGenericType(message.GetType());
return handlerType;
}
}
#workabyte
For Castle v3, GetComponentType method stays the same but the BuildFactoryComponent method looks like this for me:
protected override Func<IKernelInternal, IReleasePolicy, object> BuildFactoryComponent(MethodInfo method, string componentName, Type componentType, IDictionary additionalArguments)
{
return (kernel, rp) => kernel.ResolveAll(componentType);
}

ArgumentNullException when streaming files via WCF

I have a WCF streaming binary files. Below is a shortened version of the contract.
[MessageContract()]
public class DocumentTransfer
{
[MessageHeader(MustUnderstand = true)]
public string Title { get; set; }
[MessageHeader(MustUnderstand = true)]
public string FileName { get; set; }
[MessageBodyMember(Order = 1)]
public System.IO.Stream Data;
}
Everything works fine but there are scenarios when only some other values associated with the file need to be updated but not the file itself. In that case the client sets Data = null. The Data property then cannot be interpreted/serialized and throws this exception:
System.ServiceModel.Dispatcher.StreamFormatter.Serialize(XmlDictionaryWriter writer, Object[] parameters, Object returnValue)
System.ServiceModel.Dispatcher.OperationFormatter.SerializeBodyContents(XmlDictionaryWriter writer, MessageVersion version, Object[] parameters, Object returnValue, Boolean isRequest)
System.ServiceModel.Dispatcher.OperationFormatter.OperationFormatterMessage.OperationFormatterBodyWriter.OnWriteBodyContents(XmlDictionaryWriter writer)
System.ServiceModel.Channels.BodyWriter.WriteBodyContents(XmlDictionaryWriter writer)
System.ServiceModel.Channels.BodyWriterMessage.OnWriteBodyContents(XmlDictionaryWriter writer)
System.ServiceModel.Channels.Message.OnWriteMessage(XmlDictionaryWriter writer)
System.ServiceModel.Channels.Message.WriteMessage(XmlDictionaryWriter writer)
System.ServiceModel.Channels.TextMessageEncoderFactory.TextMessageEncoder.WriteMessage(Message message, Stream stream)
System.ServiceModel.Channels.HttpOutput.WriteStreamedMessage(TimeSpan timeout)
System.ServiceModel.Channels.HttpOutput.Send(TimeSpan timeout)
System.ServiceModel.Channels.HttpChannelFactory.HttpRequestChannel.HttpChannelRequest.SendRequest(Message message, TimeSpan timeout)
System.ServiceModel.Channels.RequestChannel.Request(Message message, TimeSpan timeout)
System.ServiceModel.Dispatcher.RequestChannelBinder.Request(Message message, TimeSpan timeout)
System.ServiceModel.Channels.ServiceChannel.Call(String action, Boolean oneway, ProxyOperationRuntime operation, Object[] ins, Object[] outs, TimeSpan timeout)
System.ServiceModel.Channels.ServiceChannel.Call(String action, Boolean oneway, ProxyOperationRuntime operation, Object[] ins, Object[] outs)
System.ServiceModel.Channels.ServiceChannelProxy.InvokeService(IMethodCallMessage methodCall, ProxyOperationRuntime operation)
System.ServiceModel.Channels.ServiceChannelProxy.Invoke(IMessage message)
System.Runtime.Remoting.Proxies.RealProxy.HandleReturnMessage(IMessage reqMsg, IMessage retMsg)
System.Runtime.Remoting.Proxies.RealProxy.PrivateInvoke(MessageData& msgData, Int32 type)
...UploadDocument(DocumentTransfer request)
...UploadDocument(DocumentTransfer request) in ...
Any ideas guys?
You are getting the error in SerializeBodyContents when the body is null.
You can either create a different operation for when you do not have a file, or put a byte in the stream to avoid the exception. The first of these options is preferable, the last if you cannot change the contract.
Another option would be to return Stream.Null.

Resources