Is there any disadvantage to using a dynamic return type on my API endpoints? - asp.net

I am currently building a web api service using MVC and I am creating the endpoints. For example, my GET endpoint will execute a stored procedure and return the data in JSON format. The model of the data returned can vary in the future and it seems like using a dynamic return type would remove the need of having to change the model and mapping every time that happens. Basically, here is some sample code, do you notice any malpractices in my implementation?
[System.Web.Mvc.HttpGet]
[Route("companies/{id}")]
public dynamic GetCompany([FromUri] int id, string userId)
{
var parameters = new Hashtable
{
{"UserID", userId},
{"CompanyID", id}
};
var result = MyDB.ExecuteSp(CompanyReadByIdSp, parameters);
return result;
}
In fact, this would enable me to transform the object and add whatever I want to it without needing to worry about the model. Is this a bad way of doing things? Thanks ahead.

Related

EF Core Update with List

To make updates to a record of SQL Server using Entity Framework Core, I query the record I need to update, make changes to the object and then call .SaveChanges(). This works nice and clean.
For example:
var emp = _context.Employee.FirstOrDefault(item => item.IdEmployee == Data.IdEmployee);
emp.IdPosition = Data.IdPosition;
await _context.SaveChangesAsync();
But is there a standard method if I want to update multiple records?
My first approach was using a list passing it to the controller, but then I would need to go through that list and save changes every time, never really finished this option as I regarded it as not optimal.
For now what I do is instead of passing a list to the controller, I pass each object to the controller using a for. (kind of the same...)
for(int i = 0; i < ObjectList.Count; i ++)
{
/* Some code */
var httpResponseObject = await MyRepositories.Post<Object>(url+"/Controller", Object);
}
And then do the same thing on the controller as before, when updating only one record, for each of the records...
I don't feel this is the best possible approach, but I haven't found another way, yet.
What would be the optimal way of doing this?
Your question has nothing to do with Blazor... However, I'm not sure I understand what is the issue. When you call the SaveChangesAsync method, all changes in your context are committed to the database. You don't have to pass one object at a time...You can pass a list of objects
Hope this helps...
Updating records in bulk using Entity Framework or other Object Relational Mapping (ORM) libraries is a common challenge because they will run an UPDATE command for every record. You could try using Entity Framework Plus, which is an extension to do bulk updates.
If updating multiple records with a single call is critical for you, I would recommend just writing a stored procedure and call if from your service. Entity Framework can also run direct queries and stored procedures.
It looks like the user makes some changes and then a save action needs to persist multiple records at the same time. You could trigger multiple AJAX calls—or, if you need, just one.
What I would do is create an endpoint—with an API controller and an action—that's specific to your needs. For example, to update the position of records in a table:
Controller:
/DataOrder
Action:
[HttpPut]
public async void Update([FromBody] DataChanges changes)
{
foreach(var change in changes)
{
var dbRecord = _context.Employees.Find(change.RecordId);
dbRecord.IdPosition = change.Position;
}
_context.SaveChanges();
}
public class DataChanges
{
public List<DataChange> Items {get;set;}
public DataChangesWrapper()
{
Items = new List<DataChange>();
}
}
public class DataChange
{
public int RecordId {get;set;}
public int Position {get;set;}
}
The foreach statement will execute an UPDATE for every record. If you want a single database call, however, you can write a SQL query or have a stored procedure in the database and pass the data as a DataTable parameter instead.

Angular2 HTTP Post ASP.NET MVC Web API

How do you properly create a Web API POST of complex object or multiple parameters using Angular2?
I have a service component in Angular2 as seen below:
public signin(inputEmail: string, inputPassword: string): Observable<Response> {
return this.http.post('/api/account/signin', JSON.stringify({ Email: inputEmail, Password: inputPassword}), this.options);
}
The targeted web api is seen below:
[HttpPost]
[Route("signin")]
public async Task<IActionResult> Signin(string email, string password)
{
....
}
This does not work because I need to convert the parameters of the web api into a single POCO class entity with Email and Password properties and put the [FromBody] attribute: Signin([FromBody] Credential credential)
Without using [FromURI] (POST requests with query strings?), how can I make POSTs of multiple parameters or complex objects without converting these parameters into a single POCO class?
Because what if I have numerous Web API POST actions with parameters like (string sensitiveInfo1, string name, int sensitiveInfo2) or (ClassifiedInfo info, string sensitiveInfo1, string sensitiveInfo2), do I need to convert them all to POCO classes and always use [FromBody]?
PS.
I was using RestangularJS before and it can posts anything (mulitple primitive objects and complex objects) without my Web API actions having [FromBody] attributes. Will about to investigate how RestangularJS do it.
Without using [FromURI] (POST requests with query strings?), how can I make POSTs of multiple parameters or complex objects without converting these parameters into a single POCO class?
I know its not what you want to hear but out of the box this is not possible. It is not a limitation of the browser code that is making the request. This means it does not matter if you are using Angular, JQuery, straight JavaScript, or even RestangularJS. This is a limitation (I use that word loosely as I am sure this is by design) of Web API (any version). Here is the documentation on this design: Parameter Binding in ASP.NET Web API by Mike Wasson.
At most one parameter is allowed to read from the message body. So this will not work:
// Caution: Will not work!
public HttpResponseMessage Post([FromBody] int id, [FromBody] string name) { ... }
So the question becomes, what are your options?
Create a model
This is the thing you were trying to avoid but I list it first because this is how Web API was intended to behave. I have not yet heard a compelling reason not to do this. This approach allows you to extend your model easily without having to change the method signature. It also allows for model validation on the model itself. Personally I really like this approach.
public class SignInModel{
public string Email {get;set;}
public string Password {get;set;}
}
[HttpPost]
[Route("signin")]
public async Task<IActionResult> Signin(SignInModel signInModel)
{
// ....
}
I did not repeat your existing JavaScript code because what you have works as is with the above web api code
URL
Again, what you were trying to avoid. This does make what you want possible with the limitation that you have to pass these parameters using the Query string on the URL. The JavaScript would change but the signature you had on the Web API method would not.
public signin(inputEmail: string, inputPassword: string): Observable<Response> {
return this.http.post('/api/account/signin/?email=inputEmail&password=inputPassword', null, this.options);
}
I did not repeat your existing Web API code because what you have works as is with the above web JavaScript code (by default FromUri is assumed I believe)
Custom Model Binder
See Passing multiple POST parameters to Web API Controller Methods by Rick Strahl. This option allows you to create a custom model binder that could do what you are asking. It is a whole bunch of extra code though for, IMHO, not much benefit. Maybe there are situations where it would be useful although I really cannot think of any off the top of my head.
Dynamic
Finally you could also pass in a dynamic object as the parameter of your Web API. This is essentially the same as receiving the JSON as a string and making your Controller code responsible for the deserialization of content. Again, I believe that this would make your code worse in most situations as you have to implement custom validation and type checks. This answer was proposed previously on SO by Bes Ley. Again, maybe there are situations where it would be useful although I really cannot think of any off the top of my head.
If you call Web API 2.2 post method from Angular 2 type script, dont forget to add following header content and parameter object.
let headers = new Headers({ 'Content-Type': 'application/x-www-form-urlencoded' });
var params = new URLSearchParams();
params.set('userid', '102');
params.set('username', 'foo');
return this._http.post('http://localhost:6579/api/PostUser', params.toString(), { headers: headers }).map(res => res.json());
Perhaps you should post with options:
{
headers: new Headers({
'Content-Type': 'application/x-www-form-urlencoded'
})
}
and encode data like
jQuery.param({user:'bla', password: 'bla'});
WebAPI does not provide this out of the box. If you try to get understanding of web API bindings, you might be able to figure out why.
I think this article might help.
The generic rules are:
– simple, string-convertible parameters (value types, strings, Guids, DateTimes and so on) are by default read from URI
– complex types are by default read from the body
– collections of simple parameters are by default read from the body too
– you cannot compose a single model based on input from both URI and request body, it has to be one or the other
I have fixed the issue of Angular2 HTTP Post ASP.NET MVC Web API
let headers = new Headers();
headers.append('Content-Type', 'application/x-www-form-urlencoded; charset=utf-8');
let params: URLSearchParams = new URLSearchParams();
params.set('value', '2');
let options = new RequestOptions({
headers: headers//,
//search: params
});
let content = new URLSearchParams();
content.set('StudentName', 'Inderjit Singh';
content.set('Mobile', '+919041165398');
content.set('Nationality', 'Indian');
content.set('AdmissionNo', '6');
content.set('SectionCode', '1');
content.set('Gender', 'Male');
content.set('RegNo', '18585');
content.set('ClassCode', '1');
this.http.post('YOUR_URL', content.toString(), { headers: headers }).map((res: Response) => { console.log("data is==>" + res.text()); }).subscribe();
WebApi will be able to deserialize your Credential object provided the JSON object has the same field names (I am not sure about case so you may be right here). You seem to be missing the headers from the post call in your Angular2 component.
Can you check the Content-Type using Chrome Debugger or Fiddler? It should be application/json.
Try this, passing a complex class object into a single data parameter.
var SearchQuery = function () {
this.Alphabet = null;
this.Search = false;
this.Keyword = null;
this.RegionList = null;
};
var para = new SearchQuery();
{ data: JSON.stringify(para) } - Post Data
you can receive it using a JObject in your API controller and deserialize it as according to your classes.

How do I pass object (ObjectProxy) from Flex back to .NET WebService?

So, there are a wealth of Flex articles online about how to handle a .NET WebMethod that returns a DataSet or DataTable. Here is an example:
Handling web service results that contain .NET DataSets or DataTables
So, I know how to use result.Tables.<tablename>.Rows and the like. But what I cannot seem to figure out or find online is how to go the other direction - a method to pass objects or tables back to the .NET Webservice from Flex, without stooping to passing XML as a string, or making huge web service methods that have one parameter for each property/column of the object being stored. Surely others, smarter than I, have tackled this issue.
I am using ASP.NET 2.0 Typed DataSets, and it would be really nice if I could just pass one object or array of objects from Flex to the web service, populate my Typed DataTable, and do an Update() through the corresponding typed TableAdapter. My dream would be a [WebMethod] something like one of these:
public void SaveObject(TypedDataTable objToSave) { ... }
public void SaveObject(TypedDataSet objToSave) { ... }
I've had the typed datatables saving to the database, I know how to do that part and even a few tricks, but we had XML being passed back-and-forth as a string - eww. I'm trying to get to a more object-based approach.
The best object based approach is AMF. I assume its probably a bit late in your your development cycle to change your integration layer, but otherwise I dont know of a way to get around marshalling your object(s) back into XML or separating them out into their primitive components.
For .NET implementations of AMF check out:
FlourineFX(FOSS)
WebORB for .NET
Its amazing how easy things become once AMF is used, for example using the Mate MVC framework and an AMF call passing a complex object to the server looks something like this:
<mate:RemoteObjectInvoker instance="yourWebservice" method="saveComplexObject" showBusyCursor="true" >
<mate:resultHandlers>
<mate:CallBack method="saveComplexObjectSuccess" arguments="{[resultObject]}" />
</mate:resultHandlers>
<mate:faultHandlers>
<mate:MethodInvoker generator="{DataManager}" method="presentFault" arguments="{fault}" />
</mate:faultHandlers>
</mate:RemoteObjectInvoker>
With result and fault handlers being optional.
The direction I ended up going was close to what I hoped was possible, but is "hack-ish" enough that I would consider SuperSaiyen's suggestion to use AMF/ORM a better solution for new/greenfield projects.
For sake of example/discussion, let's say I am working with a Person table in a database, and have a typed DataSet called PeopleDataSet that has PersonTableAdapter and PersonDataTable with it.
READ would look like this in .NET web service:
[WebMethod]
public PeopleDataSet.PersonDataTable GetAllPeople() {
var adapter = new PersonTableAdapter();
return adapter.GetData();
}
... which in Flex would give you a result Object that you can use like this:
// FLEX (AS3)
something.dataProvider = result.Tables.Person.Rows;
Check out the link I put in the question for more details on how Flex handles that.
CREATE/UPDATE - This is the part I had to figure out, and why I asked this question. The Flex first this time:
// FLEX (AS3)
var person:Object = {
PersonID: -1, // -1 for CREATE, actual ID for UPDATE
FirstName: "John",
LastName: "Smith",
BirthDate: "07/19/1983",
CreationDate: "1997-07-16T19:20+01:00" // need W3C DTF for Date WITH Time
};
_pplWebSvcInstance.SavePerson(person); // do the web method call
(For handling those W3C datetimes, see How to parse an ISO formatted date in Flex (AS3)?)
On the .NET web service side then, the trick was figuring out the correct Type on the web method's parameter. If you go with just Object, then step into a call with a debugger, you'll see .NET figures it is a XmlNode[]. Here is what I figured out to do:
[WebMethod]
public int SavePerson(PeopleDataSet p) {
// Now 'p' will be a PeopleDataSet with a Table called 'p' that has our data
// row(s) (just row, in this case) as string columns in random order.
// It WILL NOT WORK to use PeopleDataSet.PersonDataTable as the type for the
// parameter, that will always result in an empty table. That is why the
// LoadFlexDataTable utility method below is necessary.
var adapter = new PersonTableAdapter();
var tbl = new PeopleDataSet.PersonDataTable();
tbl.LoadFlexDataTable(p.Tables[0]); // see below
// the rest of this might be familiar territory for working with DataSets
PeopleDataSet.PersonRow row = tbl.FirstOrDefault();
if (row != null) {
if (row.PersonID > 0) { // doing UPDATE
row.AcceptChanges();
row.SetModified();
}
else { // doing CREATE
row.CreationDate = DateTime.UtcNow; // set defaults here
row.IsDeleted = false;
}
adapter.Update(row); // database call
return row.PersonID;
}
return -1;
}
Now, the kluge utility method you saw called above. I did it as extension method, that is optional:
// for getting the Un-Typed datatable Flex gives us into our Typed DataTable
public static void LoadFlexDataTable(this DataTable tbl, DataTable flexDataTable)
{
tbl.BeginLoadData();
tbl.Load(flexDataTable.CreateDataReader(), LoadOption.OverwriteChanges);
tbl.EndLoadData();
// Probably a bug, but all of the ampersand (&) in string columns will be
// unecessarily escaped (&) - kluge to fix it.
foreach (DataRow row in tbl.Rows)
{
row.SetAdded(); // default to "Added" state for each row
foreach (DataColumn col in tbl.Columns) // fix & to & on string columns
{
if (col.DataType == typeof(String) && !row.IsNull(col))
row[col] = (row[col] as string).Replace("&", "&");
}
}
}

Best Practices when using .NET Session for temporary storage?

I'm still relatively new to .NET and ASP.NET MVC, and I have had a few occasions where it would be nice to store information retrieved from the DB temporarily so it can be used on a subsequent server request from the client. I have begun using the .NET Session to store this information, keyed off of a timestamp, and then retrieve the information using the timestamp when I hit the server again.
So a basic use case:
User clicks 'Query' button to gather information from the system.
In JS, generate a timestamp of the current time, and pass this to the server with request
On server, gather information from DB
On server, use unique timestamp from client as a key into the Session to store the response object.
Return response object to client
User clicks 'Generate Report' button (will format query results into Excel doc)
Pass same timestamp from #2 down to server again, and use to gather query results from #4.
Generate report w/o additional DB hit.
This is the scheme that I have begun to use in any case where I use the Session as temporary storage. But generating a timestamp in JS isn't necessarily secure, and the whole things feels a little... unstructured. Is there an existing design pattern I can use for this, or a more streamlined/secure approach? Any help would be appreciated.
Thanks.
You may take a look at TempData which stores the data in Session.When you pull something out of TempData it will be removed after the Action is done executing.
So, if you put something in TempData in an Action, it will live in TempData across all other actions until its requested TempDatafrom TempData again.
You can also call TempData.Peek("key") which will keep it in memory until you call TempData["key"] or TempData.Remove("key")
Ok, I'm not sure I understand you correctly as the JS timestamp step seems superfluous.
But this is what I would do.
public static string SessionReportKey = "Reports";
public static string ReportIDString = "ReportID";
public Dictionary<string, object> SessionReportData
{
get
{
return Session[SessionReportKey] == null ?
new Dictionary<string, object>() :
(Dictionary<string, object>) Session[SessionReportKey];
}
set
{
Session[SessionReportKey] = value;
}
}
public ActionResult PreviewReport()
{
//retrive your data
object reportData = GetData();
//get identifier
string myGUID = new GUID().ToString();
//might only need [SessionReportData.Add(myGUID, reportData);] here
SessionReportData = SessionReportData.Add(myGUID, reportData);
//in your view make a hyperlink to PrintReport action with a
//query string of [?ReportID=<guidvalue>]
ViewBag[ReportIDString] = myGUID;
return View(reportData);
}
public FileContentResult PrintReport()
{
if(SessionReportData[QueryString[ReportIDString]] == null)
{
//error no report in session
return null;
}
return GenerateFileFromData(SessionReportData[QueryString[ReportIDString]]);
}

.Net Web service Encrypt Decrypt Dataset

Does anyone know of examples which show how to encrypt a dataset at the client side and send it over to a web service and have it decrypted there?
Another question:
What i need to do is a send hundreds of rows of data from a client to a web service and have the web service update the database with these records. I can't think of any other way to do this without using a dataset. Is there a better method?
Thanks in advance!
As far as the encryption is concerned, why try to reinvent the wheel? Just connect to the webservice over SSL - it'll most likely be much safer than a homegrown alternative.
I would probably create a custom struct/object and send an array of those to the webservice rather than a DataSet. It will mean (slightly) less network traffic; it will make the webservice's WSDL more descriptive; and it will make it easier for any non-Microsoft apps to talk to the webservice, if that becomes necessary in the future.
EDIT: An example...
At the server-side you can declare a custom type (eg, ExampleUser), and then setup your method to accept an array of that type instead of a DataSet:
[WebService(Namespace="http://example.yourdomain.com/ExampleWebService/")]
public class ExampleWebService : System.Web.Services.WebService
{
// this is your custom type
public class ExampleUser
{
public int UserID { get; set; }
public string Name { get; set; }
public DateTime DateOfBirth { get; set; }
}
// this is your method
// accepts an array of ExampleUser rather than a DataSet
[WebMethod]
public void UploadUsers(ExampleUser[] usersArray)
{
// do something...
}
}
In the client application you would add a reference to the webservice. This will enable you to use the ExampleUser type declared in the server-side code above.
You could then just convert your DataSet to an array of ExampleUser objects before sending it to the webservice:
// get the number of rows in the DataTable
int rowCount = yourDataSet.Tables[0].Rows.Count;
// create an array of ExampleUser with the correct capacity
ExampleWebService.ExampleUser[] usersArray =
new ExampleWebService.ExampleUser[rowCount];
// iterate through each row in the table
for (int i = 0; i < rowCount; i++)
{
DataRow dr = yourDataSet.Tables[0].Rows[i];
// create an ExampleUser object and populate it from the DataRow columns
ExampleWebService.ExampleUser eu = new ExampleWebService.ExampleUser();
eu.UserID = (int)dr["User_ID"];
eu.Name = (string)dr["Name"];
eu.DateOfBirth = (DateTime)dr["Date_Of_Birth"];
// add the ExampleUser object to the array
usersArray[i] = eu;
}
// the array is populated so let's call the webservice
ExampleWebService.UploadUsers(usersArray);
EDIT: Another example...
If you're using .NET 3.5 then you can get the client-side down to just a few lines of code by using LINQ and object initialisers to create your array:
// create and populate the array
ExampleWebService.ExampleUser[] usersArray =
yourDataSet.Tables[0].AsEnumerable().Select
(
s => new ExampleWebService.ExampleUser()
{
UserID = (int)s["User_ID"],
Name = (string)s["Name"],
DateOfBirth = (DateTime)s["Date_Of_Birth"]
}
).ToArray();
// the array is populated so let's call the webservice
ExampleWebService.UploadUsers(usersArray);
Well, there are a lot of approaches to this. Since you are sending this over the wire, you could: 1) Write the data to an XML stream (this is very much what the DataSet is meant to do) then 2) you could compress the XML (the compression ratio would be best at this stage) then 3) Encrypt using one of the .NET cryptographic schemes and finally 4) decrypt, unzip, and deserialize your XML into a DataSet object or whatever you want to do with it.
Note, you might need to Base64 the result of the encryption. Another alternative is to not encrypt and use the Web Service over SSL and just use that native encryption. Depending on the type of data a DataSet may not be the best choice in terms of performance. You could send a CSV or JSON style data block; this would potentially be smaller, especially if there is only one "DataTable" object in your DataSet. A lot of this is situation dependent as far as what the best method do use is.

Resources