ASP.NET MVC 4 ApiController doesn't serialize all properties - asp.net-mvc-4

I'm testing the new ApiController in asp.net mvc 4 beta but when I try to return an class that looks like the following only a few properties gets serialized?
public class PageModel : IPageModel {
public string Id { get; set; }
public virtual IPageMetadata Metadata { get; private set; }
public PageModel() {
Metadata = new PageMetadata();
}
}
this is the code in my api controller
// GET /api/pages/5
public PageModel Get(string id) {
return new PageModel { Id = "pages/1", Metadata = {Name = "Foo"} };
}
and this is the result
{
Id: "pages/1",
Parent: null
}
Is it possible to get the complete object and not only a few things?

Readonly properties are not serialized. Make the setter of the Metadata property public if you want it to be serialized. I think that this behavior is normal for input parameters but not for output which is your case. IMHO it's a bug that could be workarounded by using a JSON serializer which supports this but maybe they will fix it before the final release and allow readonly properties to be serialized for output parameters.
Actually it's not a big pain, because you should be using view models anyway, so simply map your domain model to a view model and have your method return this view model which will contain only the properties that you need to actually expose to the client. This view model will contain properties with public getters and setters.

Related

Cannot create a DbSet for 'Model' because this type is not included in the model for the context

I do a Generic and using DI
so I create a empty class
public class DBRepo
{
}
and my model class to inheriting class DBRepo
public partial class UserAccount : DBRepo
{
public int Id { get; set; }
public string Account { get; set; }
public string Pwd { get; set; }
}
then this is a Interface to do CRUD
public interface IDBAction<TEntity> where TEntity : class,new()
{
void UpdateData(TEntity _entity);
void GetAllData(TEntity _entity);
}
public class DBService<TEntity> : IDBAction<TEntity> where TEntity : class,new()
{
private readonly CoreContext _db;
public DBService(CoreContext _db)
{
this._db = _db;
}
public void UpdateData(TEntity _entity)
{
this._db.Set<TEntity>().UpdateRange(_entity);
this._db.SaveChanges();
}
public void GetAllData(TEntity _entity)
{
var x = this._db.Set<TEntity>().Select(o => o).ToList();
}
}
And I Dependency Injection Service Provider in constructor
this.DBProvider = new ServiceCollection()
.AddScoped<IDBAction<DBRepo>, DBService<DBRepo>>()
.AddScoped<DBContext>()
.AddDbContext<CoreContext>(options => options.UseSqlServer(ConnectionString))
.BuildServiceProvider();
last step I Get Services
DBProvider.GetService<IDBAction<DBRepo>>().GetAllData(new UserAccount());
I will get a error message same with title
or I change to
DBProvider.GetService<IDBAction<UserAccount>>().GetAllData(new UserAccount());
I'll get other message
Object reference not set to an instance of an object.'
but the void UpdateData() is can work,
so how to fix GetAllData() problem?
The error simply is because the class you're using here UserAccount has apparently not been added to your context, CoreContext. There should be a property there like:
public DbSet<UserAccount> UserAccounts { get; set; }
Regardless of whether you end up using the generic Set<T> accessor, you still must defined a DbSet for the entity on your context.
That said, you should absolutely not be creating your own service collection inside your repo. Register your context and your repo with the main service collection in Startup.cs and then simply inject your repo where you need it. The DI framework will take care of instantiating it with your context, as long as you have a constructor that takes your context (which you seem to).
And that said, you should ditch the repo entirely. It still requires a dependency on Entity Framework and doesn't do anything but proxy to Entity Framework methods. This is just an extra thing you have to maintain and test with no added benefit.

Dapper and DAL Where must i place my validation

I am started with my first Dapper Dal project.
I have three projects:
- Website (MVC)
- DataLayer (Dapper)
- Model (Poco Classes)
I want to add validation to my model but i also want to use clean poco classes for my datalayer. My datalayer use dapper to map my poco classes to the database.
I have searched the internet but i can't find a good answer.
My question is:
Where do i add my validation?
- In a seppetated project with classes that extend my poco classes or is there a different way?
If you want a clean separation between your DAL classes and your MVC classes, then you can do just that by, for instance, using ViewModels in your MVC-project. The ViewModel would have the properties and validations that works best with what you are presenting in the browser. Your controller would be responsible for mapping the data between the DAL classes and the ViewModels. Automapper is a very good tool for just that.
It would look a bit like the following:
DAL:
public class MyDapperClass
{
public int Id { get; set; }
public string SomeProperty { get; set; }
}
ViewModel:
public class MyViewModelClass
{
public int Id { get; set; }
[StringLength(50),Required]
public string SomeProperty { get; set; }
}
Controller:
// using AutoMapper;
public class MyController : Controller
{
public MyController()
{
// Set up AutoMapper to be able to map your class
Mapper.CreateMap<MyDapperClass, MyViewModelClass>();
}
public ActionResult MyAction()
{
var dalObject = DAL.GetObject();
var viewModel = Mapper.Map<MyViewModelClass>(dalObject);
return View(viewModel);
}
}

How to hide Navigation properties when creating JSON response using EF 4?

I am developing a Web Services application, and when I get from my EF model - for example a list of my "User" object - I get the fields of my table PLUS the Foreign Keys, etc?
Does any know how to avoid these? I have been trying to modify the poco template but without luck.
namespace JOhn.DataAccess
{
[Serializable()]
[DataContract(Namespace = "")]
public class CustomEntityObject : EntityObject
{
//[DataMember]
[Browsable(false)]
[XmlIgnore]
[SoapIgnore]
public new EntityKey EntityKey { get; set; }
}
}
A common solution is to use the DTO ("Data Transfer Object") pattern.
Define a new class called UserDto which contains just the fields you want to expose over your API, then copy the values from your User entity object to a UserDto instance.
Packages like AutoMapper can be used to avoid writing the value copying code.
There is a
[ScriptIgnore]
Attribute as well, it works well with Microsoft's default JavaScript Serializer.
namespace AutoFX.DataAccess
{
[Serializable()]
[DataContract(Namespace = "")]
public class CustomEntityObject : EntityObject
{
//[DataMember]
[Browsable(false)]
[XmlIgnore]
[SoapIgnore]
[ScriptIgnore]
public new EntityKey EntityKey { get; set; }
}
}
try creating a model class and bind the data of the user object using this model. This will help to overcome the dependencies ..
Another way to do it without creating DTO's is to use JSON.Net in your MVC Application. Then your object could look like:
namespace John.DataAccess
{
[Serializable()]
[DataContract(Namespace = "")]
public class CustomEntityObject : EntityObject
{
//[DataMember]
[Browsable(false)]
[XmlIgnore]
[SoapIgnore]
[JsonIgnore]
public new EntityKey EntityKey { get; set; }
}
}

Why does ASP.NET MVC assumes that view will have matching input and output types?

ASP.NET MVC (or rather Html.Helpers and base page implementation) assumes that there will be one type for both rendering and posting (namely Model).
This is a violation of ISP, isn't it?
I am tempted to derive my Edit views (those that have different render-data, and post-data) from a custom EditPageBaseView<TViewModel, TFormData>.
The problem is I want my validation and post work against FormData instance (stored inside ViewModel), but MVC assumes that entire ViewModel will be POSTed back.
Is there an OOB way to facilitate that? (I didn't find one if there is).
Is it a bad idea (in concept) to have separate data types for different operations exposed by a service (a view in this case).
I tend to follow the CQRS model when constructing my view models. All rendering is done with ViewModel classes and all posting back is done with Command classes. Here's a contrived example. Let's say we have a View with a small form for creating users.
The ViewModel and Command classes looks like this:
public abstract class ViewModel {}
public abstract class Command: ViewModel
public class CreateUserViewModel : ViewModel
{
public string Username { get; set; }
public string Password { get; set; }
public string PasswordConfirm { get; set; }
}
public class CreateUserCommand : Command
{
public string Username { get; set; }
public string Password { get; set; }
public string PasswordConfirm { get; set; }
}
The UserController creates a CreateUserViewModel as the model for the Get request and expects a CreateUserCommand for the Post request:
public ActionResult CreateUser()
{
// this should be created by a factory of some sort that is injected in
var model = new CreateUserViewModel();
return View(model);
}
[HttpPost]
public ActionResult CreateUser(CreateUserCommand command)
{
// validate and then save the user, create new CreateUserViewModel and re-display the view if validation fails
}
Model binding takes care of ensuring that the properties of the Posted CreateUserCommand are populated properly, even though the Get View is bound to a CreateUserViewModel.
They don't have to match, but they do match by default.
If you don't want them to match, you can specify a different model in your Form or ActionLink:
Example of a Mismatch using Razor and C#:
Index.chtml:
#model FirstModel
<div>
#using (Html.BeginForm("Action", "ControllerName", new { ParameterName = new SecondModel { First = "First", Second = "Second" } }, FormMethod.Post)) {
<input type="submit" value="Submit Button" />
}
</div>
The Controller:
public class ControllerName : Controller {
public ActionResult Index() {
return View(new FirstModel());
}
public ActionResult Action(SecondModel ParameterName) {
return View() // Where to now?
}

Default parameter value in MVC 4 Web API

I am curious why the ApiController handles default parameter values on actions differently than a 'regular' Controller.
This code works just fine, request to /Test means page gets value 1
public class TestController : Controller
{
public ActionResult Index(int page = 1)
{
return View(page);
}
}
This code doesn't work when a request is made to /api/Values. It fails with:
"The parameters dictionary contains a null entry for parameter 'page' of non-nullable type 'System.Int32' for method 'System.Collections.Generic.IEnumerable`1[System.String] Get(Int32)' in 'MvcApplication1.Controllers.Controllers.ValuesController'. An optional parameter must be a reference type, a nullable type, or be declared as an optional parameter."
public class ValuesController : ApiController
{
public IEnumerable<string> Get(int page = 1)
{
return new string[] { page.ToString() };
}
}
Any hints on why this is?
Try adding the [FromUri] or [FromForm] parameter attribute.
public class ValuesController : ApiController
{
public IEnumerable<string> Get([FromUri]int page = 1)
{
return new string[] { page.ToString() };
}
}
Mike Stall has two good posts about parameter binding in Webapi which does not work as it does in ASP MVC. The big problem to get used to is that you can only read the request body once in your pipeline. So if you need to read more than 1 complex object as a parameter, you probably need to resort to ModelBinding by parameter. I had a problem similar to yours when I was reading the content body earlier in the pipeline for logging purposes and did not realize about the read once restriction above and had to solve with my own custom model binder.
Explains model binding at http://blogs.msdn.com/b/jmstall/archive/2012/04/16/how-webapi-does-parameter-binding.aspx and then suggests a way to make WebAPI model binding more like ASP MVC http://blogs.msdn.com/b/jmstall/archive/2012/04/18/mvc-style-parameter-binding-for-webapi.aspx
Try defining as Nullable<T>:
public class ValuesController : ApiController
{
public IEnumerable<string> Get(int? page = 1)
{
return new string[] { page.ToString() };
}
}