Service method with changing parameter - c#

I have a service method:
public long InsertMessage(OutgoingInEntity input)
{
var request = new InsertOutgoingMessageRequest
{
Id = input.Id
... // fields
};
return Util.UsingWcfSync<IOutgoing, long>(client => client.InsertOutgoing(request));
}
I want to reuse this method in other context because I want 1 method which call this specific service, but the parameter OutgoingInEntity can change. When I call this method with other entities, the fields used in InsertOutgoingMessageRequest will be available and I will map like I did with the var request I cannot initiate InsertOutgoingMessageRequest in other context.
How can I say this input parameter is like generic and can be used for all kind of entities?

If you want to manage the object you receive you can just do this:
public long InsertMessage(Object input)
{
OutgoingInEntity yourObj = (OutgoingInEntity)input;
///.. your code ..///
}
Then you can do just the same for whatever you need.

Related

Can we add LINQ where condition of method parameters

Below is my existing method using inside some condition
public List<MenuItem> getTopMenu(List<Role> Id)
{
// here many lines code for checking id value
}
I am changing above method to check where condition inside method parameter like below but not working code
public List<MenuItem> getTopMenu(List<Role> Id.Where(r=>r.status=="Active"))
{
}
Error is coming i need to pass only active id inside method i can not write individually to every method.
Move the filter inside the method:
public List<MenuItem> getTopMenu(List<PersonRoleDTO> Id)
{
Id = Id.Where(r=>r.status=="Active").ToList();
// Whatever else
}
You cannot put statements as parameters to a method.
I think it would better if you add the default parameter (status="Active") on your method and use it like below.
Also this makes your method more usable if you want to query with another status later.
public List<MenuItem> getTopMenu(List<PersonRoleDTO> Id, string status="Active")
{
var query = Id.Where(r=>r.status==status);
//your logic
}
This is syntax and a design issue.
Syntax:
You can't pass a condition as a parameter it doesn't make sense.
Design:
You have multiple ways of doing this. My two suggestions are, either you can have a method to return the predicate to obtain only the active "Roles":
private Func<Role, bool> OnlyActiveRoles(IList roles) => r => r.status == "Active";
Or you can just filter the Roles list before calling those methods.

How to query a SingleResult object in a ASP.NET based server?

How do I query a SingleResult object in a ASP.NET based server? Please help with the below code.
public class UserController : TableController<User>
{
...
public Task DeleteUser(string id)
{
SingleResult<User> user = Lookup(id);
// I can drill down into user via the debugger and see the data
// I'm looking for. But I don't know how to translate what I see
// in the debugger to a .Net call.
// This is what I see in the debugger:
// new System.Linq.SystemCore_EnumerableDebugView
// <AlertMeService.DataObjects.User>(result.Queryable).Items[0].GroupNm
// which equals "fooGrp"
// The below call doesn't work
// string GroupNm = user.GroupNm;
// I need GroupNm from the object to send a group notification
PostNotification(user.GroupNm, ...);
return DeleteAsync(id);
}
...
Any help is greatly appreciated.
SingleResult returns an IQueryable, so use the Linq Single or SingleOrDefault methods to execute the query and get the result.
Single will throw an exception if 0, 2 or more values are returned, and SingleOrDefault will allow either 0 or 1 value and will throw if 2 or more than values are returned. If you want multiple values then use First/FirstOrDefault or Last/LastOrDefault, or some other terminal Linq method, as appropriate
SingleResult<User> userQuery = Lookup(id);
User user = userQuery.SingleOrDefault();
if( user == null ) {
}
else {
}
According to your description, I have checked the SingleResult class:
public sealed class SingleResult<T> : SingleResult
{
//
// Summary:
// Initializes a new instance of the System.Web.Http.SingleResult`1 class.
//
// Parameters:
// queryable:
// The System.Linq.IQueryable`1 containing zero or one entities.
public SingleResult(IQueryable<T> queryable);
//
// Summary:
// The System.Linq.IQueryable`1 containing zero or one entities.
public IQueryable<T> Queryable { get; }
}
Per my understanding, you could modify your DeleteUser method as follows:
public Task DeleteUser(string id)
{
SingleResult<User> result = Lookup(id);
var user=result.Queryable.SingleOrDefault();
if(user!=null)
{
//I need GroupNm from the object to send a group notification
PostNotification(user.GroupNm, ...);
return DeleteAsync(id);
}
return Task.FromException($"the specific user with userid:{id} is not found.");
}

MVC requests interfering with calls to AutoQuery-enabled ServiceStack API

I have an MVC solution that contains a ServiceStack API alongside an MVC UI that makes calls to the API services. Some of those services are AutoQuery GET endpoints, and I'm running into a problem where the ServiceStack service picks up posted form values or unrelated querystring values and throws argument errors when I call the services.
I've tried a number of ways of calling the services:
using (var fooSvc = new HostContext.ResolveService<FooService>(HttpContext))
{
var foos = (QueryResponse<Foo>)fooSvc.Get(new Foos());
// Do stuff here
}
No dice. Posted form values screw it up and I get a System.FormatException saying Input string was not in a correct format on this line in my service:
var q = AutoQuery.CreateQuery(request, Request.GetRequestParams());
So I tried:
var foos = (QueryResponse<Foo>)HostContext.ServiceController.Execute(new Foos());
In this case, I get a System.NotImplementedException saying it couldn't find a Post(Foos) or Any(Foos) (the call in question is a GET).
I'm sure I'm missing something simply. Mythz, got another rescue for me?
EDIT: I hand-typed that code...the initial block had AutoQuery when I meant QueryResponse...
EDIT 2: Here is the general structure of my AutoQuery services. These are all GET on the service because those endpoints also need to support POST to create a resource. For example, I might have the URI at http:/service.com/api/users and want to be able to GET with AutoQuery or POST to create a new user.
[Authenticate]
public class UsersService : Service
{
public IAutoQuery AutoQuery { get; set; }
public object Get(Users request)
{
var q = AutoQuery.CreateQuery(request, Request.GetRequestParams());
// Here I add some other conditions to the query object to filter results based on the user's role, etc.
return AutoQuery.Execute(request, q);
}
[RequiredRole("Admin")]
public object Post(CreateUser request)
{
var user = request.ConvertTo<User>();
Db.Save(user);
return user;
}
}
When you call a Service you're just calling a method directly on the target instance. What does the method returns? It's unlikely it's AutoQuery<Foo> since it's not a valid type in ServiceStack:
var foos = (AutoQuery<Foo>)fooSvc.Get(new Foos());
If you're getting a FormatException check the QueryString param that's causing it. Something in the Request is incompatible with the AutoQuery field.
Did you create the method yourself because AutoQuery generates services using the Any method so that it can be called by any verb. If it's a Custom Service change it to Any.
Otherwise you can Execute the method with the current HTTP Request with:
var foos = (QueryResponse<Foo>)HostContext.ServiceController
.Execute(new Foos(), HttpContext.ToRequest());
You can also override what Verb that gets executed with:
var req = HttpContext.ToRequest();
req.Items[Keywords.InvokeVerb] = "GET";
var foos = (QueryResponse<Foo>)HostContext.ServiceController
.Execute(new Foos(), req);
Custom AutoQuery Implementation
If you have conflicted fields trying to forward the request I would just change them to use fields that don't conflict with AutoQuery request. But if you still want to continue using the conflicted fields you can create a
Custom AutoQuery Implementation that removes it from the AutoQuery services instead, e.g:
public class UsersService : Service
{
public IAutoQuery AutoQuery { get; set; }
public object Any(Users request)
{
Dictionary<string, string> args = Request.GetRequestParams();
args.Remove("OrganizationId");// E.g remove conflicted fields
var q = AutoQuery.CreateQuery(request, args);
return AutoQuery.Execute(request, q);
}
}

Web API Action Filter to handle empty sets & 404's

I have a controller method which returns a list of resources like this:
[HttpGet, Route("events/{id:int}/rosters")]
public RosterExternalList List(int id, int page = 1, int pageSize = 50) {
return Repository.GetRosters(id).ToExternal(page, pageSize);
}
And the repository method is:
public IQueryable<EventRoster> GetRosters(int id) {
return EventDBContext.EventRosters.Where(x => x.eventID == id).OrderBy(x => x.EventRosterID).AsQueryable();
}
This is the same pattern for multiple list methods. The problem is that when no items are retrieved from the db, the api sends a 200 with an empty response body even if the id passed in is invalid.
What I want is if id = invalid, send appropriate response (404?). Otherwise, if the id is valid, and there are no records, send the 200 with empty body.
My question is - is this the right way to handle this? Can this be done via an Action Filter so it will be implemented across all the methods like this? How?
It is possible with an action filter like this:
public class EventsFilter : ActionFilterAttribute
{
public EventDBContext EventDBContext { get; set; }
public override void OnActionExecuting(HttpActionContext actionContext)
{
bool exists = false;
var routeData = actionContext.Request.GetRouteData();
object value;
if (routeData.Values.TryGetValue("id", out value))
{
int id;
if (int.TryParse(value, out id))
{
exists = EventDBContext.EventRosters.Where(x => x.eventID == id).Any();
}
}
if (exists == false)
{
var response = actionContext.Request.CreateErrorResponse(HttpStatusCode.NotFound, "Event not found");
throw new HttpResponseException(response);
}
}
}
You would also need to configure a dependency resolver to set EventDBContext property.
Is it the right way?
It depends on your solution design:
If your business layer is integrated and depends on the Web API (as it appears from your example), then yes it is the way to go.
If Web API (service layer) is not the only one who uses your business layer, then you would want to avoid duplication of event checking in other places and move it to a more generic class than Web API action filter and call this class from your business layer instead of Web API to make sure that regardless of the caller, you would always check if event exists. In that class you would throw something like BusinessLogicNotFoundException with information about event. And on the Web API, you would need to create an ExceptionFilterAttribute that handles BusinessLogicNotFoundException and creates an appropriate 404 response.

Asana API C# Project Design

I am designing an API wrapper in C# for Asana, a project management solution. During the design process, I ran into a few roadblocks. I am wondering what a good way to design the API wrapper would be.
The Asana API I am integrating with works with REST. The requests return JSON.
There will be 6 data classes (User, Task, Project, etc), each containing a bunch of strings to hold the data returned from the REST requests. My first idea with these classes is to give them each factory Parse() constructors so I can easily pass in json and get a data object in return. I realize I can't extract the static factory methods into an interface.
I will have a REST request class that will manage sending and receiving data from the REST server. It will always return a JSON string.
Finally, I would like a AsanaAPI class that will contain methods to wrap those exposed on the REST server (i.e GetUser, GetAllUsers, GetTask). Every method either returns a specific data class or an array of data classes. Here are the two cases:
public User GetSingleUser(string userID = "me")
{
if(userID == "") throw new ArgumentException("UserID cannot be blank");
string url = string.Format("{0}/{1}{2}", userUrl, userID, "?opt_fields=id,name,email,workspaces,workspaces.id,workspaces.name");
JSONNode root = JSON.Parse(GetResponse(url))["data"];
return User.Parse(root);
}
public List<User> GetAllUsers()
{
List<User> users = new List<User>();
string url = string.Format("{0}{1}", userUrl, "?opt_fields=id,name,email,workspaces,workspaces.id,workspaces.name");
JSONArray root = JSON.Parse(GetResponse(url))["data"].AsArray;
foreach(JSONNode userRoot in root)
{
users.Add(User.Parse(userRoot));
}
return users;
}
Each method will have that same format, but the User type will be replaced with Project, Task, etc. I want to extract the logic in these two methods because there will be many more methods with almost the exact same format.
In summary, the roadblocks I ran into were the fact that
I can't extract the factory constructor method from the data class.
I can't extract the parsing logic from the request methods
Is there something I can do with generics or is there just a better way of designing this project?
So I created a Parsable interface containing only a Parse method. Each data type implements Parsable. I was able to extract the parsing logic using generic types. It isn't the prettiest solution, but it does work.
public User GetSingleUser(string userID = "me")
{
if(userID == "") throw new ArgumentException("UserID cannot be blank");
string url = "{baseUrl}/users/{userID}?{opt_fields}".FormatWith(
new { baseUrl = BASE_URL, userID = userID, opt_fields = "opt_fields=id,name,email,workspaces,workspaces.id,workspaces.name" });
return (User)ParseJson<User>(AsanaRequest.GetResponse(url));
}
public User[] GetAllUsers()
{
string url = "{baseUrl}/users?{opt_fields}".FormatWith(
new { baseUrl = BASE_URL, opt_fields = "opt_fields=id,name,email,workspaces,workspaces.id,workspaces.name" });
return (User[])ParseJsonArray<User>(AsanaRequest.GetResponse(url));
}
public T ParseJson<T>(string json) where T : Parsable, new()
{
JSONNode root = JSON.Parse(json)["data"];
T ret = new T();
ret.Parse(root);
return ret;
}
public T[] ParseJsonArray<T>(string json) where T : Parsable, new()
{
JSONArray root = JSON.Parse(json)["data"].AsArray;
T[] nodes = new T[root.Count];
for(int i = 0; i < root.Count; i++)
{
T newParsable = new T();
newParsable.Parse(root[i]);
nodes[i] = newParsable;
}
return nodes;
}

Categories