I'm trying to create a convenient serialized reference to asset objects. It serialized correctly, but deserialization fails with exception
"Error setting value to 'textAssetRef' on 'TestData'."
I've prepared a small test component for Unity to illustrate this:
using System;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using UnityEditor;
using UnityEngine;
public class Test : MonoBehaviour
{
public TestData testData;
public void Awake()
{
try
{
var sData = JsonConvert.SerializeObject(testData, Formatting.Indented);
UnityEngine.Debug.Log(sData);
testData = JsonConvert.DeserializeObject<TestData>(sData);
UnityEngine.Debug.Log("Done.");
}
catch (Exception x)
{
UnityEngine.Debug.LogError(x.Message);
}
}
}
[Serializable]
public class TestData
{
public TextAssetRef textAssetRef;
}
[Serializable]
[JsonConverter(typeof(Serialization))]
public class TextAssetRef : ObjectRef<TextAsset>
{
public TextAssetRef() { }
public TextAssetRef(TextAssetRef other) : base(other) { }
public TextAssetRef(TextAsset ta) : base(ta) { }
}
[Serializable]
public class ObjectRef<T> where T : UnityEngine.Object
{
public T obj;
public ObjectRef() { }
public ObjectRef(ObjectRef<T> other) { obj = other.obj; }
public ObjectRef(T obj) { this.obj = obj; }
public class Serialization : JsonConverter
{
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
var objRef = (ObjectRef<T>)value;
var jObject = new JObject { { "path", AssetDatabase.GetAssetPath(objRef.obj) } };
serializer.Serialize(writer, jObject);
}
public override bool CanConvert(Type objectType) { return objectType == typeof(ObjectRef<T>); }
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
var jObject = JObject.Load(reader);
var assetPath = jObject.GetValue("path").Value<string>();
return new ObjectRef<T> { obj = AssetDatabase.LoadAssetAtPath<T>(assetPath) };
}
}
}
To reproduce the error just create a new project with JSON.Net asset package in Unity, create empty object in scene, put this Test component on it and press Play.
What am I to do to get it deserialized correctly?
Related
I am using NUnit3 and trying to make use of TestFixtureSource in the following class hierarchy:
public class AtataTestFixtureData
{
public static IEnumerable FixtureParams
{
get
{
yield return new TestFixtureData(new AtataConfigContainer
{
AtataJsonConfig = new BaseAtataConfig()
});
}
}
}
[TestFixtureSource(typeof(AtataConfigContainer), nameof(AtataTestFixtureData.FixtureParams))]
public class AtataTestsWithDbBase : OzCsTestsWithDbBase, IAtataAbpTests
{
public AtataTestsWithDbBase()
{
}
public AtataTestsWithDbBase(AtataConfigContainer aAtataConfigContainer)
{
AtataAbpTestsAdapter = AtataAbpTestsAdapter.Instance;
AtataConfigContainer = aAtataConfigContainer;
}
}
public class SomeSiteComAuTestsBase : AtataTestsWithDbBase
{
public SomeSiteComAuTestsBase(AtataConfigContainer aAtataConfigContainer) : base(aAtataConfigContainer)
{
}
}
[TestFixture]
public class IndexTests : SomeSiteComAuTestsBase
{
/// <summary>
/// Class constructor.
/// </summary>
public IndexTests(AtataConfigContainer aAtataConfigContainer) : base(aAtataConfigContainer)
{
}
[Test]
public void Get()
{
//Arrange
//Act
IndexPageObject indexPage = Go.To<IndexPageObject>();
//Assert
}
}
When I run IndexTests.Get() I get the exception OneTimeSetUp: No suitable constructor was found but according to public IndexTests(AtataConfigContainer aAtataConfigContainer) : base(aAtataConfigContainer) I have the needed constructor.
What am I missing here?
You are getting this error because your IndexTests class has a constructor that takes parameters, but your TestFixtureSource is on a base class. The TestFixtureSource needs to be on your IndexTests. The TestFixtureSource attribute is not inherited.
I'm trying to use the MvvmCross Messenger plugin, but I simple get it to work.. it always return a "Null Reference Exception".
Here is the BaseViewModel I created to test it:
namespace TestProject.Core.ViewModels
{
public class BaseViewModel : MvxViewModel
{
private readonly IMvxMessenger _messenger;
public BaseViewModel(IMvxMessenger messenger)
{
messenger = _messenger;
}
public IMvxCommand TestMessageCommand
{
get { return new MvxCommand(DoTestMessage); }
}
private void DoTestMessage()
{
var message = new TestMessage(this, "Potato");
_messenger.Publish(message);
}
}
}
Here is the other ViewModel that should receive the message:
namespace TestProject.Core.ViewModels
{
public class HomeViewModel : MvxViewModel
{
private string _testMessage = string.Empty;
private readonly MvxSubscriptionToken _token;
public HomeViewModel(IMvxMessenger messenger)
{
_token = messenger.Subscribe<TestMessage>(OnTestMessage);
}
private void OnTestMessage(OnTestMessage testMessage)
{
_testMessage = testMessage.Result;
}
public ICommand ShowBasePageCommand
{
get { return new MvxCommand(() => ShowViewModel<BaseViewModel>()); }
}
}
}
And finally, here is the message:
namespace TestProject.Core.Messages
{
public class TestMessage
: MvxMessage
{
public QRCodeResultMessage(object sender, string result) : base(sender)
{
Result = result;
}
public string Result { get; private set; }
}
}
I bound a button on the HomePage to the "ShowBasePageCommand", and on the BasePage there is another button bound to the "TestMessageCommand".
Full Exception:
System.NullReferenceException: Object reference not set to an nstance of an object.
at TestProject.Core.ViewModels.BaseViewModel.DoTestMessage () [0x00014] in /Users/diegopatrocinio/Projects/Xamarin/TestProject/TestProject.Core/ViewModels/BaseViewModel.cs:49
at MvvmCross.Core.ViewModels.MvxCommand.Execute (System.Object parameter) [0x00009] in <69bce0378e8e413982d3b552d7e387a8>:0
at Xamarin.Forms.Button.Xamarin.Forms.IButtonController.SendClicked () [0x0000a] in C:\BuildAgent3\work\ca3766cfc22354a1\Xamarin.Forms.Core\Button.cs:121
at Xamarin.Forms.Platform.Android.ButtonRenderer+ButtonClickListener.OnClick (Android.Views.View v) [0x0000f] in C:\BuildAgent3\work\ca3766cfc22354a1\Xamarin.Forms.Platform.Android\Renderers\ButtonRenderer.cs:303
at Android.Views.View+IOnClickListenerInvoker.n_OnClick_Landroid_view_View_ (System.IntPtr jnienv, System.IntPtr native__this, System.IntPtr native_v) [0x00011] in /Users/builder/data/lanes/4009/3a62f1ea/source/monodroid/src/Mono.Android/platforms/android-25/src/generated/Android.Views.View.cs:1857
at at (wrapper dynamic-method) System.Object:1b16fb3a-f768-4a9f-8e2e-60f0085ed7fb (intptr,intptr,intptr)
Stack:
System.Diagnostics.Debugger.Mono_UnhandledException_internal() in
System.Diagnostics.Debugger.Mono_UnhandledException(System.NullReferenceException ex) in /Users/builder/data/lanes/4009/3a62f1ea/source/mono/mcs/class/corlib/System.Diagnostics/Debugger.cs:122
object.1b16fb3a-f768-4a9f-8e2e-60f0085ed7fb( arg0, arg1, arg2) in
TestProject.Core.ViewModels.BaseViewModel.DoTestMessage() in /Users/diegopatrocinio/Projects/Xamarin/TestProject/TestProject.Core/ViewModels/BaseViewModel.cs:49
Please note that your constructor has inverted the parameter and the class member:
public BaseViewModel(IMvxMessenger messenger)
{
messenger = _messenger;
}
Should be
public BaseViewModel(IMvxMessenger messenger)
{
_messenger = messenger;
}
In my case I need subscribe to TFS events (create/delete team project, workitem, checkin, iteration, areas) for realization some business logic. I based on this manual. Now I can catch only workitem and checkin events, but I need more (team project, iteration, areas). In this list, I did not find the right events.
using System;
using System.Collections.Generic;
using System.Diagnostics;
using Microsoft.TeamFoundation.Common;
using Microsoft.TeamFoundation.Framework.Server;
using Microsoft.TeamFoundation.Integration.Server;
using Microsoft.TeamFoundation.VersionControl.Server;
using Microsoft.TeamFoundation.WorkItemTracking.Server;
public class WorkItemChangedEventHandler : ISubscriber
{
public string Name
{
get { return "WorkItemChangedEventHandler"; }
}
public SubscriberPriority Priority
{
get { return SubscriberPriority.Normal; }
}
public Type[] SubscribedTypes()
{
var types = new List<Type>
{
typeof(Microsoft.TeamFoundation.WorkItemTracking.Server.WorkItemChangedEvent),// working
typeof(Microsoft.TeamFoundation.VersionControl.Server.CheckinNotification),// working
typeof(Microsoft.TeamFoundation.Integration.Server.ProjectCreatedEvent)// NOT working
};
return types.ToArray();
}
public EventNotificationStatus ProcessEvent(TeamFoundationRequestContext requestContext, NotificationType notificationType,
object notificationEventArgs, out int statusCode, out string statusMessage, out ExceptionPropertyCollection properties)
{
statusCode = 0;
properties = null;
statusMessage = String.Empty;
try
{
EventLog.WriteEntry("WorkItemChangedEventHandler", string.Format("Entity: {0} was modified", notificationEventArgs.GetType()));
}
catch (Exception ex)
{
EventLog.WriteEntry("WorkItemChangedEventHandler", ex.Message + ex.StackTrace);
}
return EventNotificationStatus.ActionPermitted;
}
}
I have one class for CheckinNotificationEventHandler:
public class CheckinNotificationEventHandler : ISubscriber
{
public Type[] SubscribedTypes()
{
return new Type[1] { typeof(CheckinNotification) };
}
public EventNotificationStatus ProcessEvent(TeamFoundationRequestContext requestContext, NotificationType notificationType, object notificationEventArgs, out int statusCode, out string statusMessage, out ExceptionPropertyCollection properties)
{
if (notificationType == NotificationType.Notification && notificationEventArgs is CheckinNotification)
{
...
}
return EventNotificationStatus.ActionPermitted;
}
}
and a second class for WorkItemChangedEventHandler:
public class WorkItemChangedEventHandler : ISubscriber
{
public Type[] SubscribedTypes()
{
return new Type[1] { typeof(Microsoft.TeamFoundation.WorkItemTracking.Server.WorkItemChangedEvent) };
}
public EventNotificationStatus ProcessEvent(TeamFoundationRequestContext requestContext, NotificationType notificationType, object notificationEventArgs, out int statusCode, out string statusMessage, out ExceptionPropertyCollection properties)
{
if (notificationType == NotificationType.Notification && notificationEventArgs is WorkItemChangedEvent)
{
...
}
return EventNotificationStatus.ActionPermitted;
}
}
From the Autofac documentation I can see how to get all registrations for a class T:
public T[] ResolveAll<T>()
{
return _container.Resolve<IEnumerable<T>>().ToArray();
}
But when I only have the Type available, how can I get the equivalent results?
public Array ResolveAll(Type service)
{
return _container.Resolve( ???
}
I am trying to implement a wrapper class which has a pre-defined interface.
EDIT
For quick reference, the answer from Matthew Watson (with relevant ideas from David L) is:
public Array ResolveAll(Type service)
{
var typeToResolve = typeof(IEnumerable<>).MakeGenericType(service);
return _container.Resolve(typeToResolve) as Array;
}
Here is an example. I've added asserts to prove that the types returned from ResolveAll<T>(this IContainer self) are the same (and in the same order) as those returned from ResolveAll(this IContainer self, Type type):
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using Autofac;
using Autofac.Core;
namespace AutofacTrial
{
public abstract class Base
{
public abstract string Name { get; }
public override string ToString()
{
return Name;
}
}
public sealed class Derived1: Base
{
public override string Name
{
get
{
return "Derived1";
}
}
}
public sealed class Derived2: Base
{
public override string Name
{
get
{
return "Derived2";
}
}
}
public sealed class Derived3: Base
{
public override string Name
{
get
{
return "Derived3";
}
}
}
static class Program
{
static void Main()
{
var builder = new ContainerBuilder();
builder.RegisterType<Derived1>().As<Base>();
builder.RegisterType<Derived2>().As<Base>();
builder.RegisterType<Derived3>().As<Base>();
var container = builder.Build();
var array1 = container.ResolveAll(typeof(Base));
var array2 = container.ResolveAll<Base>();
Trace.Assert(array1.Length == 3);
Trace.Assert(array2.Length == 3);
for (int i = 0; i < array1.Length; ++i)
{
Trace.Assert(array1[i].GetType() == array2[i].GetType());
Console.WriteLine(array1[i]);
}
}
public static T[] ResolveAll<T>(this IContainer self)
{
return self.Resolve<IEnumerable<T>>().ToArray();
}
public static object[] ResolveAll(this IContainer self, Type type)
{
Type enumerableOfType = typeof(IEnumerable<>).MakeGenericType(type);
return (object[]) self.ResolveService(new TypedService(enumerableOfType));
}
}
}
The underling implementation is the same
I also used Reflector to look at the implementation of Resolve<IEnumerable<T>>(), and it winds up doing this:
public static TService Resolve<TService>(this IComponentContext context, IEnumerable<Parameter> parameters)
{
return (TService) context.Resolve(typeof(TService), parameters);
}
which calls:
public static object Resolve(this IComponentContext context, Type serviceType, IEnumerable<Parameter> parameters)
{
return context.ResolveService(new TypedService(serviceType), parameters);
}
So the two must be equivalent, since they are implemented that way.
You can invoke _container.Resolve by calling your wrapped method via reflection (MSDN), but in doing so you will lose your compile-time type safety.
public class Container
{
public T[] ResolveAll<T>()
{
return _container.Resolve<IEnumerable<T>>().ToArray();
}
public object ResolveAllGeneric(Type t)
{
MethodInfo method = GetType().GetMethod("ResolveAll")
.MakeGenericMethod(new Type[] { t });
return method.Invoke(this, new object[] { });
}
}
I am having a model not in EF, but in plain text. I have to have the updated events handled for each of the model's properties so that i can log their changes.
Is there a way for this to be achieved.
Implement the INotifyPropertyChanged interface.
A simple example:
using System.ComponentModel;
public class MyModel : INotifyPropertyChanged
{
string _myProperty;
public event PropertyChangedEventHandler PropertyChanged;
public string MyProperty
{
get { return _myProperty; }
set
{
_myProperty = value;
NotifyPropertyChanged("MyProperty");
}
}
public void NotifyPropertyChanged(string info)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(info));
}
}
}
You can use it like...
public class Test
{
public static void Main()
{
var model = new MyModel();
model.PropertyChanged += new PropertyChangedEventHandler(LogChange);
model.MyProperty="apples";
model.MyProperty="oranges";
model.MyProperty="pears";
}
public static void LogChange(object sender, PropertyChangedEventArgs args)
{
Console.WriteLine(args.PropertyName + " has changed!");
Console.WriteLine("New value: "
+ sender.GetType().GetProperty(args.PropertyName)
.GetValue(sender, null));
}
}