I've noticed (even in Web API 2.1) that deep parameter types get filled (processed by the model binder) only on the first level. That is :
public class Person
{
public string Name { get; set; }
public PersonDetails Details { get; set; }
}
public class PersonDetails
{
public string Address { get; set; }
public int Age { get; set; }
}
// ...
public class PersonController : ApiController
{
[HttpPost]
public void ProcessPerson(Person person)
{
// person.Name is filled in correctly
// person.Details.Address and person.Details.Age are not filled in correctly. That is, they have default values (null and 0)
}
}
Is there a simple solution for this problem, except flatting out the Person class like so ?
public class PersonData
{
public string Name { get; set; }
public string Address { get; set; }
public int Age { get; set; }
}
Later edit 1 :
If I flatten the Person class I get all the data correctly
The request is made by POST (and not GET) because I need to ensure there is no caching and since the operation alters state it would be semantically incorrect to use GET
Related
I am using aspnetboilerplate and added below configuration in preintiliaze in module. I have also added data annotation Audited to my entity but still it is not working. My entity is inheriting from AuditedEntity as don't need deleted feature. Please help
Configuration.EntityHistory.IsEnabled = true; Configuration.EntityHistory.Selectors.Add(new NamedTypeSelector("Abp.AuditedEntities", type => typeof(AuditedEntity).IsAssignableFrom(type)));
I have taken reference from here Can't enable Entity History in ASP.NET Zero
Below is entity definition
[Audited]
public partial class QuestionResponse : AuditedEntity<long>
{
public long ApplicationId { get; set; }
public long QuestionId { get; set; }
public string Response { get; set; }
public string Remark { get; set; }
public bool IsActive { get; set; }
public Application Application { get; set; }
public AbpUsers CreatorUser { get; set; }
public AbpUsers LastModifierUser { get; set; }
public Question Question { get; set; }
}
AuditedEntity<long> is not assignable to AuditedEntity.
Add a selector based on the interface IAuditedEntity instead.
Configuration.EntityHistory.Selectors.Add(
new NamedTypeSelector("Abp.AuditedEntities", type =>
// typeof(AuditedEntity).IsAssignableFrom(type)));
typeof(IAuditedEntity).IsAssignableFrom(type)));
Reference
From aspnetboilerplate/aspnetboilerplate's AuditedEntity.cs:
public abstract class AuditedEntity : AuditedEntity<int>, IEntity
{
}
public abstract class AuditedEntity<TPrimaryKey> : CreationAuditedEntity<TPrimaryKey>, IAudited
{
...
}
I have around 50 master tables that requires simple and straight forward CRUD operations, my tables are already available in the sql database.
My question is how to make it generic so that I dont need to create manually each individual page for master tables. I saw some ABP CRUDEntityAscyn classes in Boilerplate framework, but I am wondering how to bring it at Presentation layer (.cshtml).
If you need to create an application service that will have Create, Update, Delete, Get, GetAll methods for a specific entity, you can inherit from CrudAppService (or AsyncCrudAppService if you want to create async methods) class to create it easier. CrudAppService base class is generic which gets related Entity and DTO types as generic arguments and is extensible which allows you to override functionality when you need to customize it.
public class Task : Entity, IHasCreationTime
{
public string Title { get; set; }
public string Description { get; set; }
public DateTime CreationTime { get; set; }
public TaskState State { get; set; }
public Person AssignedPerson { get; set; }
public Guid? AssignedPersonId { get; set; }
public Task()
{
CreationTime = Clock.Now;
State = TaskState.Open;
}
}
[AutoMap(typeof(Task))]
public class TaskDto : EntityDto, IHasCreationTime
{
public string Title { get; set; }
public string Description { get; set; }
public DateTime CreationTime { get; set; }
public TaskState State { get; set; }
public Guid? AssignedPersonId { get; set; }
public string AssignedPersonName { get; set; }
}
public class TaskAppService : AsyncCrudAppService<Task, TaskDto>
{
public TaskAppService(IRepository<Task> repository)
: base(repository)
{
}
}
public interface ITaskAppService : IAsyncCrudAppService<TaskDto>
{
}
public class TaskAppService : AsyncCrudAppService<Task, TaskDto>, ITaskAppService
{
public TaskAppService(IRepository<Task> repository)
: base(repository)
{
}
}
calling webapi from client code:
var _editionService = abp.services.app.edition
_editionService.deleteEdition({
id: edition.id
}).done(function () {
getEditions();
abp.notify.success(app.localize('SuccessfullyDeleted'));
});
read for more > https://aspnetboilerplate.com/Pages/Documents/Application-Services#crudappservice-and-asynccrudappservice-classes
I have a route on my WebAPI project that accepts an object as input ExportPostData. ExportPostData has a property called "contract" of type Contract which was successfully being populated when I called the route. I added the [OnDeserialized] tag to the Contract class and now it always fails deserialization. There are no errors thrown, just Contract is null. I have no idea how to debug this since my OnDeserialized method never even gets hit.
ExportPostData
public class ExportPostData
{
public Contract contract { get; set; }
public bool includeSubItems { get; set; }
public string user { get; set; }
public string[] projects { get; set; }
}
Contract
public class ZEstimateContract
{
public string _id { get; set; }
public string contractName { get; set; }
public string contractNumber { get; set; }
public string updatedBy { get; set; }
public DateTime updated_at { get; set; }
[OnDeserialized()]
internal void Deserialized()
{
// THIS NEVER GETS HIT
Console.WriteLine("I'm deserialized");
}
}
Change
[OnDeserialized()]
internal void Deserialized()
{
// THIS NEVER GETS HIT
Console.WriteLine("I'm deserialized");
}
to this:
[OnDeserialized]
internal void Deserialized(StreamingContext context)
{
// THIS GETS HIT NOW
Console.WriteLine("I'm deserialized");
}
Without the parameter, the method's signature doesn't match what OnDeserialized is looking for. See this article for details: http://msdn.microsoft.com/en-us/library/system.runtime.serialization.ondeserializedattribute.aspx
I have a handful of email templates and in each template I have a header and footer that all share the same info.
The header and footer are represented by EmailModel.cs
public class EmailModel
{
public string CompanyName { get { return ConfigurationManager.AppSettings["CompanyName"]; } }
public string PhoneNumber { get { return ConfigurationManager.AppSettings["PhoneNumber"]; } }
public string FacebookUrl { get { return ConfigurationManager.AppSettings["FacebookUrl"]; } }
public string TwitterUrl { get { return ConfigurationManager.AppSettings["TwitterUrl"]; } }
public string YouTubeUrl { get { return ConfigurationManager.AppSettings["YouTubeUrl"]; } }
//Additional methods for sending these templates as emails
}
Now for a specific email template I have a view model.NewSignUpEmailViewModel.cs
Should I do this:
public class NewSignUpEmailViewModel : EmailModel
{
public string FirstName { get; set; }
public string CompanyName { get; set; }
public string UserName { get; set; }
public Guid UserId { get; set; }
}
or this:
public class NewSignUpEmailViewModel
{
public EmailModel Email {get; set;}
public string FirstName { get; set; }
public string CompanyName { get; set; }
public string UserName { get; set; }
public Guid UserId { get; set; }
}
I just used email as an example, is there pros/cons to each?
The only con I can see is that in some cases you will run into duplicate property name issue.
Composition is often preferred over inheritance, but both have their place. One good rule of thumb is to determine if there is an "is-a" or a "has-a" relationship between your objects. If object 1 has object 2 as a component, composition is definitely the way to go.
As an example, let's approach your data model a bit differently:
public class SocialLinks
{
public string FacebookUrl { get; set; }
public string TwitterUrl { get; set; }
public string YouTubeUrl { get; set; }
}
public class User
{
public SocialLinks links { get; set; }
public string Name { get; set; }
// and so on
}
In this example, it's obvious that a user HAS social web links, as opposed to the user being a specialized version of the SocialLinks class. Hope that helps!
I'm having trouble passing view information from my Get/Create action to my view. Here are my three model classes;
public class Competition
{
public int Id { get; set; }
public int CompetitionId { get; set; }
public string Name { get; set; }
public string Prize { get; set; }
}
public class CompetitionEntry
{
public int Id { get; set; }
public int CompetitionEntryId { get; set; }
public string Name { get; set; }
public string Email { get; set; }
public int CompetitionId { get; set; }
}
public class CompetitionEntryViewModel
{
public int Id { get; set; }
public Competition Competitions { get; set; }
public int CompetitionId { get; set; }
public string Name { get; set; }
public string Email { get; set; }
}
Here is my Get/Create action in CompetitionEntry Controller;
public ActionResult Create(int id)
{
CompetitionEntryViewModel competitionentryviewmodel = db.CompetitionEntriesView.Find(id);
return View(competitionentryviewmodel);
}
I know this doesn't work. The id parameter goes into the URL fine. How to I get access to my Competition class in th Get action? I need to be able to show the competion name on my Create Competition entry view.
Thanks in advance!
public ActionResult Create(int id)
{
var data = db.CompetitionEntriesView.Find(id);
CompetitionEntryViewModel competitionentryviewmodel = new CompetitionEntryViewModel();
competitionentryviewmodel.CompetitionName = data.Name;
return View(competitionentryviewmodel);
}
What you are trying to do is build an object graph and display it through a view model. In order to do this, you need to map your domain model(s) to your view model.
You can do the mapping yourself by writing a lot of code (re-inventing the wheel), or, you could consider using third party tools to do this for you. I recommend you use an AutoMapper as it is very simple to use imo.
The other problem is that your view model contains a domain model. This is likely to cause you a lot of headache in near future. If I were you, I would replace Competition with CompetitionViewModel.
I would also consider creating a view model for a list of competitions, i.e. CompetitionsViewModel. Look into partial views to see how you can display a list of competitions.
Good luck