how to pass same parameter for different values in API - c#

I'm building an API to read data from stored procedure. I have to use same method to read different set of data by pass a different parameter in URL.
Below is how the paths look:
localhost:8080/api/PowerFeed/GetData/date/{date}/ClassID/{ClassID}
localhost:8080/api/PowerFeed/GetData/date/{date}/MainID/{MainID}
How do I use a single parameter to access different IDs in my method.
Method:
public IHttpActionResult GetData(DateTime date, int ClassID)
{
if(date == null)
{
Logger.Debug(
CommonConstants.Failed,
$"{nameof(PowerFeedController)}.nameof(GetData)}",
CorrelationId);
return BadRequest("Invalid request parameters. Cannot get data without
date");
}
var stopwatch = Stopwatch.StartNew();
IEnumerable<Power> records = null;
if(ClassID >= 0)
{
records = _dataAccessor.GetData(ApplicationName, date, ClassID);
Logger.Debug(stopwatch.ElapsedMilliseconds.ToString(),
$"{Operation.MeasureExecutionTimeInMilliseconds}-{nameof(PowerFeedDataAccessor)}.{nameof(_dataAccessor.GetData)}",
CorrelationId)
}
else
{
Logger.Debug(
CommonConstants.Failed,
$"{nameof(PowerFeedController)}.{nameof(GetData)}",
CorrelationId);
return BadRequest("Invalid request parameters. Cannot get data without ClassID");
}
return Ok(records);
}
In the above method, how would I pass MainID instead of ClassID, so that it can be called to get a different set of data?

Is the shape of the data that's returned (the schema) the same in both instances? If so, something more like this would be preferred:
localhost:8080/api/PowerFeed/GetData/date/{date}&classID={ClassID}&mainID=MainID
and then in the controller:
public IHttpActionResult GetData(DateTime date, int classID, int mainID)
{
if(date == null)
throw new NullReferenceException(); //etc
if(classID == 0 && mainID == 0)
throw new NullReferenceException(); //etc
// do method for each case (ClassID or MainID) here
}
If the shape / schema is different depending on the query, then you should have 2 separate controllers.

You could try a PUT like below:
[HttpPut]
[Route("startthread")]
public async Task<WebApiResponse> StartThread(StartThreadRequest request)
{
// logic
// return the response;
}

Related

Is it possible to avoid caching when calling the extension method GetOrCreateAsync on an IMemoryCache?

I'm using an IMemoryCache to cache a token retrieved from an Identity server.
In the past I've used the GetOrCreateAsync extension method available in the Microsoft.Extensions.Caching.Abstractions library.
It is very helpful because I can define the function and the expiration date at the same time.
However with a token, I won't know the expires in x amount of seconds value until the request is finished.
I want to account for a use case of the value not existing by not caching the token at all.
I have tried the following
var token = await this.memoryCache.GetOrCreateAsync<string>("SomeKey", async cacheEntry =>
{
var jwt = await GetTokenFromServer();
var tokenHasValidExpireValue = int.TryParse(jwt.ExpiresIn, out int tokenExpirationSeconds);
if (tokenHasValidExpireValue)
{
cacheEntry.AbsoluteExpirationRelativeToNow = TimeSpan.FromSeconds(tokenExpirationSeconds);
}
else // Do not cache value. Just return it.
{
cacheEntry.AbsoluteExpirationRelativeToNow = TimeSpan.FromSeconds(0); //Exception thrown. Value needs to be positive.
}
return jwt.token;
}
As you can see, an exception is thrown when I try to set an expiration of no time TimeSpan.FromSeconds(0).
Is there a way around this besides calling the Get and Set methods separately?
I would like to use the GetOrCreateAsync method if possible.
You can't actually accomplish this with the current extension because it will always create an entry BEFORE calling the factory method. That said, you can encapsulate the behavior in your own extension in a manner which feels very similar to GetOrCreateAsync.
public static class CustomMemoryCacheExtensions
{
public static async Task<TItem> GetOrCreateIfValidTimestampAsync<TItem>(
this IMemoryCache cache, object key, Func<Task<(int, TItem)>> factory)
{
if (!cache.TryGetValue(key, out object result))
{
(int tokenExpirationSeconds, TItem factoryResult) =
await factory().ConfigureAwait(false);
if (tokenExpirationSeconds <= 0)
{
// if the factory method did not return a positive timestamp,
// return the data without caching.
return factoryResult;
}
// since we have a valid timestamp:
// 1. create a cache entry
// 2. Set the result
// 3. Set the timestamp
using ICacheEntry entry = cache.CreateEntry(key);
entry.Value = result;
entry.AbsoluteExpirationRelativeToNow = TimeSpan.FromSeconds(tokenExpirationSeconds);
}
return (TItem)result;
}
}
You can then call your extension method in a very similar manner:
var memoryCache = new MemoryCache(new MemoryCacheOptions());
var token = await memoryCache.GetOrCreateIfValidTimestampAsync<string>("SomeKey", async () =>
{
var jwt = await GetTokenFromServer();
var tokenHasValidExpireValue = int.TryParse(jwt.ExpiresIn, out int tokenExpirationSeconds);
return (tokenExpirationSeconds, jwt.token);
}
I have the need for this and simply set the expiration time to now, I assume you can set it to past time as well just to be sure:
// Don't wanna cache if this is the result
if (key == null || key.Expiration < DateTime.UtcNow)
{
entry.AbsoluteExpiration = DateTimeOffset.Now;
return null;
}
Maybe something in these lines:
await _memoryCache.GetOrCreateAsync("key",
async entry =>
{
var value = // get your value here;
entry.AbsoluteExpiration = value != null
? DateTimeOffset.UtcNow.AddMinutes(5)
: DateTimeOffset.UtcNow; // don't cache nulls
return value;
});

MOQ testing Email Service and unable to get back the value

I am trying to test a Domain Service which is to send email after the order has been placed. This service has private methods so I called a method on the public interface which was calling this private service method. Issue is I cant seem to check CC on the email as this is in the private method.
the only approach I know to figure this if that value was saved as an interface property etc but its not. see the code below.
public int SendConsolidatedDespatchNotifications(int countOfWorkDays)
{
var sent = 0;
var trackings = _despatchLineRepository.GetTrackingWithoutDespatchNotificationInPreviousWorkDays(countOfWorkDays);
var trackingsWithinOrder = trackings == null
? new List<OrderLineTracking>()
: trackings.Where(dl => dl.DespatchReference != null).ToList();
trackingsWithinOrder.GroupBy(ot => ot.OrderKey).ForEach(
ot =>
{
if (SendConsolidatedDespatchNotifications(ot))
{
_despatchLineRepository.SetAsSent(ot.Select(ol => ol.DespatchLine));
sent++;
}
});
return sent;
}
private bool SendConsolidatedDespatchNotifications(IGrouping<int, OrderLineTracking> orderTrackingLines)
{
if (orderTrackingLines == null)
return false;
if (orderTrackingLines.Key == 0)
return false;
if (orderTrackingLines.Any())
{
var firstLine = orderTrackingLines.First();
var allOrderLines = _orderLineRepository.GetOrderLinesByOrderKey(firstLine.OrderKey);
var partiallyDespatchedLines = FindPartiallyDespatchedLines(orderTrackingLines);
var notDespatchedLines = FindNotDespatchedLines(allOrderLines, orderTrackingLines);
return SendConsolidatedDespatchedEmail(firstLine.DespatchReference, orderTrackingLines, partiallyDespatchedLines, notDespatchedLines);
}
return false;
}
private bool SendConsolidatedDespatchedEmail(
string poNumber,
IEnumerable<OrderLineTracking> despatchedLines,
IEnumerable<OrderLineTracking> partiallyDespatchedLines,
IEnumerable<OrderLine> notDespatchLines)
{
//we just assume that one PO have always just one order
var firstDespatchedLine = despatchedLines.First();
var order = firstDespatchedLine.OrderLine.OrderHeader;
if (order?.Customer == null)
return false;
var despatchGroups = new List<DespatchLineGroup>();
despatchedLines.GroupBy(dl => dl.DespatchReference).ForEach(
dl => despatchGroups.Add(
new DespatchLineGroup
{
DespatchReference = dl.Key,
DespatchedLines = dl,
TrackingWebLink = GetTrackingWebLinkFor(dl.First())
}));
var despatchNotificationEmail = new DespatchConsolidatedNotificationEmail(
order.Customer,
order,
despatchGroups,
CreateNotDespatchedItemsList(partiallyDespatchedLines, notDespatchLines));
var ccCustomer = _customerRepository.GetByCostCentreIdentifier(order.CostCentreIdentifier, order.Customer.Key);
var ccOnBasket = ccCustomer?.CostCentre;
if (ccOnBasket == null)
{
despatchNotificationEmail.To.Add(new EmailAddress(order.Customer.FullName, order.Customer.Login));
}
else
{
FillInSubaccountDetails(despatchNotificationEmail, ccCustomer, order, order.Customer, ccOnBasket);
}
despatchNotificationEmail.PopulateContentWithTags();
despatchNotificationEmail.SendAfter = firstDespatchedLine.DespatchDate;
despatchNotificationEmail.Save();
_log.InfoFormat("Despatch email {0} for {2} sent to {1}", "DespatchConsolidatedNotificationEmail", order.Customer.Login, poNumber);
return true;
}
private void FillInSubaccountDetails(
EmailTemplate email,
Customer ccCustomer,
OrderHeader order,
Customer masterAccount,
CostCentre ccOnBasket)
{
//send notifications to CostCentre account, which is on basket
email.To.Add(new EmailAddress(ccCustomer.FullName, ccCustomer.Login));
if (ccOnBasket.ReceiveNotifications) //send notifications to master only if CC is set so
{
email.To.Add(new EmailAddress(masterAccount.FullName, masterAccount.Login));
}
if (order.OrderPlacedBy.HasValue) //PD-2140 Sending email to Purchaser as well
{
var purchaser = _customerRepository.Get(order.OrderPlacedBy.Value);
if (purchaser?.Purchaser != null && purchaser.Purchaser.ReceiveNotifications)
{
email.To.Add(new EmailAddress(purchaser.FullName, purchaser.Login));
}
}
if ( order.ApprovedBy != null)
{
var approver = _customerRepository.Get(order.ApprovedBy.Value);
if(approver?.Approver != null && //has approver and its not MAH
approver.Approver.ReceiveNotifications)
email.To.Add(new EmailAddress(approver.FullName, approver.Login));
}
}
//this inherits from EmailTemplate which has save method.
public class DespatchConsolidatedNotificationEmail : EmailTemplate
{
public DespatchConsolidatedNotificationEmail() { }
public DespatchConsolidatedNotificationEmail(
Customer customer,
OrderHeader orderHeader,
List<DespatchLineGroup> despatchLines,
List<NotDespatchedLine> notDespatchLines)
{
AddEmailData(customer);
AddEmailData(orderHeader);
AddEmailData(despatchLines);
AddEmailData(notDespatchLines);
}
}
//below is the save method
public int Save()
{
var manageSave = Configuration.Container.Resolve<IWantToManageSaving>();
return manageSave.Save(this);
}
Note Email implements a abstract class which is EmailTemplate not an interface..
I want to figure out which emailAddress has been added ?
There are arguments pro and con unit testing private methods. I'll leave it up to you to decide if it's a good idea or not. Having said that you can use the PrivateObject class. Something along these lines:
Class c = new Class();
PrivateObject o = new PrivateObject(c);
var whatever = o.Invoke("FillInSubaccountDetails");
Assert.AreEqual(whatever, expected);
There is a problem here since your method returns void, there's no return value to assert. You may need to adapt your method?
So, based on all the code you provided:
Your Save method smells of bad practice. You shouldn't use an IoC container to manually resolve dependencies. If you had the IWantToManageSaving (I like the naming by the way :) ) injected via a ctor you could mock it in your test. If you had a var savingManagerMock = new Mock<IWantToManageSaving>(), you could then verify in your unit test that the Save method was called with a correctly setup instance of EmailTemplate. Something like:
// ASSERT
savingManagerMock.Verify(x => x.Save(It.IsAny<EmailTemplate>(
arg => arg.To.Contains(/* ... */));
Or something or the like, depends on the actual assertions you want.
Another way would be to abstract the construction of the DespatchConsolidatedNotificationEmail into a factory IDespatchConsolidatedNotificationEmailFactory, make it return a special mock of DespatchConsolidatedNotificationEmail and setup its Save method to, for example, save the current state of the EmailTemplate and then assert it. I would still lean towards the first solution, however.
On an ending note: as you can see testing this method is fairly complicated. That usually means it could be written better. In this case I see two red flags, first one is the explicit use of a Container which can always be avoided with dependency injection (manual resolution ain't no injection :P). Second is that this method is fairly complicated! It calls a lot of private methods and there's a lot of logic that can't be understood by a quick read through the method. You should consider splitting these private methods into maybe internal helper methods in another class that would be unit-tested separately. Then you could trust them when testing this public method, as they're basicaly a dependency at that point, setup mocks and just assert that correct internal methods are called and the method's contract is fulfilled.

Why is my class object empty after running JsonConvert.DeserializeObject<class>(string)?

I am trying to deserialize a JSON string into a large object; I'm using Angular to send my data to a controller. Data is present and correct when it arrives in the controller, but after deserialization, it's null. I've looked at some questions already, like this one here:
Deserialization of JSON.Net returns 'null'
Based on the answer to the question above, and the comment from the user who wrote the question, my code should work, but it doesn't.
Here's my code:
[HttpPost]
public OnlineOrderValidation Post(HttpRequestMessage request)
{
Task<string> result = request.Content.ReadAsStringAsync();
var json = result.Result;
OnlineOrder order = new OnlineOrder();
order = JsonConvert.DeserializeObject<OnlineOrder>(json);
// Save first
_orderFacade.UpdateCurrentOrder(CacheStore, order);
var validation = _orderFacade.ValidateOrder(order);
return validation;
}
The OnlineOrder class should accept all of the data from JsonConvert.DeserializeObject(json), but the values are either 0, null, or false.
I've tried DeserializeAnonymousType(string json, T anonObject) and JavasScriptSerializer.Deserialize(string data) as well, and they both yield an empty OnlineOrder object with null values.
I've also tried passing the object to the controller like this:
public OnlineOrderValidation Post([FromBody]string ooJson)
{
var order = JsonConvert.DeserializeObject<OnlineOrder>(ooJson);
//work with order object...
}
and like this
public OnlineOrderValidation Post([FromUri]string ooJson)
{
var order = JsonConvert.DeserializeObject<OnlineOrder>(ooJson);
//work with order object...
}
I wonder if the problem lies with 'ReadAsStringAsync()'. I noticed that it spits out each property on OnlineOrder out of order. The async part of this method must add each field/value pair as it finishes parsing the string, and since the first field in result doesn't match the first field on the OnlineOrder object, maybe JsonConvert.DeserializeObject() just stops execution.
Do I need to make a wrapper class for OnlineOrder and deserialize to that? Do I need to reorder the fields in my 'result' object before deserializing? Is there something else I'm doing wrong? Thanks all!
EDIT: Here is my JSON, as requested. The actual object is much larger than this, but you get the idea. The JSON string has valid data, but is either nulled or zeroed out after deserialization.
{"isTMIOfferEligible":false,
"tmiOfferCode":null,
"tmiUpgradeOfferCode":null,
"id":"a34mdxdivt0svmu1okucidbi",
"accountNumber":"111111111",
"transferAccountNumber":"222222222",
"serviceAddress":
{"address":"3922 N STREET AVE",
"addressLine":"3901 N STREET AVE",
"suite":"SYNCRD",
"city":"SOOFOO",
"state":"SD",
"zip":"57000",
"franchise":"111",
"market":"1 "},
"transferAddress":
{"disconnectDate":null,
"transferFromAddress":
{"address":"1234 SOME PLACE",
"addressLine":"1234 SOME PLACE",
"suite":"SYNCRD",
"city":"SOOFOO",
"state":"SD",
"zip":"57000",
"franchise":"123",
"market":"Z "
}
}
}
EDIT 2
I've updated my code to deserialize the JSON string from a custom, async method, and instantiate OnlineOrder when it's assigned:
try
{
var orderTask = ReadStringAsynchronously(request);
OnlineOrder order = orderTask.Result;
//work with order object
}
catch (Exception ex) { //...}
private async Task<OnlineOrder> ReadStringAsynchronously(HttpRequestMessage request)
{
try
{
var json = await request.Content.ReadAsStringAsync();
OnlineOrder orderAsync = JsonConvert.DeserializeObject<OnlineOrder>(json);
return orderAsync;
}
catch (Exception)
{
throw new Exception("Error occurred while reading string asynchronously.");
}
}
The order object on the task is still null.
The issue, in this case, was in the JSON string itself. There was HTML nestled within the JSON string that was not being encoded. Before posting to the server, I used encodeURIComponent(value) in a custom method:
function encodeOrderHtml(order) {
debugger;
var o = order;
//scrub HTML from product options
if (o && o.serviceDiscount) {
var sd = o.serviceDiscount;
if (sd.description !== "" && sd.description !== null) {
sd.description = encodeURIComponent(sd.description);
}
if (sd.descriptionNew !== "" && sd.descriptionNew !== null) {
sd.descriptionNew = encodeURIComponent(sd.descriptionNew);
}
if (sd.displayNameNew !== "" && sd.displayNameNew !== null) {
sd.displayNameNew = encodeURIComponent(sd.displayNameNew);
}
if (sd.name !== "" && sd.name !== null) {
sd.name = encodeURIComponent(sd.name);
}
}
return o;
}
I was able to get valid data to the server by posting the object in the body of the request.

Same "Where" each time returns different result

I have a .NET service (using Entity Framework 6.0.0-rc-1)
public class MyController : ApiController
{
protected DbConnection _connection = new DbConnection();
public HttpResponseMessage MyProcedure1()
{
...
var dayPlans = _connection.DayPlans
.Where(dp => dp.period >= start && dp.period < end);
if (dayPlans.Count() > 0)
{
//success
}
else
{
//fail
}
...
}
public HttpResponseMessage MyProcedure2()
{
...
var dayPlans = _connection.DayPlans
.Where(dp => dp.period >= start && dp.period < end);
if (dayPlans.Count() > 0)
{
//success
}
else
{
//fail
}
...
}
}
These procedures being used simultaneously by client side. The thing is from time to time dayPlans.Count() equals zero while it should not. I've double checked filtration params and database content, there are few records that has to be selected from db, but they are not.
Another important moment is when LINQ request in one method fails, it always fails in another. But when these procedures are called one after another they might fail in all possible variants - first request failed, second succeeded; first failed second failed; etc.
What might be the problem?
That was my mistake, date boundaries weren't calculated correctly

Returning the first method that works, more elegant way?

Recently I've found myself writing methods which call other methods in succession and setting some value based on whichever method returns an appropriate value first. What I've been doing is setting the value with one method, then checking the value and if it's not good then I check the next one. Here's a recent example:
private void InitContent()
{
if (!String.IsNullOrEmpty(Request.QueryString["id"]))
{
Content = GetContent(Convert.ToInt64(Request.QueryString["id"]));
ContentMode = ContentFrom.Query;
}
if (Content == null && DefaultId != null)
{
Content = GetContent(DefaultId);
ContentMode = ContentFrom.Default;
}
if (Content == null) ContentMode = ContentFrom.None;
}
Here the GetContent method should be returning null if the id isn't in the database. This is a short example, but you can imagine how this might get clunky if there were more options. Is there a better way to do this?
The null coalescing operator might have the semantics you want.
q = W() ?? X() ?? Y() ?? Z();
That's essentially the same as:
if ((temp = W()) == null && (temp = X()) == null && (temp == Y()) == null)
temp = Z();
q = temp;
That is, q is the first non-null of W(), X(), Y(), or if all of them are null, then Z().
You can chain as many as you like.
The exact semantics are not quite like I sketched out; the type conversion rules are tricky. See the spec if you need the exact details.
You could also do something a little more sneaky, along the lines of this:
private Int64? GetContentIdOrNull(string id)
{
return string.IsNullOrEmpty(id) ? null : (Int64?)Convert.ToInt64(id);
}
private Int64? GetContentIdOrNull(DefaultIdType id)
{
return id;
}
private void InitContent()
{
// Attempt to get content from multiple sources in order of preference
var contentSources = new Dictionary<ContentFrom, Func<Int64?>> {
{ ContentFrom.Query, () => GetContentIdOrNull(Request.QueryString["id"]) },
{ ContentFrom.Default, () => GetContentIdOrNull(DefaultId) }
};
foreach (var source in contentSources) {
var id = source.Value();
if (!id.HasValue) {
continue;
}
Content = GetContent(id.Value);
ContentMode = source.Key;
if (Content != null) {
return;
}
}
// Default
ContentMode = ContentFrom.None;
}
That would help if you had many more sources, at the cost of increased complexity.
Personally, I find when I have lots of statements that are seemingly disparate, it's time to make some functions.
private ContentMode GetContentMode(){
}
private Content GetContent(int id){
}
private Content GetContent(HttpRequest request){
return GetContent(Convert.ToInt64(request.QueryString["id"]));
}
private void InitContent(){
ContentMode mode = GetContentMode();
Content = null;
switch(mode){
case ContentMode.Query:
GetContent(Request);
break;
case ContentMode.Default:
GetContent(DefaultId);
break;
case ContentMode.None:
... handle none case...
break;
}
}
This way, you separate your intentions - first step, determine the content mode. Then, get the content.
I suggest you try some kind of Factory design pattern for this case. You can abstract the content create procedure by register different creators. Moreover, you can add preference on each creator for your own logic. Besides, I suggest you encapsulate all data related to Content just like "ContentDefinition" class from other's post.
In general, you need to know that there is always a trade off between flexibility and efficiency. Sometime your first solution is good enough:)
Ok, because I noticed a bit late that you actually wanted the ContentFrom mode as well, I've done my best to come up with a translation of your sample below my original answer
In general I use the following paradigm for cases like this. Search and replace your specific methods here and there :)
IEnumerable<T> ValueSources()
{
yield return _value?? _alternative;
yield return SimpleCalculationFromCache();
yield return ComplexCalculation();
yield return PromptUIInputFallback("Please help by entering a value for X:");
}
T EffectiveValue { get { return ValueSources().FirstOrDefault(v => v!=null); } }
Note how you can now make v!=null arbitrarily 'interesting' for your purposes.
Note also how lazy evaluation makes sure that the calculations are never done when _value or _alternative are set to 'interesting' values
Here is my initial attempt at putting your sample into this mold. Note how I added quite a lot of plumbing to make sure this actually compiles into standalone C# exe:
using System.Collections.Generic;
using System.Linq;
using System;
using T=System.String;
namespace X { public class Y
{
public static void Main(string[]args)
{
var content = Sources().FirstOrDefault(c => c); // trick: uses operator bool()
}
internal protected struct Content
{
public T Value;
public ContentFrom Mode;
//
public static implicit operator bool(Content specimen) { return specimen.Mode!=ContentFrom.None && null!=specimen.Value; }
}
private static IEnumerable<Content> Sources()
{
// mock
var Request = new { QueryString = new [] {"id"}.ToDictionary(a => a) };
if (!String.IsNullOrEmpty(Request.QueryString["id"]))
yield return new Content { Value = GetContent(Convert.ToInt64(Request.QueryString["id"])), Mode = ContentFrom.Query };
if (DefaultId != null)
yield return new Content { Value = GetContent((long) DefaultId), Mode = ContentFrom.Default };
yield return new Content();
}
public enum ContentFrom { None, Query, Default };
internal static T GetContent(long id) { return "dummy"; }
internal static readonly long? DefaultId = 42;
} }
private void InitContent()
{
Int64? id = !String.IsNullOrEmpty(Request.QueryString["id"])
? Convert.ToInt64(Request.QueryString["id"])
: null;
if (id != null && (Content = GetContent(id)) != null)
ContentMode = ContentFrom.Query;
else if(DefaultId != null && (Content = GetContent(DefaultId)) != null)
ContentMode = ContentFrom.Default;
else
ContentMode = ContentFrom.None;
}

Categories

Resources