How can I add datacontract to a type (xxxDataTable) which is created by a typed data set? - wcf

I have a typed data set and I want to pass tables(which are created by .net) or collection of rows instead of objects(which I would be creating) or collecion of objects to the client side. Silverlight framework doesn't support system.data.datatable.

you don't need to add datacontract attribute to types you don't own. You can implement IDataContractSurrogate to replace instances of client-unknown types with known-type instances (for example lightweight datatable POCO).
If you used code-first approach, you wouldn't have this extra projection-copy operation between typed dataset class objects and your own POCO objects on serialization/deserialization (and you would have full control over data object types (POCOs)).
I find useful to use Json.Net 'any object to JObject' convertor (pretty fast and customizable) as first step before converting to something else:
public static class JsonExtensions
{
public static object Normalize(this JToken token)
{
var type = token.GetType();
if (type == typeof(JObject))
{
return (token as JObject).OfType<JProperty>().ToDictionary<JProperty, string, object>(property => property.Name, property => property.Value.Normalize());
}
if (type == typeof(JProperty))
{
var property = token as JProperty;
//return new DictionaryEntry(property.Name, property.Value.Normalize());
return new KeyValuePair<string, object>(property.Name, property.Value.Normalize());
}
if (type == typeof(JValue))
{
return (token as JValue).Value;
}
if (type == typeof(JArray))
{
//return (token as JArray).OfType<JValue>().Select(value => value.Normalize()).ToArray();
return (token as JArray).Select(value => value.Normalize()).ToArray();
}
throw new NotImplementedException();
//return null;
}
}
public class TestClass
{
public string StringProperty { get; set; }
public int IntProperty { get; set; }
public TestClass RefProperty { get; set; }
}
private static string DataContractXmlSerialize<T>(T source)
{
var serializer = new DataContractSerializer(source.GetType());
using (var ms = new MemoryStream())
{
serializer.WriteObject(ms, source);
return Encoding.UTF8.GetString(ms.ToArray());
}
}
Usage:
var test = new TestClass()
{
StringProperty = "StringProperty",
IntProperty = int.MaxValue,
RefProperty = new TestClass() { IntProperty = int.MinValue }
};
var jObj = JObject.FromObject(test);
var dict = jObj.Normalize();
var serializedDict = DataContractXmlSerialize(dict);
as you can see - output is WCF-serializable (standard dictionary being serialized produces not very nice xml with but you can use your own serializable dictionary)

You simply cannot use the ADO.NET implementation of a DataTable in your Silverlight client, but there are alternatives.
However, this blog post has an alternative DataTable implementation that you can serialize and can support in Silverlight.

If you want to access data in Silverlight application you should use RIA Services. You should create custom DTO object and create list of DTO objects from your DataTable rows and return it from RIA Service.
You get started with RIA Services follow MSDN at http://msdn.microsoft.com/en-us/library/ee707376(v=vs.91).aspx

Related

Derived type's properties missing in JSON response from ASP.NET Core API

The JSON response from my ASP.NET Core 3.1 API controller is missing properties. This happens when a property uses a derived type; any properties defined in the derived type but not in the base/interface will not be serialized to JSON. It seems there is some lack of support for polymorphism in the response, as if serialization is based on a property's defined type instead of its runtime type. How can I change this behavior to ensure that all public properties are included in the JSON response?
Example:
My .NET Core Web API Controller returns this object that has a property with an interface type.
// controller returns this object
public class Result
{
public IResultProperty ResultProperty { get; set; } // property uses an interface type
}
public interface IResultProperty
{ }
Here is a derived type that defines a new public property named Value.
public class StringResultProperty : IResultProperty
{
public string Value { get; set; }
}
If I return the derived type from my controller like this:
return new MainResult {
ResultProperty = new StringResultProperty { Value = "Hi there!" }
};
then the actual response includes an empty object (the Value property is missing):
I want the response to be:
{
"ResultProperty": { "Value": "Hi there!" }
}
While the other answers are good and solves the problem, if all you want is the general behavior to be like pre netcore3, you can use the Microsoft.AspNetCore.Mvc.NewtonsoftJson NuGet package and in Startup.cs do:
services.AddControllers().AddNewtonsoftJson()
More info here. This way, you don't need to create any extra json-converters.
I ended up creating a custom JsonConverter (System.Text.Json.Serialization namespace) which forces JsonSerializer to serialize to the object's runtime type. See the Solution section below. It's lengthy but it works well and does not require me to sacrifice object oriented principles in my API's design. (If you need something quicker and can use Newtonsoft then check out the top voted answer instead.)
Some background: Microsoft has a System.Text.Json serialization guide with a section titled Serialize properties of derived classes with good information relevant to my question. In particular it explains why properties of derived types are not serialized:
This behavior is intended to help prevent accidental exposure of data
in a derived runtime-created type.
If that is not a concern for you then the behavior can be overridden in the call to JsonSerializer.Serialize by either explicitly specifying the derived type or by specifying object, for example:
// by specifying the derived type
jsonString = JsonSerializer.Serialize(objToSerialize, objToSerialize.GetType(), serializeOptions);
// or specifying 'object' works too
jsonString = JsonSerializer.Serialize<object>(objToSerialize, serializeOptions);
To accomplish this with ASP.NET Core you need to hook into the serialization process. I did this with a custom JsonConverter that calls JsonSerializer.Serialize one of the ways shown above. I also implemented support for deserialization which, while not explicitly asked for in the original question, is almost always needed anyway. (Oddly, supporting only serialization and not deserialization proved to be tricky anyway.)
Solution
I created a base class, DerivedTypeJsonConverter, which contains all of the serialization & deserialization logic. For each of your base types, you would create a corresponding converter class for it that derives from DerivedTypeJsonConverter. This is explained in the numbered directions below.
This solution follows the "type name handling" convention from Json.NET which introduces support for polymorphism to JSON. It works by including an additional $type property in the derived type's JSON (ex: "$type":"StringResultProperty") that tells the converter what the object's true type is. (One difference: in Json.NET, $type's value is a fully qualified type + assembly name, whereas my $type is a custom string which helps future-proof against namespace/assembly/class name changes.) API callers are expected to include $type properties in their JSON requests for derived types. The serialization logic solves my original problem by ensuring that all of the object's public properties are serialized, and for consistency the $type property is also serialized.
Directions:
1) Copy the DerivedTypeJsonConverter class below into your project.
using System;
using System.Collections.Generic;
using System.Dynamic;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Text.Json;
using System.Text.Json.Serialization;
public abstract class DerivedTypeJsonConverter<TBase> : JsonConverter<TBase>
{
protected abstract string TypeToName(Type type);
protected abstract Type NameToType(string typeName);
private const string TypePropertyName = "$type";
public override bool CanConvert(Type objectType)
{
return typeof(TBase) == objectType;
}
public override TBase Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
{
// get the $type value by parsing the JSON string into a JsonDocument
JsonDocument jsonDocument = JsonDocument.ParseValue(ref reader);
jsonDocument.RootElement.TryGetProperty(TypePropertyName, out JsonElement typeNameElement);
string typeName = (typeNameElement.ValueKind == JsonValueKind.String) ? typeNameElement.GetString() : null;
if (string.IsNullOrWhiteSpace(typeName)) throw new InvalidOperationException($"Missing or invalid value for {TypePropertyName} (base type {typeof(TBase).FullName}).");
// get the JSON text that was read by the JsonDocument
string json;
using (var stream = new MemoryStream())
using (var writer = new Utf8JsonWriter(stream, new JsonWriterOptions { Encoder = options.Encoder })) {
jsonDocument.WriteTo(writer);
writer.Flush();
json = Encoding.UTF8.GetString(stream.ToArray());
}
// deserialize the JSON to the type specified by $type
try {
return (TBase)JsonSerializer.Deserialize(json, NameToType(typeName), options);
}
catch (Exception ex) {
throw new InvalidOperationException("Invalid JSON in request.", ex);
}
}
public override void Write(Utf8JsonWriter writer, TBase value, JsonSerializerOptions options)
{
// create an ExpandoObject from the value to serialize so we can dynamically add a $type property to it
ExpandoObject expando = ToExpandoObject(value);
expando.TryAdd(TypePropertyName, TypeToName(value.GetType()));
// serialize the expando
JsonSerializer.Serialize(writer, expando, options);
}
private static ExpandoObject ToExpandoObject(object obj)
{
var expando = new ExpandoObject();
if (obj != null) {
// copy all public properties
foreach (PropertyInfo property in obj.GetType().GetProperties(BindingFlags.Public | BindingFlags.Instance).Where(p => p.CanRead)) {
expando.TryAdd(property.Name, property.GetValue(obj));
}
}
return expando;
}
}
2) For each of your base types, create a class that derives from DerivedTypeJsonConverter. Implement the 2 abstract methods which are for mapping $type strings to actual types. Here is an example for my IResultProperty interface that you can follow.
public class ResultPropertyJsonConverter : DerivedTypeJsonConverter<IResultProperty>
{
protected override Type NameToType(string typeName)
{
return typeName switch
{
// map string values to types
nameof(StringResultProperty) => typeof(StringResultProperty)
// TODO: Create a case for each derived type
};
}
protected override string TypeToName(Type type)
{
// map types to string values
if (type == typeof(StringResultProperty)) return nameof(StringResultProperty);
// TODO: Create a condition for each derived type
}
}
3) Register the converters in Startup.cs.
services.AddControllers()
.AddJsonOptions(options => {
options.JsonSerializerOptions.Converters.Add(new ResultPropertyJsonConverter());
// TODO: Add each converter
});
4) In requests to the API, objects of derived types will need to include a $type property. Example JSON: { "Value":"Hi!", "$type":"StringResultProperty" }
Full gist here
The documentation shows how to serialize as the derived class when calling the serializer directly. The same technique can also be used in a custom converter that we then can tag our classes with.
First, create a custom converter
public class AsRuntimeTypeConverter<T> : JsonConverter<T>
{
public override T Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
{
return JsonSerializer.Deserialize<T>(ref reader, options);
}
public override void Write(Utf8JsonWriter writer, T value, JsonSerializerOptions options)
{
JsonSerializer.Serialize(writer, value, value?.GetType() ?? typeof(object), options);
}
}
Then mark the relevant classes to be used with the new converter
[JsonConverter(typeof(AsRuntimeTypeConverter<MyBaseClass>))]
public class MyBaseClass
{
...
Alternately, the converter can be registered in startup.cs instead
services
.AddControllers(options =>
.AddJsonOptions(options =>
{
options.JsonSerializerOptions.Converters.Add(new AsRuntimeTypeConverter<MyBaseClass>());
}));
I had a similar issue, where I was returning an enumerable of type TAnimal (but the object instances were of derived types such as Dog, Cat, etc.):
[HttpGet]
public IEnumerable<TAnimal> GetAnimals()
{
IEnumerable<TAnimal> list = GetListOfAnimals();
return list;
}
This only included properties defined in TAnimal.
However, in ASP .NET Core 3.1 at least, I found that I could just cast the object instances to object, and the JSON serializer then included all the properties from the derived classes:
[HttpGet]
public IEnumerable<object> GetAnimals()
{
IEnumerable<TAnimal> list = GetListOfAnimals();
return list.Select(a => (object)a);
}
(Note that the signature of the GetAnimals method must also changed, but that doesn't usually matter much in a web API context). If you need to provide type information for Swagger or whatever, you can annotate the method:
[HttpGet]
[Produces(MediaTypeNames.Application.Json, Type = typeof(TAnimal[]))]
public IEnumerable<object> GetAnimals()
{
...
}
Casting to object is a simple solution if you only have a 1-layer-deep object hierarchy to worry about.
This is the expected result. You're upcasting when you do that, so what will be serialized is the upcasted object, not the actual derived type. If you need stuff from the derived type, then that has to be the type of the property. You may want to use generics for this reason. In other words:
public class Result<TResultProperty>
where TResultProperty : IResultProperty
{
public TResultProperty ResultProperty { get; set; } // property uses an interface type
}
Then:
return new Result<StringResultProperty> {
ResultProperty = new StringResultProperty { Value = "Hi there!" }
};
I solved it by writing this extension:
public static class JsonSerializationExtensions
{
public static string ToJson<T>(this IEnumerable<T> enumerable, bool includeDerivedTypesProperties = true)
where T : class
{
var jsonOptions = new JsonSerializerOptions()
{
PropertyNamingPolicy = JsonNamingPolicy.CamelCase
};
if (includeDerivedTypeProperties)
{
var collection = enumerable.Select(e => e as object).ToList();
return JsonSerializer.Serialize<object>(collection, jsonOptions);
}
else
{
return JsonSerializer.Serialize(enumerable, jsonOptions);
}
}
}
I was also struggling with this in a .NET Core 3.1 API, where I wanted the result to include $type attribute.
As suggested, install the correct package and then 'AddNewtonsoftJson'.
I wanted the $type field to be added to show the derived type handling, to get that
services.AddControllers().AddNewtonsoftJson(options =>
{
options.SerializerSettings.TypeNameHandling = Newtonsoft.Json.TypeNameHandling.All;
});
Not knocking Newtonsoft, but I found an easier way to resolve this with the built handlers.
[OperationContract]
[WebInvoke(Method = "GET", UriTemplate = "/emps", BodyStyle = WebMessageBodyStyle.Wrapped, RequestFormat = WebMessageFormat.Json, ResponseFormat = WebMessageFormat.Json)]
List<emp> GetEmps();
//[DataContract(Namespace = "foo")] <<< comment/removed this line
public class emp
{
public string userId { get; set; }
public string firstName { get; set; }
}
public class dept
{
public string deptId{ get; set; }
public string deptName{ get; set; }
}
In my case dept objects where working fine, but emp ones were not - they came across as empty.

Service fabric remoting serializer

In experimenting with Service Fabric remoting I have some data types that are not serialized correctly. This is causing me many issues.
From the documentation it appears that everything needs to be decorated with [DataContract]. After using this on some test types it does appear that they serialize correctly.
However I frankly don't want to have to decorate everything. That would be a huge step backwards for me. I would prefer to use custom serialization all the way around.
This documentation seems to suggest that it is possible to register a custom serializer however it appears to only be for stateful services. I am primarily using remoting with stateless services.
The current remoting stack requires that your types use DataContract. Supposedly the team is close to releasing a new remoting stack in the near future that contains the ability to plug in custom serialization and a lot of improvements on the performance side but this is not available yet.
In the meantime, a workaround (not a very nice one mind you) is to make all of your proxies receive string or byte[] or something like that and take care of serialization/deserialization manually using something like JSON.Net. Personally I'd bite the bullet and make your types Data Contract Serializable until the new remoting bits are available.
With the release of Service Fabric V2 Remoting, this is now possible. See here for further details. Below
Here is an implementation of MessagePack remoting serializer I have used, but in your case the JSON example in the docs would probably suffice.
public class MessagePackMessageFactory : IServiceRemotingMessageBodyFactory
{
public IServiceRemotingRequestMessageBody CreateRequest(string interfaceName, string methodName, int numberOfParameters)
{
return new MessagePackRemotingRequestMessageBody(numberOfParameters);
}
public IServiceRemotingResponseMessageBody CreateResponse(string interfaceName, string methodName)
{
return new MessagePackServiceRemotingResponseMessageBody();
}
}
[MessagePackObject]
public class MessagePackRemotingRequestMessageBody : IServiceRemotingRequestMessageBody
{
[Key(0)]
public object Value;
public MessagePackRemotingRequestMessageBody()
{
}
public MessagePackRemotingRequestMessageBody(int parameterInfos)
{
}
public void SetParameter(int position, string paramName, object parameter)
{
Value = parameter;
}
public object GetParameter(int position, string paramName, Type paramType)
{
return Value;
}
}
[MessagePackObject]
public class MessagePackServiceRemotingResponseMessageBody : IServiceRemotingResponseMessageBody
{
[Key(0)]
public object Response;
public object Get(Type paramType)
{
// ignore paramType?
return Response;
}
public void Set(object response)
{
Response = response;
}
}
public class ServiceRemotingResponseMessagePackMessageBodySerializer : IServiceRemotingResponseMessageBodySerializer
{
public OutgoingMessageBody Serialize(IServiceRemotingResponseMessageBody responseMessageBody)
{
if (!(responseMessageBody is MessagePackServiceRemotingResponseMessageBody body))
{
return new OutgoingMessageBody(new[] { new ArraySegment<byte>(new byte[0]) });
}
var bytes = MessagePackSerializer.Serialize(body, ServiceFabricResolver.Instance);
return new OutgoingMessageBody(new[] { new ArraySegment<byte>(bytes) });
}
public IServiceRemotingResponseMessageBody Deserialize(IncomingMessageBody messageBody)
{
using (var stream = messageBody.GetReceivedBuffer())
{
if (stream.Length == 0)
{
return new MessagePackServiceRemotingResponseMessageBody();
}
var body = MessagePackSerializer.Deserialize<MessagePackServiceRemotingResponseMessageBody>(stream, ServiceFabricResolver.Instance);
return body;
}
}
}
public class ServiceRemotingMessagePackSerializationProvider : IServiceRemotingMessageSerializationProvider
{
public IServiceRemotingRequestMessageBodySerializer CreateRequestMessageSerializer(Type serviceInterfaceType,
IEnumerable<Type> requestBodyTypes)
{
return new ServiceRemotingRequestMessagePackMessageBodySerializer();
}
public IServiceRemotingResponseMessageBodySerializer CreateResponseMessageSerializer(Type serviceInterfaceType, IEnumerable<Type> responseBodyTypes)
{
return new ServiceRemotingResponseMessagePackMessageBodySerializer();
}
public IServiceRemotingMessageBodyFactory CreateMessageBodyFactory()
{
return new MessagePackMessageFactory();
}
}
public class ServiceRemotingRequestMessagePackMessageBodySerializer : IServiceRemotingRequestMessageBodySerializer
{
public OutgoingMessageBody Serialize(IServiceRemotingRequestMessageBody serviceRemotingRequestMessageBody)
{
if (serviceRemotingRequestMessageBody == null) return null;
if (!(serviceRemotingRequestMessageBody is MessagePackRemotingRequestMessageBody body))
{
return new OutgoingMessageBody(new[] { new ArraySegment<byte>(new byte[0]) });
}
var bytes = MessagePackSerializer.Serialize(body, ServiceFabricResolver.Instance);
return new OutgoingMessageBody(new[] { new ArraySegment<byte>(bytes) });
}
public IServiceRemotingRequestMessageBody Deserialize(IncomingMessageBody messageBody)
{
using (var stream = messageBody.GetReceivedBuffer())
{
if (stream.Length == 0)
{
return new MessagePackRemotingRequestMessageBody();
}
var body = MessagePackSerializer.Deserialize<MessagePackRemotingRequestMessageBody>(stream, ServiceFabricResolver.Instance);
return body;
}
}
}

Web API will not use ISerializable implementation

I thought I had jumped through the necessary hoops to get my JsonMediaTypeFormatter working with custom ISerializable implementations, complete with passing unit tests. But I'm unable to get it to work when I pass in values via Swagger UI.
My key questions are:
What am I doing wrong with my unit test causing it to serialize/deserialize different from what Web API is doing?
What do I need to change to get this working with Web API's serializing/deserialization and Swagger/Swashbuckle?
Class being serialized: (Notice that serializing and then deserializing drops off the time component and only keeps the date component. The helps for testing/observing purposes.)
public class Pet : ISerializable
{
public DateTime Dob { get; set; }
public Pet()
{
Dob = DateTime.Parse("1500-12-25 07:59:59");
}
public Pet(SerializationInfo info, StreamingContext context)
{
Dob = DateTime.Parse(info.GetString("Dob"));
}
public void GetObjectData(SerializationInfo info, StreamingContext context)
{
info.AddValue("Dob", Dob.Date.ToString());
}
}
Web API Method: (always returns null)
public class TestController : ApiController
{
[Route("~/api/Pet")]
public string Get([FromUri] Pet data)
{
return data.Dob.ToString();
}
}
Passing Unit Test: (and serialization helpers from MSDN docs)
[TestFixture]
public class SerializationTests
{
[Test]
public void PetTest()
{
var date = new DateTime(2017, 1, 20, 5, 0, 0);
var foo = new Pet { Dob = date };
var jsonFormatter = new JsonMediaTypeFormatter { SerializerSettings = new JsonSerializerSettings { ContractResolver = new DefaultContractResolver { IgnoreSerializableInterface = false } } };
var serialized = SerializationHelpers.Serialize(jsonFormatter, foo);
Console.WriteLine(serialized);
var deserialized = SerializationHelpers.Deserialize<Pet>(jsonFormatter, serialized);
Assert.That(foo.Dob, Is.Not.EqualTo(date.Date));
Assert.That(deserialized.Dob, Is.EqualTo(date.Date));
}
}
public static class SerializationHelpers
{
public static string Serialize<T>(MediaTypeFormatter formatter, T value)
{
// Create a dummy HTTP Content.
Stream stream = new MemoryStream();
var content = new StreamContent(stream);
// Serialize the object.
formatter.WriteToStreamAsync(typeof(T), value, stream, content, null).Wait();
// Read the serialized string.
stream.Position = 0;
return content.ReadAsStringAsync().Result;
}
public static T Deserialize<T>(MediaTypeFormatter formatter, string str) where T : class
{
// Write the serialized string to a memory stream.
Stream stream = new MemoryStream();
StreamWriter writer = new StreamWriter(stream);
writer.Write(str);
writer.Flush();
stream.Position = 0;
// Deserialize to an object of type T
return formatter.ReadFromStreamAsync(typeof(T), stream, null, null).Result as T;
}
}
WebApiConfig.cs
public static class WebApiConfig
{
public static void Register(HttpConfiguration config)
{
// Web API configuration and services
config.Formatters.Clear();
var jsonFormatter = new JsonMediaTypeFormatter { SerializerSettings = new JsonSerializerSettings { ContractResolver = new DefaultContractResolver { IgnoreSerializableInterface = false } } };
config.Formatters.Add(jsonFormatter);
// Web API routes
config.MapHttpAttributeRoutes();
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{id}",
defaults: new { id = RouteParameter.Optional }
);
}
}
A few other notes:
When I run the passing unit test, the Console.WriteLine output is:
{"Dob":"1/20/2017 12:00:00 AM"}
which is exactly what I want/expect.
My Swagger UI looks like this using the default Swashbuckle settings from Nuget. Note that value of the date is what is set in the default constructor, showing that my ISerializable implementation is ignored.
NOTE:
I have changed the question to remove all generics from the picture. This problem is fundamentally about ISerializable implementations now and not about Generics.
WebAPI api does not know how to deserialize this generic object. I see a similar question here in SO but did not personally try/test it. Hope it helps: Generic Web Api method
Rather than having a generic method, you can create a generic controller. So your code above will look something like below.
public abstract class MyClass{ }
public class PersonDto: MyClass{}
public class TestController<T> : ApiController where T: MyClass
{
public string Get([FromUri] T data)
{
...
}
}

WCF can't get object from other project in the same solution

I have two projects in a solution:
Project A have defined some classes.
Project B is a WCF project and return object from project A
In project A, I write a function to get a list of objects (Song)
public class SongRepository
{
private dbContext db = new dbContext();
public List<Song> getAll()
{
List<Song> songs = db.Songs.ToList();
return songs;
}
}
An in project B, I write a function that used SongRepository to get a list of objects (Song)
public class Service1 : IService1
{
private SongRepository sr = new SongRepository();
public List<Song> getAllSong()
{
List<Song> songs = sr.getAll();
return songs;
}
}
and IService1 class:
namespace webservice
{
// NOTE: You can use the "Rename" command on the "Refactor" menu to change the interface name "IService1" in both code and config file together.
[ServiceContract]
public interface IService1
{
[OperationContract]
List<Song> getAllSong();
}
}
The result is that the WCF project does not return a list of Songs. But I've tested separately in project A and it returns the true data (a list of Songs)
I don't know how to use WCF to get data from another project in the same solution. Can anyone help me?
My problem have solved! Thank you devdigital for answering me.
The solution is that I use an adapter to convert business object to data transfer object. Other people have the same issue as me can go to this link: http://valueinjecter.codeplex.com/
to download the Omu.ValueInjecter dll.
After adding this dll file above, use this two functions to convert BO to DTO:
public T ConvertObjType<T>(object obj)
{
T instance = Activator.CreateInstance<T>();
if (obj != null)
StaticValueInjecter.InjectFrom((object)instance, new object[1]
{
obj
});
return instance;
}
public List<T> ConvertListObjType<G, T>(List<G> listObj)
{
if (listObj == null || listObj.Count == 0)
return (List<T>)null;
List<T> list = new List<T>();
typeof(T).GetProperties();
listObj[0].GetType().GetProperties();
foreach (G g in listObj)
{
object obj = (object)g;
T instance = Activator.CreateInstance<T>();
StaticValueInjecter.InjectFrom((object)instance, new object[1]
{
obj
});
list.Add(instance);
}
return list;
}
I hope this help you out.

"Real" Object References in Distributed Cache?

I'm personally committed to .net distributed caching solutions, but I think this question is interesting across all platforms.
Is there a distributed caching solution (or generic strategy) that allows to both store objects in the cache while maintaining the integrity of the references between them?
To exemplify - Suppose I have an object Foo foo that references an object Bar bar and also and object Foo foo2 that references that same Bar bar. If I load foo to the cache, a copy of bar is stored along with it. If I also load foo2 to the cache, a separate copy of bar is stored along with that. If I change foo.bar in the cache, the change does not impact foo2.bar :(
Is there an existing distributed cache solution that will enable me to load foo, foo2 and bar into the cache while maintaining the foo.bar foo2.bar references?
First and foremost
I do not know of any distributed system, and I do not pretend to build one. This post explains how you can simulate this behavior with .NET and C# using the IObjectReference interface with serializable objects.
Now, lets go on with the show
I do not know of such a distributed system, but you can somewhat easily achive this with .NET using the IObjectReference interface. Your implementation of ISerializable.GetObjectData would need to call SerializationInfo.SetType to point out a proxy class that implements IObjectReference, and would be able (with help from data provided by your GetObjectData method) to get a reference to the real object that should be used.
Example code:
[Serializable]
internal sealed class SerializationProxy<TOwner, TKey> : ISerializable, IObjectReference {
private const string KeyName = "Key";
private const string InstantiatorName = "Instantiator";
private static readonly Type thisType = typeof(SerializationProxy<TOwner, TKey>);
private static readonly Type keyType = typeof(TKey);
private static readonly Type instantiatorType = typeof(Func<TKey, TOwner>);
private readonly Func<TKey, TOwner> _instantiator;
private readonly TKey _key;
private SerializationProxy() {
}
private SerializationProxy(SerializationInfo info, StreamingContext context) {
if (info == null) throw new ArgumentNullException("info");
_key = (TKey)info.GetValue(KeyName, keyType);
_instantiator = (Func<TKey, TOwner>)info.GetValue(InstantiatorName, instantiatorType);
}
void ISerializable.GetObjectData(SerializationInfo info, StreamingContext context) {
throw new NotSupportedException("This type should never be serialized.");
}
object IObjectReference.GetRealObject(StreamingContext context) {
return _instantiator(_key);
}
internal static void PrepareSerialization(SerializationInfo info, TKey key, Func<TKey, TOwner> instantiator) {
if (info == null) throw new ArgumentNullException("info");
if (instantiator == null) throw new ArgumentNullException("instantiator");
info.SetType(thisType);
info.AddValue(KeyName, key, keyType);
info.AddValue(InstantiatorName, instantiator, instantiatorType);
}
}
This code would be called with SerializationProxy.PrepareSerialization(info, myKey, myKey => LoadedInstances.GetById(myKey)) from your GetObjectData method, and your LoadedInstances.GetById should return the instance from a Dictionary<TKey, WeakReference> or load it from cache/database if it isnt already loaded.
EDIT:
I've wrote some example code to show what I mean.
public static class Program {
public static void Main() {
// Create an item and serialize it.
// Pretend that the bytes are stored in some magical
// domain where everyone lives happily ever after.
var item = new Item { Name = "Bleh" };
var bytes = Serialize(item);
{
// Deserialize those bytes back into the cruel world.
var loadedItem1 = Deserialize<Item>(bytes);
var loadedItem2 = Deserialize<Item>(bytes);
// This should work since we've deserialized identical
// data twice.
Debug.Assert(loadedItem1.Id == loadedItem2.Id);
Debug.Assert(loadedItem1.Name == loadedItem2.Name);
// Notice that both variables refer to the same object.
Debug.Assert(ReferenceEquals(loadedItem1, loadedItem2));
loadedItem1.Name = "Bluh";
Debug.Assert(loadedItem1.Name == loadedItem2.Name);
}
{
// Deserialize those bytes back into the cruel world. (Once again.)
var loadedItem1 = Deserialize<Item>(bytes);
// Notice that we got the same item that we messed
// around with earlier.
Debug.Assert(loadedItem1.Name == "Bluh");
// Once again, force the peaceful object to hide its
// identity, and take on a fake name.
loadedItem1.Name = "Blargh";
var loadedItem2 = Deserialize<Item>(bytes);
Debug.Assert(loadedItem1.Name == loadedItem2.Name);
}
}
#region Serialization helpers
private static readonly IFormatter _formatter
= new BinaryFormatter();
public static byte[] Serialize(ISerializable item) {
using (var stream = new MemoryStream()) {
_formatter.Serialize(stream, item);
return stream.ToArray();
}
}
public static T Deserialize<T>(Byte[] bytes) {
using (var stream = new MemoryStream(bytes)) {
return (T)_formatter.Deserialize(stream);
}
}
#endregion
}
// Supercalifragilisticexpialidocious interface.
public interface IDomainObject {
Guid Id { get; }
}
// Holds all loaded instances using weak references, allowing
// the almighty garbage collector to grab our stuff at any time.
// I have no real data to lend on here, but I _presume_ that this
// wont be to overly evil since we use weak references.
public static class LoadedInstances<T>
where T : class, IDomainObject {
private static readonly Dictionary<Guid, WeakReference> _items
= new Dictionary<Guid, WeakReference>();
public static void Set(T item) {
var itemId = item.Id;
if (_items.ContainsKey(itemId))
_items.Remove(itemId);
_items.Add(itemId, new WeakReference(item));
}
public static T Get(Guid id) {
if (_items.ContainsKey(id)) {
var itemRef = _items[id];
return (T)itemRef.Target;
}
return null;
}
}
[DebuggerDisplay("{Id} {Name}")]
[Serializable]
public class Item : IDomainObject, ISerializable {
public Guid Id { get; private set; }
public String Name { get; set; }
// This constructor can be avoided if you have a
// static Create method that creates and saves new items.
public Item() {
Id = Guid.NewGuid();
LoadedInstances<Item>.Set(this);
}
#region ISerializable Members
public void GetObjectData(SerializationInfo info, StreamingContext context) {
// We're calling SerializationProxy to call GetById(this.Id)
// when we should be deserialized. Notice that we have no
// deserialization constructor. Fxcop will hate us for that.
SerializationProxy<Item, Guid>.PrepareSerialization(info, Id, GetById);
}
#endregion
public static Item GetById(Guid id) {
var alreadyLoaded = LoadedInstances<Item>.Get(id);
if (alreadyLoaded != null)
return alreadyLoaded;
// TODO: Load from storage container (database, cache).
// TODO: The item we load should be passed to LoadedInstances<Item>.Set
return null;
}
}