reduce complexity of service class - c#

I am developing a web api using WCF Web Api preview 5. At the moment I have a resource class fully functional, how ever I noticed my methods inside this resouce are getting complex.
For example:
[WebInvoke(UriTemplate = "{EhrID}/PhysicalTest",Method="POST")]
public HttpResponseMessage<DTO.PhysicalTest> PostPhysicalTest(int EhrID, DTO.PhysicalTest PhysicalTestDTO)
{
var EHR = repository.FindById(EhrID);
var PhysicalTest = Mapper.Map<DTO.PhysicalTest, PhysicalTest>(PhysicalTestDTO);
if (PhysicalTest == null)
{
var response = CreateResponseForException("No object to store", HttpStatusCode.BadRequest);
throw new HttpResponseException(response);
}
try
{
if (EHR.PhysicalTests == null)
{
EHR.PhysicalTests = new List<PhysicalTest>();
}
PhysicalTest.CreationDate = DateTime.Now;
EHR.PhysicalTests.Add(PhysicalTest);
_unitOfWork.Commit();
return new HttpResponseMessage<DTO.PhysicalTest>(PhysicalTestDTO, HttpStatusCode.Created);
}
catch (Exception ex) {
var response = CreateResponseForException("Cannot create Physical Test", HttpStatusCode.InternalServerError);
throw new HttpResponseException(response);
}
}
As you may notice this method has the task of posting a new Physical Test, but it's actually validating my model too (I'm missing lots of validations still, property validations), which should not be this class concern. If there any approachable way to reduce the complexity of the methods inside de resource?

I would split it up into smaller more focused methods. I might also start using instance variables instead of passing all these arguments around, but for the sake of this post I've rewritten it without pushing stuff to instance variables.
[WebInvoke(UriTemplate = "{EhrID}/PhysicalTest",Method="POST")]
public HttpResponseMessage<DTO.PhysicalTest> PostPhysicalTest(int EhrID, DTO.PhysicalTest PhysicalTestDTO)
{
var EHR = repository.FindById(EhrID);
var PhysicalTest = Mapper.Map<DTO.PhysicalTest, PhysicalTest>(PhysicalTestDTO);
if (PhysicalTest == null)
{
var response = CreateResponseForException("No object to store", HttpStatusCode.BadRequest);
throw new HttpResponseException(response);
}
PostPhysicalTest(EHR, PhysicalTest);
return new HttpResponseMessage<DTO.PhysicalTest>(PhysicalTestDTO, HttpStatusCode.Created);
}
private void PostPhysicalTest(EHR ehr, PhysicalTest physicalTest)
{
try
{
CreatePhysicalTest(ehr, physicalTest);
}
catch (Exception ex) {
var response = CreateResponseForException("Cannot create Physical Test", HttpStatusCode.InternalServerError);
throw new HttpResponseException(response);
}
}
private void CreatePhysicalTest(EHR ehr, PhysicalTest physicalTest)
{
if (ehr.PhysicalTests == null)
{
ehr.PhysicalTests = new List<PhysicalTest>();
}
physicalTest.CreationDate = DateTime.Now;
ehr.PhysicalTests.Add(physicalTest);
_unitOfWork.Commit();
}

Related

Recursive method is returning first iteration object C#

static void Main(string[] args)
{
token objtoken = new token();
var location = AddLocations();
OutPutResults outPutResultsApi = new OutPutResults();
GCPcall gCPcall = new GCPcall();
OutPutResults finaloutPutResultsApi = new OutPutResults();
var addressdt = new AddressDataDetails();
finaloutPutResultsApi.addressDatas = new List<AddressDataDetails>();
Console.WriteLine("Hello World!");
List<string> placeId = new List<string>();
var baseUrl = "https://maps.googleapis.com/maps/api/place/textsearch/json?";
var apiKey = "&key=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx".Trim();
foreach (var itemlocations in location)
{
var searchtext = "query=" + itemlocations.Trim();
var finalUrl = baseUrl + searchtext + apiKey;
gCPcall.RecursiveApiCall(finalUrl, ref placeId, objtoken.NextToken);
}
var ids = gCPcall.myPalceid;
}
public List<string> RecursiveApiCall(string finalUrl, ref List<string> placeId, string nextToken = null)
{
try
{
var token = "&pagetoken=" + nextToken;
using (var client = new HttpClient())
{
var responseTask = client.GetAsync(finalUrl + token);
responseTask.Wait();
var result = responseTask.Result;
if (result.IsSuccessStatusCode)
{
var readTask = result.Content.ReadAsStringAsync();
readTask.Wait();
var students = readTask.Result;
Rootobject studentsmodel = JsonConvert.DeserializeObject<Rootobject>(students);
nextToken = studentsmodel.next_page_token;
foreach (var item in studentsmodel.results)
{
placeId.Add(item.place_id);
}
}
}
if (nextToken != null)
{
RecursiveApiCall(finalUrl, ref placeId, nextToken);
}
return placeId;
}
catch (Exception ex)
{
throw;
}
}
My recursive method has some issue. Here whenever I am debugging this code it work fine. It goes in recursive call twice.
As debugging result I am getting list place_id with 20 items in first call and next call 9 items total 29 items in place_id object which is correct in static main method.
But if I run without debugging mode I am getting only 20 place_id. next recursive iteration data is missing even if it has next valid token.
I don't have any clue why this is happening. Can someone tell me what is the issue with my code?
Here are my suggestions, which may or may not solve the problem:
// First of all, let's fix the signature : Go async _all the way_
//public List<string> RecursiveApiCall(string finalUrl, ref List<string> placeId, string nextToken = null)
// also reuse the HttpClient!
public async Task ApiCallAsync(HttpClient client, string finalUrl, List<string> placeId, string nextToken = null)
{
// Loop, don't recurse
while(!(nextToken is null)) // C# 9: while(nextToken is not null)
{
try
{
var token = "&pagetoken=" + nextToken;
// again async all the way
var result = await client.GetAsync(finalUrl+token);
if (result.IsSuccessStatusCode)
{
// async all the way!
var students = await result.Content.ReadAsStringAsync();
Rootobject studentsmodel = JsonConvert.DeserializeObject<Rootobject>(students);
nextToken = studentsmodel.next_page_token;
foreach (var item in studentsmodel.results)
{
// Will be reflected in main, so no need to return or `ref` keyword
placeId.Add(item.place_id);
}
}
// NO recursion needed!
// if (nextToken != null)
// {
// RecursiveApiCall(finalUrl, ref placeId, nextToken);
// }
}
catch (Exception ex)
{
// rethrow, only is somewhat useless
// I'd suggest using a logging framework and
// log.Error(ex, "Some useful message");
throw;
// OR remove try/catch here all together and wrap the call to this method
// in try / catch with logging.
}
}
Mind that you'll need to make your main :
async Task Main(string[] args)
and call this as
await ApiCallAsync(client, finalUrl, placeId, nextToken);
Also create an HttpClient in main and reuse that:
"HttpClient is intended to be instantiated once and re-used throughout the life of an application. Instantiating an HttpClient class for every request will exhaust the number of sockets available under heavy loads. This will result in SocketException errors." - Remarks
... which shouldn't do much harm here, but it's a "best practice" anyhow.
Now, as to why you get only 20 instead of expected 29 items, I cannot say if this resolves that issue. I'd highly recommend to introduce a Logging Framework and make log entries accordingly, so you may find the culprid.

Cancellation Token Disposed Exception

I had this piece of code that initially worked fine. However, After adding it to a class where I store my methods that are reused, it keeps failing. The exception that is caught states that the CancellationTokenSource has been Disposed. Can someone point me in the right direction?
I have tried creating a new client and Adding CancellationToken.None to the PutAsync() method from HTTPClient Class but it still fails with the CancellationTokenSource Disposed exception.
public async void AddProduct(Product product)
{
string storeId = "";
try
{
var storeData = JObject.Parse(Connect.Json).SelectToken("store").ToString();
var stores = JsonConvert.DeserializeObject<List<Store>>(storeData);
var store = stores[0];
storeId = store.Id;
store.Products.Add(product);
ProdInfo info = new Info();
foreach(Product p in store.Products)
{
info.AddedProducts = + p.Id;
}
var content = JsonConvert.SerializeObject(info);
using (Connect.Client)
using (var response = await Connect.Client.PutAsync(_url + "/stores/" + storeId, new StringContent(content)))
{
var cont = response.Content;
string result = await cont.ReadAsStringAsync();
if ((int)response.StatusCode == 200)
{
this.JobResult = result;
//this.JobResult = "Store has been successfully updated";
}
else
{
this.JobResult = result;
//this.JobResult = "Store was not updated!";
}
}
}
catch (Exception ex)
{
//this.JobResult = "Store has not been updated due to an error.";
this.JobResult = ex.Message;
}
}
I was able to solve this by simple removing 'using(Connect.Client)' from all of my methods. As #sellotape stated, They were disposing of the HttpClient before I was able to use it again. Thank you all for your contributions.

(Async & await) vs (without Async & await) in Web API

I am new with Async and await using C# Programming. In WebAPI, we have created two API Controllers one with Async and await Programming and other is without that. We have done load testing using JMeter and we have got following results.
Users Sync Async
100 No Errors No Errors
500 No Errors No Errors
750 No Errors Errors - (59.0 %) - 502 Bad Gateway
763 No Errors Errors
764 No Errors Errors
765 Errors - (0.13 %) - 502 Bad Gateway Errors
1000 Errors Errors
Can you any please explain/suggest which approach is best or how can we proceed ?
API Code :
GetPersonalDetailsController - Async and await Used
public async Task<IHttpActionResult> GET([FromUri] RequestQueryListDTO objAPIRequest)
{
DateTime startResponseTime = DateTime.Now;
Response objResponse = null;
string strResponse = string.Empty;
var HeaderType = Request.Content.Headers.ContentType;
ProductBAL objProductBAL = null;
try
{
if (objAPIRequest != null)
{
Task<Response> tskGetProductDetails = Task<Response>.Run(() =>
{
objProductBAL = new ProductBAL();
return objProductBAL.GetProductDetails(objAPIRequest);
//Business Access Layer Logic calling
});
objResponse = await tskGetProductDetails;
}
else
{
objResponse = new Response();
objResponse.ReturnCode = -1;
objResponse.ReturnMessage = "Missing Parameters.";
}
}
catch (Exception ex)
{
\\ Exception Logging
}
finally
{
objProductBAL = null;
}
objResponse.ResponseTime = Math.Round((DateTime.Now - startResponseTime).TotalMilliseconds).ToString();
if (objResponse.ReturnCode == Convert.ToInt32(General.ReturnCode))
{
return Content<Response>(HttpStatusCode.BadRequest, objResponse);
}
else
{
return Ok(objResponse);
}
}
========================================================================
GetPDPController - Without using Async and await
public IHttpActionResult GET([FromUri] RequestQueryListDTO objAPIRequest)
{
DateTime startResponseTime = DateTime.Now;
Response objResponse = null;
string strResponse = string.Empty;
var HeaderType = Request.Content.Headers.ContentType;
try
{
if (objAPIRequest != null)
{
//Business Access Layer Logic calling
}
else
{
objResponse = new Response();
objResponse.ReturnCode = -1;
objResponse.ReturnMessage = "Missing Parameters.";
}
}
catch (Exception ex)
{
// Exception Logging Code
}
finally
{
objProductBAL = null;
}
objResponse.ResponseTime = Math.Round((DateTime.Now - startResponseTime).TotalMilliseconds).ToString();
if (objResponse.ReturnCode == Convert.ToInt32(General.ReturnCode))
{
return Content<Response>(HttpStatusCode.BadRequest, objResponse);
}
else
{
return Ok(objResponse);
}
}
My suggestion is have two methods, one Async and one not. That way you can test more.
GetProductDetails
GetProductDetailsAsync
you would then need to change the signature of the calling method aka GET
public IHttpActionResult GET([FromUri] RequestQueryListDTO objAPIRequest)
{
var objResponse = new Response();
//check the properties of objAPIRequest
if(bad)
{
//add stuff if you want
return Content<Response>(HttpStatusCode.BadRequest, objResponse);
}
//Business Access Layer Logic calling
//-----------------------
ProductBAL objProductBAL = new ProductBAL();
//you need to change this to async
var productDetails = objProductBAL.GetProductDetails(objAPIRequest);
//-----------------------
return Ok(objResponse);
}

Update in couchbase

I need some help in update using couchbase. I have a task in my page. If the user clicks the like then the likes count should be updated in my couchbase bucket. I have tried my own update handler code but that has some time latency. I have included my update code too below.
This is my code for liking a task...
public ResponseVO LikeTask(LikeVO likeVO)
{
ResponseVO response = new ResponseVO();
try
{
if (!isLiked(likeVO.TaskID, likeVO.UserID))
{
UpdateTaskDB likeUpdate = new UpdateTaskDB();
UpdateTaskVO updatetaskvo = new UpdateTaskVO();
updatetaskvo.FieldName = "Likes";
LikeVO tempvo = new LikeVO();
tempvo.LikedOn = DateTime.Now.ToString();
tempvo.UserID = likeVO.UserID;
tempvo.UserName = likeVO.UserName;
tempvo.TaskID = likeVO.TaskID;
updatetaskvo.ObjectValue = tempvo;
updatetaskvo.TaskID = likeVO.TaskID;
likeUpdate.UpdateDocument(updatetaskvo);
}
response.StatusMessage = "Liked Successfully";
}
catch (Exception ex)
{
response.StatusCode = "0";
response.StatusMessage = ex.Message;
}
return response;
}
My own update handler code:
public class UpdateTaskDB
{
CouchbaseClient oCouchbase;
public UpdateTaskDB()
{
oCouchbase = new CouchbaseClient("vwspace", "");
}
public TaskVO GetTaskByID(string task_id)
{
TaskVO results = null;
try
{
String str1;
str1 = (String)oCouchbase.Get(task_id);
results = JsonConvert.DeserializeObject<TaskVO>(str1);
}
catch (Exception ex)
{
}
return results;
}
public void UpdateDocument(UpdateTaskVO inputParams)
{
try
{
var client = new CouchbaseClient("vwspace", "");
TaskVO taskDoc = GetTaskByID(inputParams.TaskID);
switch (inputParams.FieldName)
{
case "Likes":
List<LikeVO> docLikes = taskDoc.likes;
docLikes.Add((LikeVO)inputParams.ObjectValue);
taskDoc.likes = docLikes;
break;
case "UnLike":
LikeVO unlikevo = (LikeVO)inputParams.ObjectValue;
for (int count = 0; count < taskDoc.likes.Count; count++)
{
if (taskDoc.likes[count].UserID.Equals(unlikevo.UserID))
{
unlikevo = taskDoc.likes[count];
break;
}
}
taskDoc.likes.Remove(unlikevo);
break;
default:
break;
}
String json = JsonConvert.SerializeObject(taskDoc);
client.Store(StoreMode.Set, inputParams.TaskID, json);
}
catch (Exception ex)
{
Console.Write("Exception :" + ex.Message);
}
}
}
Is ther any other way to handle this update in couchbase? Kindly help me out..
The latency you're seeing is likely due to the fact that you're creating two instances of the CouchbaseClient for each click. Creating an instance of a CouchbaseClient is an expensive operation, because of the bootstrapping and configuration setup that takes place.
There are a couple of different approaches you can take to minimize how frequently you create CouchbaseClient instances. One would be to create a static client that is reused by your data access classes. Another approach for web apps is to associate instances with HttpApplication instances. For an example of the Web approach, see my (incomplete) sample project on GitHub below.
https://github.com/jzablocki/couchbase-beer.net/blob/master/src/CouchbaseBeersWeb/Models/WebRepositoryBase%271.cs
Also, I would suggest using CAS operations when updating a document's like count. You want to make sure that a "like" vote doesn't cause the entire document to be update from a stale read.
For example:
public TaskVO GetTaskByID(string task_id)
{
var getResult = oCouchbase.ExecuteGet<string>(task_id);
var results = JsonConvert.DeserializeObject<TaskVO>(str1.Value);
results.Cas = getResult.Cas; //Here I'm suggesting adding a Cas property to your TaskVO
return results;
}
Then on your update:
public void UpdateDocument(UpdateTaskVO inputParams)
{
try
{
TaskVO taskDoc = GetTaskByID(inputParams.TaskID);
switch (inputParams.FieldName)
{
...
}
String json = JsonConvert.SerializeObject(taskDoc);
client.ExecuteStore(StoreMode.Set, inputParams.TaskID, json, taskDoc.Cas);
//this will fail if the document has been updated by another user. You could use a retry strategy
}
catch (Exception ex)
{
Console.Write("Exception :" + ex.Message);
}
}

How do I access the CodeDomProvider from a class inheriting from Microsoft.VisualStudio.TextTemplating.VSHost.BaseCodeGeneratorWithSite?

Does anyone know how to get a CodeDomProvider in the new Microsoft.VisualStudio.TextTemplating.VSHost.BaseCodeGeneratorWithSite from the Visual Studio 2010 SDK? I used to get access to it just by in mere inheritance of the class Microsoft.CustomTool.BaseCodeGeneratorWithSite, but now with this new class it is not there. I see a GlobalServiceProvider and a SiteServiceProvider but I can't find any example on how to use them.
Microsoft.VisualStudio.TextTemplating.VSHost.BaseCodeGeneratorWithSite:
http://msdn.microsoft.com/en-us/library/bb932625.aspx
I was to do this:
public class Generator : Microsoft.VisualStudio.TextTemplating.VSHost.BaseCodeGeneratorWithSite {
public override string GetDefaultExtension() {
// GetDefaultExtension IS ALSO NOT ACCESSIBLE...
return this.InputFilePath.Substring(this.InputFilePath.LastIndexOf(".")) + ".designer" + base.GetDefaultExtension();
}
// This method is being called every time the attached xml is saved.
protected override byte[] GenerateCode(string inputFileName, string inputFileContent) {
try {
// Try to generate the wrapper file.
return GenerateSourceCode(inputFileName);
} catch (Exception ex) {
// In case of a faliure - print the exception
// as a comment in the source code.
return GenerateExceptionCode(ex);
}
}
public byte[] GenerateSourceCode(string inputFileName) {
Dictionary<string, CodeCompileUnit> oCodeUnits;
// THIS IS WHERE CodeProvider IS NOT ACCESSIBLE
CodeDomProvider oCodeDomProvider = this.CodeProvider;
string[] aCode = new MyCustomAPI.GenerateCode(inputFileName, ref oCodeDomProvider);
return Encoding.ASCII.GetBytes(String.Join(#"
", aCode));
}
private byte[] GenerateExceptionCode(Exception ex) {
CodeCompileUnit oCode = new CodeCompileUnit();
CodeNamespace oNamespace = new CodeNamespace("System");
oNamespace.Comments.Add(new CodeCommentStatement(MyCustomAPI.Print(ex)));
oCode.Namespaces.Add(oNamespace);
string sCode = null;
using (StringWriter oSW = new StringWriter()) {
using (IndentedTextWriter oITW = new IndentedTextWriter(oSW)) {
this.CodeProvider.GenerateCodeFromCompileUnit(oCode, oITW, null);
sCode = oSW.ToString();
}
}
return Encoding.ASCII.GetBytes(sCode );
}
}
Thanks for your help!
You can access the CodeDomProvider via the SiteServiceProvider by asking for the SVSMDCodeDomProvider service.
Something along the lines of:
IVSMDCodeDomProvider provider = SiteServiceProvider.
GetService(typeof(SVSMDCodeDomProvider)) as IVSMDCodeDomProvider;
if (provider != null)
{
codeDomProvider = provider.CodeDomProvider as CodeDomProvider;
}
The SiteServiceProvider is the limited scope service provider exposed by the site of a SingleFileGenerator, whereas the GlobalServiceProvider is VS' main service provider that you can ask for any globally-scoped interface.
Hope this helps.
Gareth

Categories