WCF DataContractSerializer and XAML Namespaces - c#

I am sending an object to a WCF service that contains a Windows Workflow definition, but the deserializer is faulting when trying to deserialize my custom activities.
This was previously working when I had the activities' namespaces defined in the form of:
xmlns:tta="clr-namespace:MyNamespace;assembly=MyAssembly"
but for maintainability reasons I have now mapped my activity namespaces to a XAML namespace using assembly attributes:
[assembly: XmlnsPrefix("http://schemas.product.com/activities/", "tta")]
[assembly: XmlnsDefinition("http://schemas.product.com/activities/", "MyNamespace")]
Thus my xaml namespace looks like: xmlns:tta="http://schemas.thacktech.com/activities/"
And my activity declared as: <tta:Naptime />
Because of this change, I now recieve NetDispatcherFaultException which reads:
The formatter threw an exception while trying to deserialize the message: There was an error while trying to deserialize parameter http://tempuri.org/:job. The InnerException message was 'Element 'http://schemas.datacontract.org/2004/07/System.Activities:Activity' contains data from a type that maps to the name 'Naptime, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null:MyNamespace.Naptime'. The deserializer has no knowledge of any type that maps to this name. Consider changing the implementation of the ResolveName method on your DataContractResolver to return a non-null value for name 'MyNamespace.Naptime' and namespace 'Naptime, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null'.'. Please see InnerException for more details.
Questions:
Why does the deseralizer succeed with type resolution when using the clr-namespace syntax, but fail when using the url-style syntax for a namespace declaration?
It looks like the type resolver is completely misinterpreting the type, listing the class name as the namespace. Why would it do this?
How do I properly implement this?
Thanks!

Related

Error when (de)serializing derived types with Json.Net

I'm trying to (de)serialize a class with this simple piece of code:-
var settings = new JsonSerializerSettings { TypeNameHandling = TypeNameHandling.All };
// Serialize
var json = JsonConvert.SerializeObject(obj, settings);
// Deseralize - this is where it fails
var test = JsonConvert.DeserializeObject<MyObject>(json, settings);
DeseralizeObject() fails with a JsonSerializationException:-
Error resolving type specified in JSON 'Xxx.Common.MyObject, Xxx.Common'. Path '$type', line 1, position 110
Inner exception: JsonSerializationException, message "Could not load assembly 'Xxx.Common".
I don't understand why it can't load the assembly - it's being referenced by this project!
It works if I don't use the JsonSerializerSettings, however I need this because the class being serialized will eventually have a List<SomeBaseClass> property that will contain derived types.
Any thoughts? I'm using Json.Net v6.0.
Edit:
I just tried adding TypeNameAssemblyFormat = FormatterAssemblyStyle.Full to my serializer settings, which resulted in this different (and confusing) exception message:-
Type specified in JSON 'Xxx.Common.MyObject, Xxx.Common, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null' is not compatible with 'Xxx.Common.MyObject, Xxx.Common, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null'. Path '$type', line 1, position 165.
Edit2:
To clarify things, the above code is a complete repro of the problem, and resides in a larger WPF application, while MyObject resides in a different project ("Xxx.Common") in the same solution, and is referenced by the WPF application project - I've simply replaced our company namespace with "Xxx" for this post.
MyObject is a simple POCO that I've created to rule out any issues that may be due to complex types, and consists of a few string and double properties, i.e.
public class MyObject
{
public string Name {get;set;}
public double Foo {get;set;}
...
}
The serialized JSON looks like this (again company NS replaced) - the pertinent part (i.e. the "$type") appears to be correct:-
{"$type":"Xxx.Common.MyObject, Xxx.Common","Name":null,"Foo":0.0,"StepSize":0.0,"Convergence":0.0,"Cutoff":0.0}

JSON.NET error resolving type in PowerShell cmdlet

I use the following code to deserialize JSON files to .NET objects:
using (var textReader = File.OpenText(filePath))
{
var settings = new JsonSerializerSettings
{
TypeNameAssemblyFormat = FormatterAssemblyStyle.Simple,
TypeNameHandling = TypeNameHandling.All
};
var deserializer = JsonSerializer.CreateDefault(settings);
deserializer.Converters.Add(new StringEnumConverter());
return deserializer.Deserialize<T>(new JsonTextReader(textReader));
}
This works all quite fine when using that functionality in the context of a unit test for example. All classes are placed in several assemblies.
Now instead of using unit tests I want to control the flow of my components by PowerShell cmdlets.
I wrote an cmdlet and import the module that is still placed in the bin\Debug folder: Import-Module .\MigrationShell.dll
This assembly references all other assemblies and classes that are serialized / deserialized.
When the JSON functions are used in the PowerShell context I get the following exception:
Error resolving type specified in JSON
'System.Collections.Generic.List`1[[Migration.Data.MediaGalleryItem,
Migration]], mscorlib'. Path '$values[0].MediaGalleryItems.$type',
line 7, position 133.
So it seems that JSON.NET is not able to resolve the type that is defined in the Migration.dll when my code is being called in PowerShell context.
How can I solve this issue?
Update:
I just checked that there are no problems in resolving my custom object types. The problem seems to be the generic list. But still the error occurs only when calling the functionality in a PowerShell cmdlet context.
I found a solution for my problem. The issue hereby is the the way of how JSON.NET resolves type names. I think it's not JSON.NET fault at all because it seems so that the PowerShell domain handles the type resolving in a different way.
The point is that I needed to provide the fully qualified type name instead of the short one for serializing and deserializing.
So I changed the serializer settings to this:
var settings = new JsonSerializerSettings
{
TypeNameAssemblyFormat = FormatterAssemblyStyle.Full,
TypeNameHandling = TypeNameHandling.All
};
So the JSON changed from this type declaration
"$type": "System.Collections.Generic.List`1[[Migration.Data.MediaGalleryItem, Migration]], mscorlib"
to this one
"$type": "System.Collections.Generic.List`1[[Migration.Data.MediaGalleryItem, Migration, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null]], mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"
Everything works fine now. Thanks to Dirk for his comments :)

ASP.NET vNext DataAnnotations DatabaseGenerated not working

I'm trying define a model ID variable like this in ASP.NET 5:
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int Id { get; set; }
To support data annotations I've added the package System.ComponentModel.DataAnnotations in my project.json file like this:
"System.ComponentModel.Annotations": "4.0.10-beta-22811"
And in the model cs file I've added using System.ComponentModel.DataAnnotations.Schema;
Though I get the following error:
Error CS0433 The type 'DatabaseGeneratedAttribute' exists in both 'System.ComponentModel.Annotations, Version=4.0.10.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a' and 'System.ComponentModel.DataAnnotations, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35'
And I don't know how I should fix this really. I've tried to include the namespace System.ComponentModel.Annotations instead of System.ComponentModel.DataAnnotations but it seems like it doesn't exist as I get this error then:
Error CS0234 The type or namespace name 'Annotations' does not exist in the namespace 'System.ComponentModel' (are you missing an assembly reference?)
And if that namespace does not exist I don't understand how I can get the previous error which tells me that DatabaseGeneratedAttribute exists in two places.
I would really appreciate all help I can get with this.
You can just use the KeyAttribute. That should do the auto generation for you.
[Key]
public int Id { get; set; }
This attribute is available in the System.ComponentModel.DataAnnotations namespace
However, if you want to continue using the DatabaseGeneratedAttribute. The error is pretty self explanatory. It tells you that it is available in both namespaces
System.ComponentModel.DataAnnotations
System.ComponentModel.DataAnnotations.Schema
You will need to explicity state the namespace you need to use E.g.
[System.ComponentModel.DataAnnotations.Schema.DatabaseGenerated(System.ComponentModel.DataAnnotations.Schema.DatabaseGeneratedOption.Identity)]
You can always use an alias to keep the namespace short and sweet.
Please check that your project does not have reference to both version of System.ComponentModel.DataAnnotations.dll assembly.
Old version (4.0.0.0) can be included to your project by default, and do not be removed after you install package with new version (4.0.10.0).

Bootstrapper.AutoMapper and Profile registration order

I'm working on an ASP.NET MVC 3 application using AutoMapper 2.2.0. I have some AutoMapper profiles declared and when I initialize them manually everything works just fine.
AutoMapper.Mapper.Initialize(x =>
{
x.AddProfile<ProdutoToProdutoViewModel>();
x.AddProfile<IPagedListProdutoToIPagedListProdutoViewModel>();
x.AddProfile<ItemToItemViewModel>();
x.AddProfile<CarrinhoToCarrinhoViewModel>();
});
//This is working
But when I try to initialize them with Bootstrapper.AutoMapper 2.0.3.0...
Bootstrapper.With.AutoMapper().Start();
...a configuration exception is thrown:
The following property on eGuruShop.Web.ViewModels.ItemViewModel cannot be mapped:
Itens
Add a custom mapping expression, ignore, add a custom resolver, or modify the destination type eGuruShop.Web.ViewModels.ItemViewModel.
Context:
Mapping to property Itens from eGuruShop.Domain.CatalogoProdutos.Item to eGuruShop.Web.ViewModels.ItemViewModel
Mapping to property Itens from System.Collections.Generic.IList`1[[eGuruShop.Domain.CatalogoProdutos.Item, eGuruShop.Domain, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null]] to System.Collections.Generic.IList`1[[eGuruShop.Web.ViewModels.ItemViewModel, eGuruShop.Web, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null]]
Mapping from type eGuruShop.Domain.CatalogoProdutos.Carrinho to eGuruShop.Web.ViewModels.CarrinhoViewModel
Exception of type 'AutoMapper.AutoMapperConfigurationException' was thrown
The CarrinhoToCarrinhoViewModel profile depends on the ItemToItemViewModel profile and when I change the initialization order to
AutoMapper.Mapper.Initialize(x =>
{
x.AddProfile<ProdutoToProdutoViewModel>();
x.AddProfile<IPagedListProdutoToIPagedListProdutoViewModel>();
x.AddProfile<CarrinhoToCarrinhoViewModel>();
x.AddProfile<ItemToItemViewModel>();
});
//Exception
I've got the same exception than before.
I suspect Bootstrapper is initializing the profiles in the wrong order, but I don't know how to solve it without abandoning Bootstrapper. Any suggestions or solutions to this problem?
Thanks

Exception Due to Type Missing From Assembly (Revised)

I have made some progress on the problem I posted about yesterday, so I am rewriting the post.
My problem appears to be related to my use of generics. Here's the relevant part of App.config (formatted with whitespace for readability):
<configSections>
<section
name="NA5300ResolverSynchroDevices"
type="InfrastructureModule.DeviceConfiguration.DeviceConfigurationSection
<NA5300ResolverSynchroModule.NA5300ResolverSynchroConfigurationElement>,
NA5300ResolverSynchroModule">
</section>
</configSections>
<NA5300ResolverSynchroDevices>
<Device deviceName="AzResolverSynchro" busAddress="7"/>
<Device deviceName="ElResolverSynchro" busAddress="8"/>
</NA5300ResolverSynchroDevices>
Here's the class I'm trying to map to the configuration section:
namespace InfrastructureModule.DeviceConfiguration
{
public class DeviceConfigurationSection<T> : ConfigurationSection
where T : DeviceConfigurationElement, new()
{
[ConfigurationProperty("", IsDefaultCollection = true, IsKey = false)]
public DeviceConfigurationElementCollection<T> Devices
{
get { return (DeviceConfigurationElementCollection<T>) base[""]; }
set { base[""] = value; }
}
}
}
Here's the C# code that tries to access the config file:
DeviceConfigurationSection<NA5300ResolverSynchroConfigurationElement> devices =
ConfigurationManager.GetSection("NA5300ResolverSynchroDevices") as
DeviceConfigurationSection<NA5300ResolverSynchroConfigurationElement>;
Here's the exception text I'm getting:
An error occurred creating the configuration section handler for NA5300ResolverSynchroDevices: Could not load type 'InfrastructureModule.DeviceConfiguration.DeviceConfigurationSection<NA5300ResolverSynchroModule.NA5300ResolverSynchroConfigurationElement>' from assembly 'NA5300ResolverSynchroModule'.
I know that in C# generics are instantiated at runtime rather than at compile time (unlike C++). I do not yet know enough about generics to understand what assembly a runtime-generated type is considered to live in when the generic type and the instantiating type live in different assemblies. Above, I told the runtime to look for it in assembly NA5300ResolverSynchroModule. I've also tried telling it to look for it in assembly InfrastructureModule. Neither works.
I am attempting to use a genric type because I will have many config sections for which the corresponding ConfigurationSection-derived types will all be of the form shown above. I want to avoid code duplication.
Can anybody see why my approach is failing and how I can fix it?
Your problem is actually how you've referenced the generic type.
Instead of (shortened):
<section name="..."
type="InfrastructureModule.DeviceConfiguration.DeviceConfigurationSection
<NA5300ResolverSynchroModule.NA5300ResolverSynchroConfigurationElement>,
NA5300ResolverSynchroModule" />
Try
<section name="..."
type="InfrastructureModule.DeviceConfiguration.DeviceConfigurationSection`1[[NA5300ResolverSynchroModule.NA5300ResolverSynchroConfigurationElement, NA5300ResolverSynchroModule]],
NA5300ResolverSynchroModule" />
Note the `1[[...]] rather than <...> or <...> part for the generic type. The part inside the [[...]] can be a full type definition as well - like namespace.class,assembly,token.
The 1 is "generic type with one type parameter". If the type takes 2 "aka SomeType<T,V>", use2. Note that you should put "type, assembly" in the double square brackets, not just "type"
I believe my problem was rooted in the fact that the runtime-generated type I tried to map to a configuration section does not live in an assembly. So, I created a type that does live in an assmebly.
namespace NA5300ResolverSynchroModule
{
public class NA5300ResolverSynchroDeviceConfigurationSection :
DeviceConfigurationSection<NA5300ResolverSynchroConfigurationElement>
{
}
}
I can reference NA5300ResolverSynchroDeviceConfigurationSection just fine in App.config.

Categories