Want to replace wcf serializer with a custom one.
After googling I've found examples. But it do not work.
Here is my code:
Substitutor:
internal class MySerializerSubstitutor : DataContractSerializerOperationBehavior
{
private static readonly MySerializer _serializer = new MySerializer();
public MySerializerSubstitutor (OperationDescription operationDescription)
: base(operationDescription)
{
}
public override XmlObjectSerializer CreateSerializer(Type type, string name, string ns, IList<Type> knownTypes)
{
return _serializer; //NEVER CALLED
}
public override XmlObjectSerializer CreateSerializer(Type type, XmlDictionaryString name, XmlDictionaryString ns, IList<Type> knownTypes)
{
return _serializer; // NEVER CALLED
}
}
Behavior which repolace serializer
public class MySerializerBehavior : IOperationBehavior
{
.......
public void ApplyDispatchBehavior(OperationDescription description, DispatchOperation dispatch)
{
var dcs = description.Behaviors.Find<DataContractSerializerOperationBehavior>();
if (dcs != null)
description.Behaviors.Remove(dcs);
description.Behaviors.Add(new MySerializerSubstitutor(description)); //works fine
}
.............
}
And host:
protected override void ApplyConfiguration()
{
var behavior = new MySerializerBehavior()
foreach (var op in Description.Endpoints.SelectMany(ep => ep.Contract.Operations))
{
op.Behaviors.Add(behavior);
}
}
Whats wrong with this code?
One problem that is immediately visible is that you cannot replace a behavior from a behavior. According to MSDN:
All of the IOperationBehavior methods pass an OperationDescription
object as a parameter. This parameter is for examination only; if you
modify the OperationDescription object the execution behavior is
undefined.
http://msdn.microsoft.com/en-us/library/system.servicemodel.description.ioperationbehavior.aspx
I am not 100% sure what you are trying to accomplish, but here is an example that modifies properties of the serializer behavior.
http://msdn.microsoft.com/en-us/library/system.servicemodel.description.datacontractserializeroperationbehavior.aspx
If you need more customization than the properties provide you can try to replace the DataContractSerializerOperationBehavior. EDIT: Just make sure you add it before calling Open. See this article for adding a new behavior http://msdn.microsoft.com/en-us/library/ms730137.aspx
Related
I am fairly new to .net core and I want to implement localization in my application. The built in ResourceManagerStringLocalizer service satisfies almost all my requirements and works great out of the box, but I want to add some custom functionality to it, without copy pasting all of the class' code and adding some to it in my own localizer. Currently I have tried deriving from the class in my own Localizer and injecting that as my IStringLocalizer:
services.AddTransient<IStringLocalizer<MyApp.SharedResource>, MyLocalizer<MyApp.SharedResource>>();
...
public class MyLocalizer<T> : ResourceManagerStringLocalizer, IStringLocalizer<T> {
public MyLocalizer(ResourceManager resourceManager, IResourceStringProvider resourceStringProvider, string baseName, IResourceNamesCache resourceNamesCache, ILogger logger) : base(resourceManager, resourceStringProvider, baseName, resourceNamesCache, logger) {
}
public override LocalizedString this[string name] {
get {
//do some custom functionality
return base[name];
}
}
public override LocalizedString this[string name, params object[] arguments] {
get {
//do some custom functionality
return base[name, arguments];
}
}
}
But this gives the following exception when trying to use it:
System.InvalidOperationException: 'Unable to resolve service for type 'System.Resources.ResourceManager' while attempting to activate 'MyApp.Services.MyLocalizer`1[MyApp.SharedResource]'.'
My question is, what's the best way to this? And whats best practice for doing things like this? I feel like this sort of stuff may come up more often in the future of this application and all help will be appreciated.
You don't have to override the built-in localizer, just implement your custom one:
public class MyLocalizer
{
private readonly IStringLocalizer _localizer;
public MyLocalizer(IStringLocalizerFactory factory)
{
var type = typeof(MyResourceType);
var assemblyName = new AssemblyName(type.GetTypeInfo().Assembly.FullName);
_localizer = factory.Create("MyResourceType", assemblyName.Name);
}
public LocalizedString this[string name]
{
get
{
// ...
return _localizer[name];
}
}
public LocalizedString this[string name, params object[] arguments]
{
get
{
// ...
return _localizer[name, arguments];
}
}
}
Then you can simple inject it to the views:
#inject MyLocalizer _loc;
<h1>#_loc["Hellow"]</h1>
or to the backend:
public class HomeController : Controller
{
private readonly MyLocalizer _loc;
public HomeController(MyLocalizer loc)
{
_loc = loc;
}
public IActionResult Index()
{
var msg = _loc["Welcome"];
//...
}
}
I am looking at implementing some pretty simple client side validation by implementing the IClientModelValidator interface. Specifically I am creating a NotEqualTo (and later an EqualTo) validation attribute that will compare the value of one input to another.
To provide a nice UX I want to use the display name of both inputs in the error messages: "Password cannot be the same as Email" for example.
This is has obviously been done a million times and there are plenty of example around, but they are either for previous versions of MVC or are not using the display name of the other property.
Below is what I have so far. I have managed to grab the display name via the Display attribute in the server side IsValid(...) method, but I can't work out how to do similar for the client side AddValidation(...) method.
[AttributeUsage(AttributeTargets.Property, AllowMultiple = false, Inherited = true)]
public class NotEqualToAttribute : ValidationAttribute, IClientModelValidator
{
private const string defaultErrorMessage = "{0} cannot be the same as {1}.";
public string OtherProperty { get; private set; }
public NotEqualToAttribute(string otherProperty) : base(defaultErrorMessage)
{
this.OtherProperty = otherProperty;
}
public override string FormatErrorMessage(string name)
{
string.Format(base.ErrorMessageString, name, this.OtherProperty);
}
public void AddValidation(ClientModelValidationContext context)
{
context.Attributes.Add("data-val", "true");
context.Attributes.Add("data-val-notequalto", this.FormatErrorMessage(context.ModelMetadata.GetDisplayName());
context.Attributes.Add("data-val-notequalto-otherproperty", this.otherProperty);
}
protected override ValidationResult IsValid(object value, ValidationContext validationContext)
{
if (value == null)
return ValidationResult.Success;
PropertyInfo otherProperty = validationContext.ObjectInstance.GetType().GetProperty(this.OtherProperty);
object otherValue = otherProperty.GetValue(validationContext.ObjectInstance, null);
if (!value.Equals(otherValue))
return ValidationResult.Success;
DisplayAttribute display = otherProperty.GetCustomAttribute<DisplayAttribute>();
string otherName = display?.GetName() ?? this.OtherProperty;
return new ValidationResult(string.Format(defaultErrorMessage, validationContext.DisplayName, otherName));
}
}
Typically I solved this myself after taking a break, just going to leave this here in case it helps someone else (or there is a better solution):
public void AddValidation(ClientModelValidationContext context)
{
context.Attributes.Add("data-val", "true");
string otherName =
context.ModelMetadata.ContainerMetadata.Properties
.Single(p => p.PropertyName == this.OtherProperty)
.GetDisplayName();
context.Attributes.Add("data-val-notequalto",
string.Format(defaultErrorMessage, context.ModelMetadata.GetDisplayName(), otherName)
);
}
You can get to the meta data for the other properties via ClientModelValidationContext.ModelMetadata.ContainerMetadata.Properties
I am attempting to generate a JSON file that will be used within the Dojo javascript framework and would like to return a position attribute to be used in a dojo.place() call. The position parameter can be either a number or a string.
Using the StructLayoutwould not seem to work as-is since the serializer would try to emit both the String and Integer types. I'm looking at creating a custom ContractResolver that overrides the CreatePrimitiveContract to return a custom JsonConverter class. However, looking a the API, it appears that the JsonConverter is created based on type, and not a specific object value.
How can I handle this case in C# using the Json.NET serializer?
Presumably the solution would involve two properties with custom setters to null out the other property when one is set in conjunction with some sort of custom Json.Net class to inspect the values of the properties and only serialize the non-null one.
** Hypothetical Example **
// C# struct (or class)
[StructLayout(LayoutKind.Explicit)]
struct DojoPosition {
[JsonProperty(PropertyName="position")]
[FieldOffset(0)]
public String StrPos;
[JsonProperty(PropertyName="position")]
[FieldOffset(0)]
public Int32 IntPos;
}
// Serialization output
DojoPosition pos;
pos.StrPos = "only";
var output = JsonConvert.SerializeObject(pos);
// Output is: { "position": "only" }
pos.IntPos = 3;
var output = JsonConvert.SerializeObject(pos);
// Output is: { "position": 3 }
I just had a similiar problem.
For simple manipulation of a contract look there: Overriding the serialization behaviour in Json.Net
For resolving a JsonPrimitiveContract override the CreateContract method.
Here is an example based on our solution:
public class JsonDotNetContractResolver : DefaultContractResolver
{
protected override JsonContract CreateContract(Type objectType)
{
if (typeof(DojoPosition).IsAssignableFrom(objectType))
{
return new JsonPrimitiveContract(objectType.GetGenericArguments()[1])
{
CreatedType = typeof(object), // Not sure this will work for you, or is necessary...
IsReference = false,
Converter = DojoPositionConverter,
};
}
return base.CreateContract(objectType);
}
private class DojoPositionConverter : JsonConverter
{
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
var dp = (DojoPosition) value;
if(string.IsNullOrEmpty(dp.StrPos))
serializer.Serialize(writer,dp.IntPos);
else
serializer.Serialize(writer,dp.StrPos);
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
//...
}
public override bool CanConvert(Type objectType)
{
//....
}
}
}
How to determine the type to deserialize from the reader is your homework ;)
NHibernate 3.2/Fluent NHibernate 1.3/StructureMap 2.6.3 -
Trying to follow DDD as an architectural strategy, I typically don't have dependencies on domain entities. However, I'm experimenting right now with adding more behavior to my domain entities so that they are not so anemic. Everything was going well until I hooked up NHibernate. I've got two issues:
NH requires a parameterless constructor and I'd rather not have a
ctor that shouldn't be used.
When NH tries to instantiate my entity, it needs to resolve my
dependencies but I haven't given NH anything with which it can do
that.
I've been reading on the web, but most (if not all) of the examples I have found are outdated (or just old). Even though the NH camp probably doesn't approve of what I'm doing, I'm looking for the NH way to do this.
The solution ended up an implementation of NHibernate's IInterceptor. It is actually a very simple implementation when you inherit from EmptyInterceptor and override JUST the Instantiate() and SetSession() methods. Here's my interceptor using StructureMap:
public class DependencyInjectionEntityInterceptor : EmptyInterceptor
{
IContainer _container;
ISession _session;
public DependencyInjectionEntityInterceptor(IContainer container)
{
_container = container;
}
public override void SetSession(ISession session)
{
_session = session;
}
public override object Instantiate(string clazz, EntityMode entityMode, object id)
{
if (entityMode == EntityMode.Poco)
{
var type = Assembly.GetAssembly(typeof (SomeClass)).GetTypes().FirstOrDefault(x => x.FullName == clazz);
var hasParameters = type.GetConstructors().Any(x => x.GetParameters().Any());
if (type != null && hasParameters)
{
var instance = _container.GetInstance(type);
var md = _session.SessionFactory.GetClassMetadata(clazz);
md.SetIdentifier(instance, id, entityMode);
return instance;
}
}
return base.Instantiate(clazz, entityMode, id);
}
}
Then, all you have to do is tell NHibernate to use your interceptor:
public FluentConfiguration GetFluentConfiguration(IContainer container)
{
return Fluently.Configure()
.Database(MsSqlConfiguration.MsSql2008
.ConnectionString(c => c.FromConnectionStringWithKey("Database"))
.ShowSql())
.Mappings(m =>
m.AutoMappings.Add(AutoMap.AssemblyOf<SomeClass>()))
.ExposeConfiguration(x =>
x.SetInterceptor(new DependencyInjectionEntityInterceptor(container)));
}
When I was researching this, some suggested passing in the SessionFactory into the ctor of the interceptor class. Honestly, from a session management perspective, this approach would be better.
If you need additional dependencies in your entities don't use constructor injection. Instead create an additional parameter in the entity method.
Now you will ask yourself how do you get the dependency. For this you can use CommandHandlers and Commands. The command handler takes the dependency within its constructor and calls the method of the entity. In the UI you create a command message and send it to a command processor which is responsible for calling the correct command handler.
I hope my explanation is comprehensible to you.
Domain:
public class Employee
{
public int Id { get; set; }
public string Name { get; set; }
public void SendNotification(string message, INotifier notifier)
{
notifier.SendMessage(string.Format("Message for customer '{0}' ({1}): {2}", Name, Id, message));
}
}
The INotifier infrastructure component is passed through the method and not the constructor!
Infrastructure:
public interface INotifier
{
void SendMessage(string message);
}
class EmailNotifier : INotifier
{
public void SendMessage(string message)
{
// SmtpClient...
}
}
class SMSNotifier : INotifier
{
public void SendMessage(string message)
{
// SMS ...
}
}
Command and CommandHandler:
public class NotificationCommandHandler : ICommandHandler<NotificationCommand>
{
private readonly INotifier _notifier;
public NotificationCommandHandler(INotifier notifier)
{
_notifier = notifier;
}
public void Execute(NotificationCommand commandMessage)
{
commandMessage.Employee.SendNotification(commandMessage.Message, _notifier);
}
}
public class NotificationCommand
{
public string Message { get; set; }
public Employee Employee { get; set; }
}
The CommandHandler gets the INotifier through constructor injection. So you do not need to use your IoC Container like a ServiceLocator.
Usage i.e. in the UI in a controller:
public class Controller
{
private readonly IMessageProcessor _messageProcessor;
public Controller(IMessageProcessor messageProcessor)
{
_messageProcessor = messageProcessor;
}
public void SendNotification (Employee employee, string message)
{
var sendMailCommand = new NotificationCommand
{
Employee = employee,
Message = message
};
_messageProcessor.Process(sendMailCommand);
}
}
If you have questions about the command processor have a look at the mvccontrib project or ask a separate question.
Sorry my previous answer didn't address the specific question. I did some more research, and it looks like I have much more to learn about when and when not to use an anemic domain model. Regarding your question, I found this article to be very on topic. It is on java, not c#, but the principles are the same. Hope this helps.
I would like to formulate a contrived scenario, which nevertheless has firm actual basis. Imagine a collection type COuter, which is a wrapper around an instance of another collection type CInner. Both implement IList (never mind the T).
Furthermore, a COuter instance is buried inside some object graph, the root of which (let us refer to it as R) is returned from a WCF service method.
My question is how can I customize the WCF serialization process, so that when R is returned, the request to serialize the COuter instance will be routed through my code, which will extract CInner and pass it to the serializer instead. Thus the receiving end still gets R, only no COuter instance is found in the object graph.
I hoped that How does WCF serialize the method call? will contain the answer, unfortunately the article mentioned there (http://msdn.microsoft.com/en-us/magazine/cc163569.aspx) only barely mentions that advanced serialization scenarios are possible using IDataContractSurrogate interface, but no details are given. I am, on the other hand, would really like to see a working example.
Thank you very much in advance.
EDIT
I have created a trivial WCF sample, which demonstrates the issue. The archive is located here - https://docs.google.com/leaf?id=0B2pbsdBJxJI3NzFiNjcxMmEtMTM5Yy00MWY2LWFiMTUtNjJiNjdkYTU1ZTk4&sort=name&layout=list&num=50
It contains three small projects:
HelloServiceAPI - contains the service interface and the argument types
Host - the HelloService host
Client - a simple console client.
The service defines one method, which returns an instance of the HelloServiceResult type, which contains a reference to COuterList type, which wraps CInnerList type. The reference is specified as IMyListInterface, where both COuterList and CInnerList implement this interface. What I need is that when the result is serialized before being transmitted to the client, the COuterList reference be replaced with the wrapped CInnerList reference. I know this can be done by utilizing the existing abilities of WCF, I just do not know how.
Here is how you implement your own Surrogate:
class YourCustomTypeSurrogate : IDataContractSurrogate
{
public Type GetDataContractType(Type type)
{
// Just for reference
//if (typeof(OldType).IsAssignableFrom(type))
//{
// return typeof(NewType);
//}
return type;
}
public object GetObjectToSerialize(object obj, Type targetType)
{
// This method is called on serialization.
//if (obj is OldType)
//{
// // ... use the XmlSerializer to perform the actual serialization.
// NewType newObj = new NewType();
// return newObj;
//}
return obj;
}
public object GetDeserializedObject(object obj, Type targetType)
{
// This method is called on deserialization.
// If PersonSurrogated is being deserialized...
//if (obj is NewType)
//{
// OldType newObj = new OldType();
// return newObj;
//}
return obj;
}
public Type GetReferencedTypeOnImport(string typeName, string typeNamespace, object customData)
{
// This method is called on schema import.
//if (typeNamespace.Equals("Your Type Namespace"))
//{
// if (typeName.Equals("NewType"))
// {
// return typeof(OldType);
// }
//}
return null;
}
public System.CodeDom.CodeTypeDeclaration ProcessImportedType(System.CodeDom.CodeTypeDeclaration typeDeclaration, System.CodeDom.CodeCompileUnit compileUnit)
{
// Not used in this sample.
// You could use this method to construct an entirely
// new CLR type when a certain type is imported, or modify a generated
// type in some way.
return typeDeclaration;
}
public object GetCustomDataToExport(Type clrType, Type dataContractType)
{
// Not used in this sample
return null;
}
public object GetCustomDataToExport(System.Reflection.MemberInfo memberInfo, Type dataContractType)
{
// Not used in this sample
return null;
}
public void GetKnownCustomDataTypes(Collection<Type> customDataTypes)
{
// Not used in this sample
}
}
Then you create a custom Serializer Operation Behavior :
public class CustomDataContractSerializerOperationBehavior : DataContractSerializerOperationBehavior
{
public CustomDataContractSerializerOperationBehavior(OperationDescription operationDescription) : base(operationDescription) { }
public override XmlObjectSerializer CreateSerializer(Type type, string name, string ns, IList<Type> knownTypes)
{
return new DataContractSerializer(
type /*typeof OldType*/,
knownTypes,
int.MaxValue /*maxItemsInObjectGraph */,
false /*ignoreExtensionDataObject*/,
true /*preserveObjectReferences*/,
new YourCustomTypeSurrogate());
}
public override XmlObjectSerializer CreateSerializer(Type type, XmlDictionaryString name, XmlDictionaryString ns, IList<Type> knownTypes)
{
return new DataContractSerializer(
type /*typeof OldType*/,
knownTypes,
int.MaxValue /*maxItemsInObjectGraph */,
false /*ignoreExtensionDataObject*/,
true /*preserveObjectReferences*/,
new YourCustomTypeSurrogate());
}
}
After that, you create an attribute to apply the above operation behavior to an operation contract :
public class CustomDataContractFormatAttribute : Attribute, IOperationBehavior
{
public void AddBindingParameters(OperationDescription description, BindingParameterCollection parameters)
{
}
public void ApplyClientBehavior(OperationDescription description, ClientOperation proxy)
{
ReplaceDataContractSerializerOperationBehavior(description);
}
public void ApplyDispatchBehavior(OperationDescription description, DispatchOperation dispatch)
{
ReplaceDataContractSerializerOperationBehavior(description);
}
public void Validate(OperationDescription description)
{
}
private static void ReplaceDataContractSerializerOperationBehavior(OperationDescription description)
{
DataContractSerializerOperationBehavior dcs = description.Behaviors.Find<DataContractSerializerOperationBehavior>();
if (dcs != null)
description.Behaviors.Remove(dcs);
description.Behaviors.Add(new CustomDataContractSerializerOperationBehavior(description));
}
}
And finally, you apply this Attribute to an operation :
[OperationContract]
[CustomDataContractFormat]
void DoWork();
If you want to apply this to whole service, then you customize Service Behavior instead of Operation Behavior.
Here are the references that were used to create this example :
http://msdn.microsoft.com/en-us/library/system.runtime.serialization.idatacontractsurrogate.aspx
http://www.danrigsby.com/blog/index.php/2008/03/07/xmlserializer-vs-datacontractserializer-serialization-in-wcf/
http://www.danrigsby.com/blog/index.php/2008/04/10/specifying-a-different-serializer-per-endpoint-in-wcf/
http://social.msdn.microsoft.com/forums/en-US/wcf/thread/e4d55f3f-86d1-441d-9187-64fbd8ab2b3d/
Have you tried the good old OnSerializingAttribute?
[Serializable]
[KnownType(typeof(COuterList))]
public class HelloServiceResult
{
public IMyListInterface List;
[OnSerialized]
void OnSerializing(StreamingContext context)
{
if (List is COuterList)
{
List = ((List as COuterList).InnerList as CInnerList);
}
}
}