WebAPI only binds first parameter - asp.net-mvc-4

Dead simple question, possibly not so simple answer. Posting JSON.
public void Post(Model1 model1, Model2 model2)
{}
model1 is populated but not model2 (null).
public void Post(Model2 model2, Model1 model1)
{}
Now, model2 is populated but not model1 (null).
Why?
Edit
The reason for two parameters? Model2 used to be referenced from Model1, but that didn't work. That's when I split them up.
Edit
Right. Thanks marcind for the answer to the question above. Now for the reason the original setup didn't work. I'm not the forms universe anymore. I post Json. If you have child objects in your model then post child objects in your json.
Given
class ProductEditModel {
public string Name {get; set;}
}
class UserEditModel {
public string User {get; set;}
public ProductEditModel Product {get; set;}
}
the following
{"user": "philip", "product.name": "barbie"}
is not going to work. You'd even get an error if you in js try and setup the sematic equivalent
{user: "philip", product.name: "barbie"}
Neither of the following work either, I don't know why they would:
{"user": "philip", "productname": "barbie"}
{"user": "philip", "product_name": "barbie"}
What does work and which should be obvious holding my profession is
{"user": "philip", "product": {"name": "barbie"}}
Please kick me.
Beware! The following will not work given corresponding edit to the model above.
{"user": "philip", "ProductEditModel": {"name": "barbie"}}

Not sure which version you are using, but the general rule we have settled on is that when binding complex types Web API considers the entire body of the request to represent a single entity and thus a single action parameter. In your case if you want to bind multiple Models you could introduce a custom binding object or alternatively you could bind to a Model[] or some other collection type.

Related

Send JSON with list to Controller

I am trying to send following JSON to my controller:
[
{
"collection": "col1",
"uuid": [
"11:22:33:44:55:66",
"11:22:33:44:55:66"
]
},
{
"collection": "test"
}
]
Every object "collection" contains a list of strings symbolized by the uuids.
My model looks like this:
public class DummyDeviceApiModel {
[Display(Name = "UUID")]
[StringLength(36)]
public List<string> uuid {get; set;}
[Display(Name = "Collection")]
public string collection {get; set;}
}
and my controller function like this:
public async Task<ActionResult<DummyDeviceModel>> PostCreateDummyDevice(List<DummyDeviceApiModel> ddpm)
What works is when I just send the collection part, but the UUID with its list makes problem:
System.InvalidCastException: Unable to cast object of type
'System.Collections.Generic.List`1[System.String]' to type
'System.String'
.
Any idea what I am doing wrong? The issue seems to be the "second" list in the model.
Thanks
Stephan
[StringLength(36)] is a validation attribute for type string not arrays of string or List<string>. The exception is probably happening behind the scenes as it tries to validate on your property since it is of type List<string>. If you want to do what I think you do, create a custom validation on your list to make sure you only have strings in your list which are less than or equal to a length of 36, then you need to either implement IValidatableObject on your model (class) or create a custom Validation Attribute for validation by creating a class which inherits from ValidationAttribute. You can read more about how to implement this interface and/or create a custom attribute at MSDN: (https://learn.microsoft.com/en-us/aspnet/core/mvc/models/validation?view=aspnetcore-3.1). As a test to see if it is indeed the StringLength attribute which is throwing the exception, comment out that particular Validation Attribute in your code and see if it runs without error.

Unwanted loading of relations in EF Core

Hello,
I have problem with EF Core feature - It automatically binds related entities together when the entities are somewhere independently attached to current dbCotnext.
Let's assume following two entities:
public class Seller {
public Guid Id {get;set;}
public List<Product> Products {get;set;}
}
public class Product {
public Guid Id {get;set;}
public Guid SellerId {get;set;}
public Seller Seller {get;set;}
}
And some code in the controller (just for imagination):
var seller = DbContext.Sellers.FirstOrDefault(e => e.Id == someId);
var products = DbContext.Products.All(t => t.SellerId == someId);
return StatusCode(200, products);
The returned JSON will be like
[
{
"id": "1234",
"sellerId": "5678",
"seller": {
"id" : "5678",
"products": ["(muted reference loop exception from json converter here.)"]
}
}
]
But I don't want the Seller to be included in each Product. If I did, I'd call Products.Include(...) for that or something else.
I don't want to crawl through entities and null the navigation properties.
I don't want to hide it with [JsonIgnore] because sometimes the relation must be included.
I also don't want to manually detach every entity all the time when this happens.
The question is, is there any way to disable or work around this behaviour?
Thanks
No, you can't/shouldn't. You need separate dto class(es).
Newtonsoft.Json is responsible for object serialization, it decides which properties must [not] be serialized. You can control it's behavior only using it's attributes. You can't control it from EF :)
And as soon as you wish sometimes to include property and sometimes not - you need two different classes (each with correct attributes). Everything other is a hack. DTO, Automapper and all this stuff - you are welcome.
BTW, having different class(es) for external API and internal data storage allows you to easily change one without breaking other (in future).
Have you tried this configuration on Startup class:
public void ConfigureServices(IServiceCollection services)
{
// Add framework services.
services.AddMvc().AddJsonOptions(a => a.SerializerSettings.ReferenceLoopHandling = Newtonsoft.Json.ReferenceLoopHandling.Ignore);
// other code
}
You have to change model class and using nullable type.
public class Product {
public Guid Id {get;set;}
public Guid SellerId {get;set;}
public Seller? Seller {get;set;}
}

Problems with EF-Agnostic design consumed by WCF service.

I am trying to set up EF to work on WCF and keeping the domain class models EF Agnostic.
The code is organized into 3 projects. (I am taking a stab a DDD - I am very new to it but am looking forward t learning more)
Project: QA - Domain Layer. Contains the DataContract models/entities.
References
QA.Data
Project: QA.Data - Data Layer. Contains the context and EDMX (code generation stragtegy = "none")
References
Entity Framework/System.Data.Entity
Project: QA.Repository - Data Access/Repository. Contains the repository classes
References
QA [Domain Layer]
QA.Data [Data Layer]
Entity Frame/System.DataEntity
My understanding is that the domain layer can reference the data layer but the data layer should never reference the domain. The problem that this presents is that my Domain Models/Classes are defined in the Domain layer but the Context which creates and returns them is in the Data layer. In order for my context to know to return a "Widget" object it would need a reference to the Domain layer which defined the "Widget"
My (failed) solution : My solution was to create interfaces for each Domain Model and place them in the data layer. The context would return ... IdbSet ... These interfaces would, in turn, be implemented by the Domain Models, therefore keeping my data layer from directly needing to reference my domain (which causes illegal circular references anyway). The domain models were originally contructed using "ADO.NET DbContext Generator w/WCF Support" T4 templates. This process resulted in the inclusion of the [KnownType(typeof(IWidgetPiece))] at the beginning of of the widget class defin ition. (A Widget has a navigation property ... ICollection ...)
The problem appears when I attempt to access the service, I get the following error
'QA.Data.IWidgetPiece' cannot be added to list of known types since
another type 'System.Object' with the same data contract name
'http://www.w3.org/2001/XMLSchema:anyType' is already present. If
there are different collections of a particular type - for example,
List and Test[], they cannot both be added as known types.
Consider specifying only one of these types for addition to the known
types list.
I can change these to the concrete implementations ... [KnownType(typeof(WidgetPiece))] ... but I continue to get this error because the navigation property they are referring to is still returning an IWidgetPiece interface type which it MUST do in order to satify the interface implementation.
I am trying to figure out how to keep things appropriately divided and still have the context returning what it should. the context returning Interfaces still doesn't "sit" right with me for this and other reasons but I cannot think of another way to do this, and even this is presenting the aforementioned issue. HELP!
Some code to hopefully clarify my previous ramblings ...
namespace QA.Data
{
public interface IWidgetPiece
{
String ID { get; set; }
}
public interface IWidget
{
String ID { get; set; }
ICollection<IWidgetPiece> Pieces;
}
public partial class WidgetEntities : DbContext
{
IDbSet<IWidget> Widgets { get; set; }
IDbSet<IWidgetPiece> WidgetPieces { get; set; }
}
}
namespace QA
{
[KnownType(typeof(IWidgetPiece))]
// [KnownType(typeof(WidgetPiece))]
[DataContract(IsReference = true)]
public partial class Widget : QA.Data.IWidget
{
[DataMember]
public String ID { get; set; }
[DataMember]
public virtual ICollection<IWidgetPiece> Pieces { get; set; }
}
[DataContract(IsReference = true)]
public partial class WidgetPiece : QA.Data.IWidgetPiece
{
[DataMember]
public string ID { get; set; }
}
}
namespace QA.Repository
{
public class WidgetRepository
{
public List<Widget> GetWidgetbyID(String sId)
{
WidgetEntities context = new WidgetEntities();
List<IWidget> objs = context.Widgets.Where(b => b.ID == "78").ToList();
List<Widget> widgetList = new List<Widget>();
foreach (var iwidget in widgetList)
widgetList((Widget)iwidget);
return widgetList;
}
}
}
Do you really want / need two separate models i.e. your data access layer model (edmx) and your "real" domain model? The whole point of an ORM framework like EF is so you can map your domain model to your database tables, using mappings between the physical (database) conceptual model.
Since EF4.1, you can construct your domain model and then in your data access layer map that to your database directly using a fluent API. You can also elect to reverse-engineer your POCO domain model from a database if you want to quickly get up an running.
It just seems a bit of unnecessary complexity to create an entire EF class model, only to then have to map it again into another class model (which will most likely be fairly close to the EF-generated one).

help me understand the method Validator.TryValidateObject()

this is the method definition:
public static bool TryValidateObject(
Object instance,
ValidationContext validationContext,
ICollection<ValidationResult> validationResults,
bool validateAllProperties
)
what i am confused is the validateAllProperties parameter, I understand when it is true-validate all properties.
What about when it is false, not validate all properties, but which property will be validated?
See here for a good answer:
http://connect.microsoft.com/VisualStudio/feedback/details/605635/missleading-parametername-validateallproperties-in-validator-try-validate-componentemodel-dataannotations
It seems that when validateAllProperties is set to false that only the RequiredAttribute is validated.
When the property is false the Validator should validate each of the properties on the object that have a ValidationAttribute applied to them. This can include any of these attributes: CustomValidationAttribute, DataTypeAttribute, RangeAttribute, RegularExpressionAttribute, RequiredAttribute, and StringLengthAttribute, along with any other attributes that derive from ValidationAttribute.
See the MSDN library on the TryValidateObject method for more information.
In the following example, Foo should be validated, while Bar should not.
public class Example
{
[Required(ErrorMessage = "Foo is a required property.")]
public object Foo { get; set; }
public object Bar { get; set; }
}
I also don't fully understand it but after struggling with Unit Testing custom validators written by me I noticed one interresting thing.
When I launched my tests without this parameter (so by default it was false), my custom validators were omitted! if I set it to true, they were taken into account in my tests and now I can happily continue TDD. Hope this helps you a bit.
Arjen is right, only the Required attribute is validated when the validateAllProperties parameter is false.
I wrote a post about OData validation using DataAnnotations and I found the same issue.
http://blog.jorgef.net/2011/01/odata-dataannotations.html

WCF DataContract Upcasting

I'm trying to take a datacontract object that I received on the server, do some manipulation on it and then return an upcasted version of it however it doesn't seem to be working. I can get it to work by using the KnownType or ServiceKnownType attributes, but I don't want to roundtrip all of the data. Below is an example:
[DataContract]
public class MyBaseObject
{
[DataMember]
public int Id { get; set; }
}
[DataContract]
public class MyDerivedObject : MyBaseObject
{
[DataMember]
public string Name { get; set; }
}
[ServiceContract(Namespace = "http://My.Web.Service")]
public interface IServiceProvider
{
[OperationContract]
List<MyBaseObject> SaveMyObjects(List<MyDerivedObject> myDerivedObjects);
}
public class ServiceProvider : IServiceProvider
{
public List<MyBaseObject> SaveMyObjects(List<MyDerivedObject> myDerivedObjects)
{
... do some work ...
myDerivedObjects[0].Id = 123;
myDerivedObjects[1].Id = 456;
myDerivedObjects[2].Id = 789;
... do some work ...
return myDerivedObjects.Cast<MyBaseObject>().ToList();
}
}
Anybody have any ideas how to get this to work without having to recreate new objects or using the KnownType attributes?
I think that your problem is that you are trying to send over a generic list.
It will work if you encapsulate the list in an object. That is create an object with a single public property which is the generic list.
You also need to make sure that all classes that are not used directly in the contract are marked as serializable.
If you want to return the derived objects then there will always be a round trip because the client and the service are separate. In order for the client to update its own list of MyBaseObjects it has to deserialize the list of MyDerivedObjects that came from the server.
The use of KnownType and/or ServiceKnownType is needed because this leads to the addition of that type information into WSDL, which is in turn used by the client to deserialize the messages to the correct type.
For starters, a useful tool for testing the scenario you've described: http://www.wcfstorm.com
You might try creating a DataContractSurrogate (IDataContractSurrogate) and returning your base type for the call to GetDataContractType. I'm not really sure that's how it was intended to be used so you still may be better of with "the extra work", but maybe I don't understand the scope of that extra work.
One of the problems with WCF (and .net remoting) is that it they tries to make “message passing” look like method calls.
This fall down when you try to use too many “oop” type designs.
The fact that the messages are
represented by .net classes, does not
make all of their behaviour like .net
class.
See this, and this, for more on the problem of Leaking Abstraction.
So you need to start thinking about message passing not object when designing your WCF interfaces, or you will hit lots of problems like this.