How can I pass list of complex objects to webapi from breezejs? - asp.net-web-api

I found that using [fromapi] attribute I can pass one complex object.
when I try to pass list of complex objects it doesn't work.
in the client side I use breeze. server side is webapi.
How can I do this?

You can create one DTO which has property for your list of objects
public class CreateUserDto
{
public string Name {set;get;}
public List<RoleDto> Roles {set;get;}
public CreateUserDto()
{
this.Roles = new List<RoleDto>();
}
}
public class RoleDto
{
public int Id {set;get;}
public string Name {set;get;}
}
And you can use that as the argument of your Web api endpoint
public HttpResponseMesssage Save(CreateUserDto model)
{
//Check model.Roles now
// to do : Return a response
}
From client, you can send data like this.(Assuming you have jQuery library loaded to your page)
var data { Name : "TestName",Roles:[]}
data.Roles.push(new { Id:1,Name:"Admin"});
data.Roles.push(new { Id:2,Name:"Editor"});
$.post("YourEndpointHere",data,function(response){
// do something with response
});
Modelbinding will take care of converting the posted form data to an instance of CreateUserDto in your Save method. You can access model.Roles property to get the list of complex objects you wanted.

you can use Dictionary as below:
[HttpPost]
public IQueryable<Product> GetProducts(Dictionary<string, object> data)
{
var categoryId = Convert.ToInt32(data["categoryId"]);
var category = _context.Categories.Single(a => a.ID == categoryId);
var galleryId = Convert.ToInt32(data["galleryId"]);
var langId = Convert.ToInt32(data["langId"]);
var searchStr = data["str"];
return category.Products.Where(a => a.GalleryID == galleryId, a.LanguageID == langId, a.Description.Contains(searchStr))
}

Related

Pass complex object between action methods ASP.Net MVC Core

I studied most of the similar questions but couldn't find the answer!
Let me declare that I can define a simple TempData like an string,int and get it in another action method successfully, but
I have 3 parameters/variable in ActionMethod1 which is named "ExternalLoginCallBack", and need to ask a "UserName" from user on client side and then save the total 4 parameters in ActionMethod2 which is named "CreateExternalUser".
This is what I have.
AskUserNameView_NoActionMethod.cshtml as below:
#model ExternalUserViewModel
<form asp-action="CreateExternalUser" asp-controller="Account" method="post">
<label asp-for="UserName">Input your UserName Here: </label>
<input asp-for="UserName" >
<input type="submit" value="ُSubmit"/>
</form>
(Using TempData) Attemp-No.1:
public async Task<IActionResult> ActionMethod1
//some codes here
var externalLoginInfo = await _signInManager.GetExternalLoginInfoAsync();
TempData["externalLoginInfo"] = externalLoginInfo;
TempData["email"] = email;
TempData["returnUrl"] = returnUrl;
return View("AskUserNameView_NoActionMethod");
but instead of showing the AskUserNameView_NoActionMethod.cshtml it shows just a white page with no errors,no exception and nothing :
Attemp-No2: I removed "ExternalLoginInfo" type and only two simple string as an object remained to pass to ActionMethod2:
public async Task<IActionResult> ActionMethod1
//some codes here
var externalUserViewModel= new ExternalUserViewModel()
{
Email = email,
ReturnUrl = returnUrl,
};
TempData["externalUserViewModel"] = externalUserViewModel;
return View("AskUserNameView_NoActionMethod");
but again the white page above appeared. When I remove the complex TempData, my AskUserNameView_NoActionMethod.cshtml rendered successfully, and I can pass UserName which is entered by client side, to ActionMethod2. But without 3 other parameters which is needed to create a new External user !!
My ExternalUserViewModel is as below:
public class ExternalUserViewModel
{
public ExternalLoginInfo ExternalLoginInfo { get; set; }
public string Email { get; set; }
public string UserName { get; set; }
public string ReturnUrl { get; set; }
}
and already added these codes to startup.cs:
services.AddControllersWithViews();
services.AddSingleton<ITempDataProvider, CookieTempDataProvider>();
app.UseSession();
If TempData is not apllicable, I tried to pass my model with 3 parameters (Email,ReturnUrl,ExternalLoginInfo ) to strongly typed AskUserNameView_NoActionMethod.cshtml, but again Username entered by client side,Email and returnedUrl passed to ActionMethod2 but externalLoginInfo was null .
Summary: need an example to pass a complex data/object from actionmethod1 to actionmethod2 , without redirect to actionmethod2 !!
You have two methods to achieve it.
As the TempData cannot store complex object here ,you can Serialize the object to json string and store it in TempData, then you can get the json string in CreateExternalUser action and Deserialize this json string to the correspond object as follow:
public async Task<IActionResult> ActionMethod1()
{
//some codes here
var externalLoginInfo = await _signInManager.GetExternalLoginInfoAsync();
TempData["externalLoginInfo"] = JsonConvert.SerializeObject(externalLoginInfo);
TempData["email"] = email;
TempData["returnUrl"] = returnUrl;
return View("AskUserNameView_NoActionMethod");
}
Receive:
public async Task<IActionResult> CreateExternalUser(UserName userName)
{
//some codes here
var externalLoginInfo = JsonConvert.DeserializeObject<ExternalLoginInfo>(TempData["externalLoginInfo"].ToString());
var email = TempData["email"] as string;
var returnUrl = TempData["returnUrl"] as string;
return View();
}
Another method is to create a custom method named TempDataExtensions to pass object from an action to another.
public static class TempDataExtensions
{
public static void Put<T>(this ITempDataDictionary tempData, string key, T value) where T : class
{
tempData[key] = JsonConvert.SerializeObject(value);
}
public static T Get<T>(this ITempDataDictionary tempData, string key) where T : class
{
object o;
tempData.TryGetValue(key, out o);
return o == null ? null : JsonConvert.DeserializeObject<T>((string)o);
}
}
Store object:
var externalLoginInfo = await _signInManager.GetExternalLoginInfoAsync();
TempData.Put("externalLoginInfo", externalLoginInfo);
Get object:
var externalLoginInfo = TempData.Get<ExternalLoginInfo>("externalLoginInfo");

WebAPI2 Model Binding not working with HTTP PUT

I'm following Scott Allen's MVC4 course on PluralSight (I'm using MVC5 and WebAPI2 but they should be the same) and I am trying to pass an object via HTTP PUT. The model binder should bind it, but I am getting NULL for the parameter.
public HttpResponseMessage PutObjective(int id, [FromBody] Objective objective)
{
if (ModelState.IsValid && id == objective.ObjectiveID)
{
//todo: update - look up id, replace text
return Request.CreateResponse(HttpStatusCode.OK, objective);
}
else
{
return Request.CreateResponse(HttpStatusCode.BadRequest);
}
}
and in my front-end javascript I am doing the following (I'm creating an object for testing, so ignore 'objective' passed in):
var updateObjective = function (objective) {
var myobj = { "ObjectiveID": "3", "ObjectiveDescription": "test" };
return $.ajax(objectiveApiUrl + "/" + objective.ObjectiveID, {
type: "PUT",
data: myobj
});
}
My class looks like this:
public class Objective
{
public int ObjectiveID { get; private set; }
public string ObjectiveDescription { get; set; }
public Objective (int Id, string Desc)
{
this.ObjectiveID = Id;
this.ObjectiveDescription = Desc;
}
}
Any thoughts on why 'objective' in the backend is always 'null' ?
I've done what Scott Allen is doing, even tried adding in [FromBody] but no luck. $.ajax should have the correct content type by default I understand, so no need to set it.
I had Fiddler2 but I'm unsure as to what I am looking at to be honest. I can see my object as JSON being sent to the backend.
Well, if you're familiar with Model Binding you'll have seen the issue in my Objective class:
public int ObjectiveID { get; private set; }
with a private set, no instance can be created of the Objective class. To make it work, the 'private' access specifier needs to be removed.
What needs to happen really is that Objective becomes ObjectiveViewModel, and we convert what comes back to an Objective domain object (which may have more properties than we need for this screen). This can have a private set.

Return raw objects from Action methods and convert them to JsonResult before rendering

The website that I'm working on is heavily depending on ajax/json and knockout.js.
I would like to have a lot of my Controllers return view-tailored 'json objects', without wrapping them in a JsonResult when returning the method.
This would mean I could easily composite multiple calls into one parent object, but still be able to call the Actions separately too.
Simplified example:
public object Main(int groupId)
{
var viewModel = new
{
Persons = Employees(groupId),
Messages = AllMessages()
};
return viewModel;
}
public object Employees(int groupId)
{
return DatabaseContext.Employees.Where(e => e.GroupId == groupId).ToList();
}
public object AllMessages()
{
return DatabaseContext.Messages.ToList();
}
I was hoping I could capture the returned object in OnActionExecuted and at that point wrap the whole result up in a final JsonResult.
The result is already converted to a string and captured in a ContentResult though.
Any ideas? :) Thanks,
A good approach on this is to create helper methods for your entity calls. Or if you have those methods already somewhere, they can actually serve as the helper methods. In that manner you can return a list of strongly-typed Messages and Employees as well as returning your desired parent object. You can then have individual controller methods that returns json objects. In addition, you can extend the parent viewmodel to return additional fields.
The Parent ViewModel
public class ParentModel {
public Employee Persons {get;set;}
public Message Messages {get;set;}
}
The Helper Methods
The beauty of using helper methods similar to what is defined here is that you can apply a few more logic to your query, and more, and you don't have to change anything in your controller methods.
public ParentModel GetMain(int groupId)
{
var viewModel = new ParentModel
{
Persons = Employees(groupId),
Messages = AllMessages()
};
return viewModel;
}
public IEnumerable<Employee> Employees(int groupId)
{
return DatabaseContext.Employees.Where(e => e.GroupId == groupId).ToList();
}
public IEnumerable<Message> AllMessages()
{
return DatabaseContext.Messages.ToList();
}
The Controller Methods
public ActionResult GetParent(int groupId){
return Json(helperinstance.GetMain());
}
public ActionResult GetEmployees(int groupId){
return Json(helperinstance.Employees());
}
public ActionResult GetMessages(int groupId){
return Json(helperinstance.AllMessages());
}
Thanks for the answer. I'm not going for the solution of von v. because I like to keep the boilerplate as small as possible.
In the end I am trying out the following approach. It seems to work pretty well for now, but I still have to test it in real production.
If anyone has some (security) concerns with this, I'm happy to hear them in the comments.
// BaseController
protected override void OnActionExecuting(ActionExecutingContext filterContext)
{
var returnType = ((ReflectedActionDescriptor)filterContext.ActionDescriptor).MethodInfo.ReturnType;
// is the returnType not deriving from ActionResult? Automatically wrap it in a JsonResult
if ( !typeof(ActionResult).IsAssignableFrom(returnType) )
{
var result = filterContext.ActionDescriptor.Execute(filterContext, filterContext.ActionParameters);
filterContext.Result = Json( result );
}
}

How to create dynamic, multiple partial views using repository pattern in MVC

I am trying to have a general home page that depending on the parameter passed to the control, different content (modules) will be displayed.
For example, a user may select Kentucky from the menu and the id for Kentucky is 1. The home controller gets the id (1) and determines the possible modules for that
state (a simple db call.) Perhaps there is an announcements module and a contacts module for the state. An announcements module could have several items but it's only one module. There would be a partial view for each type of module.
Here is the basic setup I have.
public interface IModuleRepository
{
IList<MenuItemModule> GetMenuItemModules(int menuItem);
IList<Announcements> GetAnnouncements(int modID);
IList<News> GetNews(int modID);
IList<Contacts> GetContacts(int modID);
}
//business object
public class MenuItemModule
{
private int _MenuItemID;
private int _ModuleID;
private int _ModuleDefID;
private string _Src;
private int _ModuleOrder;
//get, set properties for these...
}
//announcements entity
public class Announcements
{
private int _ID = -1;
private int _MenuItemID = -1;
private int _ModuleID = -1;
private string _Description = string.Empty;
//get set props ...
}
In my home controller...
public class HomeController : Controller
{
private IModuleRepository modRepository;
public HomeController(IModuleRepository modRepository)
{
this.modRepository = modRepository;
}
public ViewResult Item(string ItemID)
{
//returns a list of menuitemmodules for the page. This gives me the Src or name of each
//module on the page, i.e. Announcements, news, contacts, etc.
var modules = modRepository.GetMenuItemModules(Convert.ToInt32(ItemID));
return View(modules);
}
}
I have tried several different models to return but I always run up against some contstraint. If I pass the menuitemmodules to my Item.aspx, then I can do something like this:
foreach (var mod in Model)
{
Html.RenderPartial(mod.Src, a); //needs an announcement object though
}
That makes it somewhat dynamic because I have the Src which would basically be something like "Announcements" and I can just create an announcements.ascx partial to process the module. But I have found it difficult to pass my menuitemmodule and an announcements entity as well.
I have also messed around with passing a more complex object and then testing every Src that comes through with an If statement. This would make scaling difficult in the future as I increase the number of possible modules in the app.
How can I solve my problem? I hope I have provided enough info. I like the basic idea here - http://www.mikesdotnetting.com/Article/105/ASP.NET-MVC-Partial-Views-and-Strongly-Typed-Custom-ViewModels but that seems to only work for static modules on a page.
I did try a composite view model called ModuleViewModel. Here is that attempt:
public class ModuleViewModel
{
public IList<Announcements> announcements { get; set; }
public IList<MenuItemModule> mods { get; set; }
}
If I pass that model to the Item.aspx I can do something like this (but I must be doing something wrong because something doesn't look right.)
foreach (var mod in Model)
{
if (mod.announcements.Count > 0)
{
Html.RenderPartial("Announcements", mod.announcements);
}
}
Once again, scalability is going to haunt me. I would like to have something like this on item page:
foreach (var mod in Model)
{
Html.RenderPartial(mod.Src, mod);
}
That would the correct partial view and pass it the correct model.
Create Module classes that derive from a common Module base class:
public class AnnouncementsModule : Module
{
}
public class ContactsModule : Module
{
}
In controller:
Create your various modules and put them into your overall view module (here it has a property called Modules that is an array of Module:
var viewModel = new ComplexViewModel
{
Modules = new []
{
new ContactsModule(),
new AnnouncementsModule()
}
};
return View(viewModule);
In view:
#Html.DisplayFor(x => x.Modules);
Create the partial views for each Type of Module in the appropriate 'Shared` folder. (Run it without creating them and it will show you an exception with the locations where it's looking for them).
After messing around with this for over a week, I finally managed to figure out how MVC can do what I want dynamically. I decided to post my solution for others that are new to MVC. Hopefully, the following will clear up the misunderstandings I had (although, at this point in my understanding of MVC, I cannot say this is the best approach.)
I will include the previous code snips and modifications for clarity:
public interface IModuleRepository
{
IList<MenuItemModule> GetMenuItemModules(int menuItem);
IList<Announcements> GetAnnouncements(int modID);
IList<News> GetNews(int modID);
IList<Contacts> GetContacts(int modID);
}
//business object
public class MenuItemModule
{
private int _MenuItemID;
private int _ModuleID;
private int _ModuleDefID;
private string _Src;
private int _ModuleOrder;
//get, set properties for these...
}
//announcements entity
public class Announcements : MenuItemModule
{
private int _ID = -1;
private string _Description = string.Empty;
//get set props ...
}
I also added another class:
public class AnnouncementModule : MenuItemModule
{
private IList<Announcements> _Announcements;
//get set prop
}
...and I created a model for the view
public class HomeItemViewModel
{
public MenuItemModule[] MenuItemModules { get; set; } //collection of menuitemmodules
}
In my home controller...
var menuItemModules = modRepository.GetMenuItemModules(ItemID);
if (menuItemModules.Count > 0)
{
AnnouncementModule aMod;
MenuItemModule[] mods = new MenuItemModule[menuItemModules.Count()];
int i = 0;
//loop through each MenuItemModule assign to the appropriate model
foreach (MenuItemModule mod in menuItemModules)
{
if (mod.Src == "Announcements")
{
aMod = new AnnouncementModule();
aMod.Announcements = modRepository.GetAnnouncements(mod.ModuleID);
//now add this to the menuitemmodule collection
mods[i] = aMod;
}
if (mod.Src == "Contacts")
{
//...
}
i++;
}
}
var viewModel = new HomeItemViewModel
{
MenuItemModules = mods
};
return View(viewModel);
Then I used the suggestion to use DisplayFor in the view. The view is strongly typed to HomeItemViewModel.
<%: Html.DisplayFor(m => m.MenuItemModules) %>
This iterates through the collection and based on the type, it will call that template. In this example, it calls AnnouncementModule.ascx which is strongly typed to AnnouncementModule.
foreach (var a in Model.Announcements)
{
//a.Description will give us the description of the announcement
}
I realize there are slicker ways to code the controller, and I plan on refactoring, but this skeleton should provide the basics to solve the question I posted.

LINQ-To-Sharepoint Multiple content types for a single list

I'm using SPMetal in order to generate entity classes for my sharepoint site and I'm not exactly sure what the best practice is to use when there are multiple content types for a single list. For instance I have a task list that contains 2 content types and I'm defining them via the config file for SPMetal. Here is my definition...
<List Member="Tasks" Name="Tasks">
<ContentType Class="LegalReview" Name="LegalReviewContent"/>
<ContentType Class="Approval" Name="ApprovalContent"/>
</List>
This seems to work pretty well in that the generated objects do inherit from WorkflowTask but the generated type for the data context is a List of WorkflowTask. So when I do a query I get back a WorkflowTask object instead of a LegalReview or Approval object. How do I make it return an object of the correct type?
[Microsoft.SharePoint.Linq.ListAttribute(Name="Tasks")]
public Microsoft.SharePoint.Linq.EntityList<WorkflowTask> Tasks {
get {
return this.GetList<WorkflowTask>("Tasks");
}
}
UPDATE
Thanks for getting back to me. I'm not sure how I recreate the type based on the SPListItem and would appreciate any feedback.
ContractManagementDataContext context = new ContractManagementDataContext(_url);
WorkflowTask task = context.Tasks.FirstOrDefault(t => t.Id ==5);
Approval a = new Approval(task.item);
public partial class Approval{
public Approval(SPListItem item){
//Set all properties here for workflowtask and approval type?
//Wouldn't there be issues since it isn't attached to the datacontext?
}
public String SomeProperty{
get{ //get from list item};
set{ //set to list item};
}
Linq2SharePoint will always return an object of the first common base ContentType for all the ContentTypes in the list. This is not only because a base type of some description must be used to combine the different ContentTypes in code but also it will then only map the fields that should definitely exist on all ContentTypes in the list. It is however possible to get access to the underlying SPListItem returned by L2SP and thus from that determine the ContentType and down cast the item.
As part of a custom repository layer that is generated from T4 templates we have a partial addition to the Item class generated by SPMetal which implements ICustomMapping to get the data not usually available on the L2SP entities. A simplified version is below which just gets the ContentType and ModifiedDate to show the methodology; though the full class we use also maps Modified By, Created Date/By, Attachments, Version, Path etc, the principle is the same for all.
public partial class Item : ICustomMapping
{
private SPListItem _SPListItem;
public SPListItem SPListItem
{
get { return _SPListItem; }
set { _SPListItem = value; }
}
public string ContentTypeId { get; internal set; }
public DateTime Modified { get; internal set; }
public virtual void MapFrom(object listItem)
{
SPListItem item = (SPListItem)listItem;
this.SPListItem = item;
this.ContentTypeId = item.ContentTypeId.ToString();
this.Modified = (DateTime)item["Modified"];
}
public virtual void MapTo(object listItem)
{
SPListItem item = (SPListItem)listItem;
item["Modified"] = this.Modified == DateTime.MinValue ? this.Modified = DateTime.Now : this.Modified;
}
public virtual void Resolve(RefreshMode mode, object originalListItem, object databaseObject)
{
SPListItem originalItem = (SPListItem)originalListItem;
SPListItem databaseItem = (SPListItem)databaseObject;
DateTime originalModifiedValue = (DateTime)originalItem["Modified"];
DateTime dbModifiedValue = (DateTime)databaseItem["Modified"];
string originalContentTypeIdValue = originalItem.ContentTypeId.ToString();
string dbContentTypeIdValue = databaseItem.ContentTypeId.ToString();
switch(mode)
{
case RefreshMode.OverwriteCurrentValues:
this.Modified = dbModifiedValue;
this.ContentTypeId = dbContentTypeIdValue;
break;
case RefreshMode.KeepCurrentValues:
databaseItem["Modified"] = this.Modified;
break;
case RefreshMode.KeepChanges:
if (this.Modified != originalModifiedValue)
{
databaseItem["Modified"] = this.Modified;
}
else if (this.Modified == originalModifiedValue && this.Modified != dbModifiedValue)
{
this.Modified = dbModifiedValue;
}
if (this.ContentTypeId != originalContentTypeIdValue)
{
throw new InvalidOperationException("You cannot change the ContentTypeId directly");
}
else if (this.ContentTypeId == originalContentTypeIdValue && this.ContentTypeId != dbContentTypeIdValue)
{
this.ContentTypeId = dbContentTypeIdValue;
}
break;
}
}
}
Once you have the ContentType and the underlying SPListItem available on your L2SP entity it is simply a matter of writing a method which returns an instance of the derived ContentType entity from a combination of the values of the base type and the extra data for the missing fields from the SPListItem.
UPDATE: I don't actually have an example converter class as we don't use the above mapping extension to Item in this way. However I could imagine something like this would work:
public static class EntityConverter
{
public static Approval ToApproval(WorkflowTask wft)
{
Approval a = new Approval();
a.SomePropertyOnWorkflowTask = wft.SomePropertyOnWorkflowTask;
a.SomePropertyOnApproval = wft.SPListItem["field-name"];
return a;
}
}
Or you could put a method on a partial instance of WorkflowTask to return an Approval object.
public partial class WorkflowTask
{
public Approval ToApproval()
{
Approval a = new Approval();
a.SomePropertyOnWorkflowTask = this.SomePropertyOnWorkflowTask;
a.SomePropertyOnApproval = this.SPListItem["field-name"];
return a;
}
public LegalReview ToLegalReview()
{
// Create and return LegalReview as for Approval
}
}
In either situation you would need to determine the method to call to get the derived type from the ContentTypeId property of the WorkflowTask. This is the sort of code I would normally want to generate in one form or another as it will be pretty repetitive but that is a bit off-topic.

Resources