I'm trying to test a custom model binder. As part of that process I am creating a new ModelBindingContext to pass into the BindModel method of the custom binder. The problem is that I need to set the MetaData property of the ModelBindingContext as a CachedDataAnnotationsModelMetadata object, but honestly am not sure how to instantiate the object.
The signature for the CachedDataAnnotationsModelMetadata object is:
public CachedDataAnnotationsModelMetadata(
CachedDataAnnotationsModelMetadata prototype,
Func<object> modelAccessor
)
Does anyone have an example of what the prototype and modelAccessor parameters are supposed to be?
Here's a snippet of the non-functional code.
// Assemble
var formCollection = new List<KeyValuePair<string, string>> {
new KeyValuePair<string, string> ("SomeRequiredProperty", "SomeValue")
};
var valueProvider = new System.Web.Http.ValueProviders.Providers.NameValuePairsValueProvider(formCollection, null);
var metadata = new System.Web.Http.Metadata.Providers.CachedDataAnnotationsModelMetadata(????, ????)
var bindingContext = new ModelBindingContext {
ModelName = "ClaimsModelBinderInputModel",
ValueProvider = valueProvider,
ModelMetadata = metadata
};
var actionContext = new HttpActionContext();
var httpControllerContext = new HttpControllerContext();
httpControllerContext.Request = new HttpRequestMessage(HttpMethod.Post, "http://localhost/someUri");
actionContext.ControllerContext = httpControllerContext;
var cmb = new ClaimsModelBinder();
// Act
cmb.BindModel(actionContext, bindingContext);
// Assert...
I've found a few examples around the net and SO of people interacting with this class, but no concrete example of implementing it.
Thanks in advance!
Update
Figured out ModelAccessor, it's just used to delay accessing the actual model until the model property is accessed. https://github.com/ASP-NET-MVC/aspnetwebstack/blob/master/src/System.Web.Http/Metadata/ModelMetadata.cs#L103
I'm still working on supplying a prototype object if anyone can help with that.
So I ended up sidestepping this issue entirely.
After inspecting the inheritance model, I realized that in this case I can get away with using the DataAnnotationsModelMetadataProvider rather than the CachedDataAnnotationsModelMetadata. The latter of these doesn't require the parameters necessary to setup caching, and since I don't need caching to test what I am testing, the standard DataAnnotationsModelMetadataProvider will suffice.
I was able to identify that I could take a different approach after reading through the source code for the metadata namespace.
https://github.com/ASP-NET-MVC/aspnetwebstack/tree/master/src/System.Web.Http/Metadata
That said, if anyone else can provide some clues to my original question, I would prefer to use the cached model object if possible.
Related
I have found a few other questions like this, however every single one has not been able to fix this problem I am having. I continuously get the same problem.
I have checked out this and that and multiple others! Every single thing I get the same problem.
I get the following exception:
The view at '~/Views/Email/Ticket.cshtml' must derive from WebViewPage, or WebViewPage<TModel>.
The code I am using is:
public static string RenderViewToString(string controllerName, string viewName, object viewModel)
{
HttpContextWrapper context = new HttpContextWrapper(HttpContext.Current);
RouteData routeData = new System.Web.Routing.RouteData();
routeData.Values.Add("controller", controllerName);
ControllerContext controllerContext = new ControllerContext(context, routeData, new RenderController());
RazorViewEngine razorViewEngine = new RazorViewEngine();
ViewEngineResult razorViewResult = razorViewEngine.FindView(controllerContext, viewName, string.Empty, false);
using (StringWriter writer = new StringWriter())
{
ViewDataDictionary viewData = new ViewDataDictionary(viewModel);
var viewContext = new ViewContext(controllerContext, razorViewResult.View, viewData, new TempDataDictionary(), writer);
razorViewResult.View.Render(viewContext, writer);
writer.Flush();
return writer.GetStringBuilder().ToString();
}
}
The RenderController is just:
private class RenderController : ControllerBase
{
protected override void ExecuteCore()
{
}
}
No matter what I have done, or different methods I have tried nothing seems to make this error go away, I have tried making the view inherit the objects but you can't have #model and #inherit in the same thing. Getting quite aggravated over this as I have followed pretty much everyone's instructions on other questions/posts but hasn't been able to help me.
Thanks to #JustinRusso he helped figure this out!
What seemed to happen was, my refernce to MVC 5.0.0.0 in my actual web project was different to my MVC version that I was referencing in my Common library which was at 4.0.0.1.
After I updated the version it worked. I feel stupid now looking back on it but that is what learning is all about!
Thanks to all for the help and suggestions!
I don't think you can mark comments as answers (at least from what I can tell) so just making it a bigger item so future people can find it.
I'm coming from Web Api so maybe this question will be a little weird.
I'm working in Visual Studio 2012
Scenario:
I've two WCF services right now (but in the future will be more) that accept the same object (suppose datacontract A with same datamembers)
I'm calling them in this way:
string url1 = "myURL1";
BasicHttpBinding binding1 = new BasicHttpBinding();
EndpointAddress address1 = new EndpointAddress(url1);
ServiceClient1 serviceClient1 = new ServiceClient1(binding1, address1);
string url2 = "myURL2";
BasicHttpBinding binding2 = new BasicHttpBinding();
EndpointAddress address2 = new EndpointAddress(url2);
ServiceClient2 serviceClient2 = new ServiceClient2(binding2, address2);
Question:
It is possible call them dinamically? In one only method in order to just change the urls?
Update
Is it possible call them without any reference of the type? Because I need use a common method in both and that method receives the same object and retrieves the same object.
T CreateServiceClient<T>(string url)
{
var binding = new BasicHttpBinding();
var endpointAddress = new EndpointAddress(url);
return (T) Activator.CreateInstance(typeof(T), new object[] {binding, endpointAddress});
}
Should be able to use it like so:
var client1 = new CreateServiceClient<ServiceClient1>("http://...");
var client2 = new CreateServiceClient<ServiceClient2>("http://...");
You can look into generics and try to constrain T to be more specific as this doesn't give you any type safety.
If you mean dynamically like you only have a Type, you can do this:
object CreateServiceClient(Type serviceClientType, string url)
{
var binding = new BasicHttpBinding();
var endpointAddress = new EndpointAddress(url);
return Activator.CreateInstance(serviceClientType, new object[] {binding, endpointAddress});
}
Just change object to be a more generic interface or class that all of your clients adhere to if you have one defined.
EDIT
Based on your updated question you will need to do one of two things
Implement an interface or a base class for your common services with the common methods you are needing to invoke. The above code will still work, just need to cast your new common type.
Use reflection to dynamically invoke the common method you are needing. This won't give you any compile time checks or errors and any issues will only be found during runtime.
I highly recommend #1, but if for some reason that is not possible, you can do #2. If you need to do #2, you could meet halfway between the two and implement a wrapper class that tries to union some of this functionality:
public class MyServiceWrapper
{
Type _serviceType;
public MyServiceWrapper(Type serviceType)
{
_serviceType = serviceType;
}
public object CreateInstance()
{
... code from above ...
}
public YourObject InvokeServiceMethod()
{
var instance = CreateInstance();
var methodInfo = _serviceType.GetMethod("MethodName");
return (YourObject) methodInfo.Invoke(instance, anyArguments);
}
}
I have a project similar(Almost identical) to Conference API project which is taking similar approach to the noted project for returning CollectionJson content. I am having difficulty Setting the Collection property of the ReadDocument (Line 30) as it does not have any setter. I could bypass this problem by doing the following change
public CollectionJsonContent(Collection collection)
{
var serializerSettings = new JsonSerializerSettings
{
NullValueHandling = NullValueHandling.Ignore,
Formatting = Newtonsoft.Json.Formatting.Indented,
ContractResolver = new CamelCasePropertyNamesContractResolver()
};
collection.Version = "1.0";
Headers.ContentType = new MediaTypeHeaderValue("application/vnd.collection+json");
using (var writer = new JsonTextWriter(new StreamWriter(_memoryStream)){CloseOutput = false})
{
//var readDocument = new ReadDocument(); {IReadDocument.Collection = collection};
var serializer = JsonSerializer.Create(serializerSettings);
serializer.Serialize(writer,collection);
writer.Flush();
}
_memoryStream.Position = 0;
}
Although above code compiles and to some extent sorts out the problem but then again I will have another problem of not being able to consume the JsonCollection content in my controller unit tests. Consider the following unit test code snippet:
using (var request = CreateRequest())
{
var controller = new TestController(DataService) {Request = request};
var temp = await controller.ListAsync(gridSearchData, sampleSearchData);
if ((temp is NotFoundResult) && (sampleCollection.Any()))
{
Assert.Fail("Controller did not return any result but query did");
}
var json = await temp.ExecuteAsync(cancellationTokenSource);
var readDocument = json.Content.ReadAsAsync<ReadDocument>(new[] {new CollectionJsonFormatter()}, cancellationTokenSource).Result;
}
Since I did not set the collection property of ReadDocument readDocument is always empty and I cant read its content.
How do you asynchronously read the contents of JsonCollection on the client side in WEB API projects?
To get a Clear picture of the approach look at the Conference Web Api
and the authors blog
OK all, this has been fixed. The Collection property is now settable again.
I have just pushed release 0.7.0 with this fix, a major naming refactoring as well as a nice improvement to serialization to not write out empty collections.
Please see the release notes for the changes (especially the naming as the package names and namespaces have changed)
As far as I see from your code, you do not serialize a ReadDocument object, but only a property of it (Collection), and then you try to deserialize that value into a new ReadDocument object.
A sample ReadDocument should serialize like this
"{"Collection": [1,2,3,4,5] }"
But you serialize collection, so you get
"[1,2,3,4,5]"
I recommend a surrogate class for serialization like this
class SerializableReadDocument
{
public Collection Collection { get; set; }
}
and update your serialization code like this
using (var writer = new JsonTextWriter(new StreamWriter(_memoryStream)){CloseOutput = false})
{
var readDocument = new SerializableReadDocument() { Collection = collection };
var serializer = JsonSerializer.Create(serializerSettings);
serializer.Serialize(writer, readDocument);
writer.Flush();
}
But, this will not resolve your problem when you try to deserialize your output since ReadDocument does not have a settable Collection property, deserialization will either fail, or return a ReadDocument object with an empty Collection.
You can use SerializableReadDocument if you like in your unit tests.
I am looking into this and will come up with a solution hopeful this weekend, which will either be to make it a public setter, or make the setter internal and have public ctor that accepts a collection.
Sorry for the difficulty.
I have a problem with casting/types and so on.
Firstly, my query is a follow on from another post here:
Initialize generic object from a System.Type
so to continue on from this question, how can I use the methods of my newly created object?
i.e. what I want to do is as follows:
Type iFace = typeof(IService1);
Type genericListType = typeof(System.ServiceModel.ChannelFactory<>).MakeGenericType(iFace);
object factory = Activator.CreateInstance(genericListType, new object[]
{
new BasicHttpBinding(),
new EndpointAddress("http://localhost:1693/Service.svc")
});
var channel = factory.CreateChannel();
by the way, although I am using this application for WCF, this is not a WCF problem
Try using a dynamic object? This allows you to call methods that might or might not exist.
Without dynamic objects:
object factory = Activator.CreateInstance(genericListType, new object[]
{
new BasicHttpBinding(),
new EndpointAddress("http://localhost:1693/Service.svc")
});
Type factoryType = factory.GetType();
MethodInfo methodInfo = factoryType.GetMethod("CreateChannel");
var channel = methodInfo.Invoke(factory) as YourChannelType;
I have a class which has the following constructor
public DelayCompositeDesigner(DelayComposite CompositeObject)
{
InitializeComponent();
compositeObject = CompositeObject;
}
along with a default constructor with no parameters.
Next I'm trying to create an instance, but it only works without parameters:
var designer = Activator.CreateInstance(designerAttribute.Designer);
This works just fine, but if I want to pass parameters it does not:
var designer = Activator.CreateInstance(designerAttribute.Designer, new DelayComposite(4));
This results in an MissingMethodException:
Constructor voor type
Vialis.LightLink.Controller.Scenarios.Composites.DelayCompositeDesigner
was not found
Any ideas here?
The problem is I really need to pass an object during construction.
You see I have a designer which loads all the types that inherit from the CompositeBase. These are then added to a list from which the users can drag them to a designer. Upon doing so an instance of the dragged is added to the designer. Each of these classes have custom properties defined on them:
[CompositeMetaData("Delay","Sets the delay between commands",1)]
[CompositeDesigner(typeof(DelayCompositeDesigner))]
public class DelayComposite : CompositeBase
{
}
When the user selects an item in the designer, it looks at these attributes in order to load up a designer for that type. For example, in the case of the DelayComposite it would load up a user control which has a label and a slider which allow the user to set the "Delay" property of the DelayComposite instance.
So far this works fine if I don't pass any parameters to the constructor. The designer creates an instance of the DelayCompositeDesigner and assigns it to the content property of a WPF ContentPresenter.
But since that designer needs to modify the properties of the selected DelayComposite
in the designer, I have to pass this instance to it. That is why the constructor looks lie this:
public DelayCompositeDesigner(DelayComposite CompositeObject)
{
InitializeComponent();
compositeObject = CompositeObject;
}
Suggestions are welcome
#VolkerK
The result of your code is this:
<---- foo
Vialis.LightLink.Controller.Scenarios.Composites.DelayCompositeDesignerVoid
.ctor()
Vialis.LightLink.Controller.Scenarios.Composites.DelayCompositeDesignerVoid
.ctor(Vialis.LightLink.Controller.Scenarios.Composites.DelayComposite)
param:Vialis.LightLink.Controller.Scenarios.Composites.DelayComposite
foo ---->
Leppie, you were correct, I had for some reason referenced the Composites assembly in my UI application... which is not something I should have done as I was loading it at runtime. The following code works:
object composite = Activator.CreateInstance(item.CompositType,(byte)205);
var designer = Activator.CreateInstance(designerAttribute.Designer, composite);
As you can see the code does not have knowledge of the DelayComposite type.
This solves the current problem, but introduces many new ones for what I want to achieve,
either way thank you and thank you to everyone who has replied here.
As for the following code, suggested by multiple people:
var designer = Activator.CreateInstance(
designerAttribute.Designer,
new object[] { new DelayComposite(4) }
);
The Activator.CreateInstance has a signature that looks like this:
Activator.CreateInstance(Type type, params object[] obj)
So it should accept my code, but I will try the suggested code
UPDATE:
I've tried this as suggested:
var designer = Activator.CreateInstance(designerAttribute.Designer, new object[] { new DelayComposite(4)});
The result is the same.
I would think that your call would need to be:
var designer = Activator.CreateInstance(designerAttribute.Designer, new object[] { new DelayComposite(4) });
Unless, of course, it is that, in which case the answer is not immediately obvious.
I think you are dealing with a Type mismatch.
Likely the assembly is referenced in different places, or they are compiled against different versions.
I suggest you iterate through the ConstructorInfo's and do a paramtype == typeof(DelayComposite) on the appropriate parameter.
Though I hate printf-like debugging ...
public static void foo(Type t, params object[] p)
{
System.Diagnostics.Debug.WriteLine("<---- foo");
foreach(System.Reflection.ConstructorInfo ci in t.GetConstructors())
{
System.Diagnostics.Debug.WriteLine(t.FullName + ci.ToString());
}
foreach (object o in p)
{
System.Diagnostics.Debug.WriteLine("param:" + o.GetType().FullName);
}
System.Diagnostics.Debug.WriteLine("foo ---->");
}
// ...
foo(designerAttribute.Designer, new DelayComposite(4));
var designer = Activator.CreateInstance(designerAttribute.Designer, new DelayComposite(4));
What does that print in the visual studio's output window?
If you want to call this contructor...
public DelayCompositeDesigner(DelayComposite CompositeObject)
...just use this:
var designer = Activator.CreateInstance(typeof(DelayCompositeDesigner), new DelayComposite(4));
or
var designer = Activator.CreateInstance<DelayCompositeDesigner>(new DelayComposite(4));
I had a similar issue, however my problem was due to the visibility of the constructor. This stack overflow helped me:
Instantiating a constructor with parameters in an internal class with reflection
I discovered another way of creating an instance of an object without calling the constructor at all while answering another question on SF.
In the System.Runtime.Serialization namespace there is a function FormatterServices.GetUninitializedObject(type) that will create an object without calling constructor.
If you look at that function in Reflector you will see it is making an external call. I don't know how black magic is actually happening under the hood. But I did prove to myself that the constructor was never called but the object was instantiated.
When I encountered this problem, I was using a method that returned the parameter list to plug in to Activator.CreateInstance and it had a different number of arguments than the constructor of the object I was trying to create.
In my case, this code work good with .NET Framework but does not work in .NET Core 3.1. It throws ExecutionEngineException which is uncatchable. But when I change target to .NET 5, it works perfectly. Hope this help some one.
Type type = assembly.GetType(dllName + ".dll");
Activator.CreateInstance(type ), new Stream[] { stream };
You can use the following overload on CreateInstance:
public static Object CreateInstance(
Type type,
Object[] args
)
And in your case it'd be (I think):
var designer = Activator.CreateInstance(
typeof(DelayCompositeDesigner),
new object[] { new DelayComposite(4) }
);
I found a solution to the problem, I was struggling with the same issue.
Here is my activator:
private void LoadTask(FileInfo dll)
{
Assembly assembly = Assembly.LoadFrom(dll.FullName);
foreach (Type type in assembly.GetTypes())
{
var hasInterface = type.GetInterface("ITask") != null;
if (type.IsClass && hasInterface)
{
var instance = Activator.CreateInstance(type, _proxy, _context);
_tasks.Add(type.Name, (ITask)instance);
}
}
}
And here is my class to activate, note that I had to change the constructor params to objects, the only way I could get it to work.
public class CalculateDowntimeTask : Task<CalculateDowntimeTask>
{
public CalculateDowntimeTask(object proxy, object context) :
base((TaskServiceClient)proxy, (TaskDataDataContext)context) { }
public override void Execute()
{
LogMessage(new TaskMessage() { Message = "Testing" });
BroadcastMessage(new TaskMessage() { Message = "Testing" });
}
}