how to retrieve class over web service? - c#

I have a "User" class in both my Web App and my Webservice.
Everything works and I get data back but when I assign the User class in my Web App that of the Web Service then it cannot convert between the two
How I call it in my web app
User user = new User();
user = userService.Login(UserName.Text, Password.Text); // Here it says it can't convert
how I send it from my web service
[WebMethod]
public User Login (string userName, string Password)
{
UserData usrData = new UserData();
return usrData.UserExists(userName, Password);
}
And what my class looks like in both the Web App and the Web Service
[Serializable]
public class User
{
/// <summary>
/// User Id
/// </summary>
public int ID { get; set; }
/// <summary>
/// User Name
/// </summary>
public string Name { get; set; }
/// <summary>
/// User Surname
/// </summary>
public string Surname { get; set; }
/// <summary>
/// User Email
/// </summary>
public string Email { get; set; }
/// <summary>
/// User Password
/// </summary>
public string Password { get; set; }
}

So you're trying to call it from C# code? Simply do this...
User user = userService.Login(UserName.Text, Password.Text);
If you're calling it from the server side, you can remove the [WebMethod] attribute. You don't even need a Web Service for this, assuming userService is of type UserService, just place the UserService.cs class inside your App_Code folder. Or better yet, place UserService class in a class library, and add a reference in your web application to that library.

Related

Add Swagger documentation for DTO properties

I'm using methods in my WebAPI that take in parameters.
I can provide descriptions in my Swagger UI for the basic parameters (id and terminology) using the following 'param' XML Comments:
/// <param name="terminology">terminology comment goes here</param>
/// <param name="id">id comment goes here</param>
/// <param name="filterDto"></param>
/// <returns>The results</returns>
[HttpGet("{terminology}/product/{id}/details")]
[ProducesResponseType(typeof(IPacksResponse), StatusCodes.Status200OK)]
[ProducesResponseType(typeof(ErrorsDto), StatusCodes.Status500InternalServerError)]
public IActionResult GetProductPacks(
[FromRoute] string terminology,
[FromRoute] string id,
[FromQuery] GetPackByIdFilteredDto filterDto)
However, what I need help with is... The 3rd parameter being passed in (filterDto) is a class with several properties which also appear in the Swagger UI, but I don't know how to create descriptions for each of those inputs:
public class GetPackByIdFilteredDto
{
public string Dt { get; set; }
public string Dc { get; set; }
public string Ms { get; set; }

How to show responses example in swagger documenation

I developed asp.net web API and I used swagger to API documentation and consume purposes. I need to show swagger response model sample in swagger documentation as follows
This image I got from the internet
How can I add a response example as above image
My controller as follows
/// <param name="sDate">Start date</param>
/// <param name="eDate">End date</param>
/// <param name="lCode">Location code</param>
/// <param name="page">Page number</param>
/// <param name="pageSize">Page size</param>
[Route("lobbydetail")]
[SwaggerResponse(HttpStatusCode.OK, Type = typeof(ResultOutput<List<LDetailRecord>>))]
[SwaggerResponse(HttpStatusCode.BadRequest, Type = typeof(APIError))]
[SwaggerResponse(HttpStatusCode.InternalServerError, Type = typeof(APIError))]
public IHttpActionResult GetDetails(DateTime sDate, DateTime eDate, string lCode = null, int page = 1, int pageSize = 100)
{
try
{
if (sDate > eDate)
{
return Content(HttpStatusCode.BadRequest, new APIError("400", "Start date is greater than end date."));
}
var tID = Convert.ToInt32(jwtData.GetTokenClaim(TENANT_ID));
return Ok(dataView.GetDetailViewData(tID, sDate, eDate, lCode, page, pageSize));
}
catch (ArgumentException ae)
{
return Content(HttpStatusCode.BadRequest, new APIError("404", "Invalid location code"));
}
catch (Exception ex)
{
Logger.LogErrorEvent(ex);
return Content(HttpStatusCode.InternalServerError, new APIError("500", "Error occurred"));
}
}
My as follows LDetailRecord
public class LDetailRecord
{
public DateTime TDateTime { get; set; }
public dynamic Account { get; set; }
public string LCode { get; set; }
public string LName { get; set; }
public string ConfNumber { get; set; }
public decimal WTime { get; set; }
public decimal AssTime { get; set; }
public List<string> RequestedServices { get; set; }
public string PersonRequested { get; set; }
public string AssistedBy { get; set; }
public string CustomerType { get; set; }
public string CheckedInBy { get; set; }
public string Comments { get; set; }
public string PreferredLanguage { get; set; }
}
In my swagger shows as follows
I'm new to the web api and swagger, please help me, what I did wrong here
The answer by #Mikah-Barnett is not entirely correct when it comes to error responses.
Also, because you're returning a different type when there's an error, use the
[ProducesErrorResponseType(typeof(APIError))]
as well. That will let Swagger know you want a different model when there's a client error.
ProducesErrorResponseTypeAttribute(Type) - Is used for API documentation, but can only define a single error type for all errors which are specified with ProducesResponseTypeAttribute(Int32) attribute.
ProducesResponseTypeAttribute(Type, Int32) - Is used for API documentation when you want to have more detailed granularity over all the different types returned, depending on the response status code
As an example, below is what you could define per endpoint. Even better, common response type attributes can be specified at the controller level, meaning you don't need to duplicate for every endpoint.
[HttpPost]
[ProducesResponseType(typeof(ValidationProblemDetails), StatusCodes.Status400BadRequest)]
[ProducesResponseType(typeof(ProblemDetails), StatusCodes.Status500InternalServerError)]
[ProducesResponseType(typeof(NewOrderResponse), StatusCodes.Status201Created)]
public async Task<IActionResult> Post([FromBody, Required] NewOrderRequest orderRequest)
You need to explicitly state the return type in your methods. So, instead of
public IHttpActionResult GetDetails(...
use
public IHttpActionResult<LDetailRecord> GetDetails(...
That lets OpenAPI know exactly what you're planning to return and it will then show an example of the model in the UI.
Also, because you're returning a different type when there's an error, use the
[ProducesErrorResponseType(typeof(APIError))]
as well. That will let Swagger know you want a different model when there's a client error.
Here's a good article from MSFT documenting how this works, and below is a more complete example (from that article) showing all the pieces together.
/// <summary>
/// Creates a TodoItem.
/// </summary>
/// <remarks>
/// Sample request:
///
/// POST /Todo
/// {
/// "id": 1,
/// "name": "Item1",
/// "isComplete": true
/// }
///
/// </remarks>
/// <param name="item"></param>
/// <returns>A newly created TodoItem</returns>
/// <response code="201">Returns the newly created item</response>
/// <response code="400">If the item is null</response>
[HttpPost]
[ProducesResponseType(201)]
[ProducesResponseType(400)]
[ProducesErrorResponseType(typeof(APIError))]
public ActionResult<TodoItem> Create(TodoItem item)
{
_context.TodoItems.Add(item);
_context.SaveChanges();
return CreatedAtRoute("GetTodo", new { id = item.Id }, item);
}

Too much recursion when attempting to serialize object

I'm currently testing usage of YML in my application, to provide a more friendly means to editing and configuring e-mail downloads and imports for users.
For testing purposes, I wanted to see if my mocked-up YML is roughly equivalent to what the application would put out.
So, for testing I have this block of code, and it never fails to throw "Too much recursion".
// TEST SERIALIZE DATA
{
var _testDomain = default(Domain);
var _testSecureString = new SecureString();
"testP455w0rd".ToCharArray().ToList().ForEach(_testSecureString.AppendChar);
var _testConfig = new EmailBeamConfig() {
DefaultProtocol = ProtocolType.Imap,
Domains = new List<Domain> {
(_testDomain = new Domain {
Accounts = new List<EmailAccount> {
new EmailAccount {
Domain = _testDomain.DomainName,
InternalUid = "de.test_a_test-account",
Password = _testSecureString,
Username = "test-account#test.de"
},
new EmailAccount {
Domain = _testDomain.DomainName,
InternalUid = "de.test_a_test-account2",
Password = _testSecureString,
Username = "test-account2#test.de"
}
},
DomainName = "de.test",
EmailServer = "192.168.252.172",
UseEncryption = true
})
},
DownloadInterval = new TimeSpan(0, 10, 0),
DownloadRoot = mainSystemConfig.GetString("importRootDir")
};
var _testSerializer = new SerializerBuilder()
.WithNamingConvention(new UnderscoredNamingConvention())
.Build();
var _testSerializedData = _testSerializer.Serialize(_testConfig);
File.WriteAllText(cfgFile.FullName, _testSerializedData);
}
Here is the source to EmailBeamConfig:
internal class EmailBeamConfig {
private string _downloadRoot;
/// <summary>
/// Gets or sets the download root dir.
/// </summary>
public string DownloadRoot {
get => _downloadRoot;
set {
_downloadRoot = value;
DownloadRootObj = new DirectoryInfo(value);
}
}
/// <summary>
/// Gets an object representing the download root.
/// </summary>
public DirectoryInfo DownloadRootObj { get; private set; }
/// <summary>
/// Gets the interval for downloading e-mails.
/// </summary>
public TimeSpan DownloadInterval { get; set; }
/// <summary>
/// Gets or sets the default protocol to be used,
/// if a protocol is not defined for an individual user.
/// </summary>
public ProtocolType DefaultProtocol { get; set; }
public List<Domain> Domains { get; set; }
}
And EmailAccount:
/// <summary>
/// Defines an e-mail account for use in this plugin.
/// </summary>
internal struct EmailAccount {
private string _internalUid;
/// <summary>
/// Gets or sets the username for this e-mail account.
/// (Used for logging in, etc)
/// </summary>
internal string Username { get; set; }
/// <summary>
/// gets or sets the password for this account.
/// </summary>
internal SecureString Password { get; set; }
/// <summary>
/// Gets or sets the internal user identifier for this user account.
/// </summary>
internal string InternalUid {
get => _internalUid;
set {
if (value == "/" || value.Length < 10) {
_internalUid = $"{ Domain }_a_{ Username?.Split('#')[0] }";
} else _internalUid = value;
}
}
/// <summary>
/// Gets or sets the domain this e-mail account is associated with.
/// </summary>
internal string Domain { get; set; }
}
I'm completely oblivious to any source of infinite recursion at this point.
EDIT: Here is the Domain struct declaration:
internal struct Domain {
/// <summary>
/// Gets or sets the name of this domain.
/// </summary>
internal string DomainName { get; set; }
/// <summary>
/// Gets or sets the URL/IP address of the e-mail server.
/// </summary>
internal string EmailServer { get; set; }
/// <summary>
/// Gets or sets a value determining whether to attempt to use an encrypted connection to
/// the e-mail server.
/// </summary>
internal bool UseEncryption { get; set; }
/// <summary>
/// Gets the list of accounts associated with this domain
/// </summary>
internal List<EmailAccount> Accounts { get; set; }
}

Generic types, Interfaces

I would like to create a generic notification engine. The idea is to have a single core engine to process any type of notification. This engine will process notification and handle all logging, error handling etc..
I created 3 simple interfaces:
public interface INotificationInput
{
/// <summary>
/// Friendly Name for logging/tracing usage
/// </summary>
string FriendlyName { get; set; }
string NotificationCode{ get; set; }
Double Version { get; set; }
}
public interface INotificationOutput
{
/// <summary>
/// Friendly Name for logging/tracing usage
/// </summary>
string FriendlyName { get; }
}
public interface INotificationProvider<out Toutput, Tinput> where Toutput : INotificationOutput where Tinput : INotificationInput
{
/// <summary>
/// Friendly Name for logging/tracing usage
/// </summary>
string FriendlyName { get; set; }
/// <summary>
/// Generates and returns an INotificationOutput from data
/// </summary>
/// <param name="data"></param>
/// <returns></returns>
Toutput GenerateNotificationOutput(Tinput data);
}
So the INotificationProvider will chunk the INotificationInput to create a INotificationOutput.
That could be information to send a email, a sms, you name it, the engine will call the methods and do the magic of scheduling, logging, handling errors and so on..
I implemented the interface like this:
/// <summary>
/// INotificationInput represented by a dummy object
/// </summary>
public class DummyNotificationInput : INotificationInput
{
public string FriendlyName { get; set; }
public string NotificationCode { get; set; }
public double Version { get; set; }
}
public class DummyNotificationOutput : INotificationOutput
{
public string FriendlyName { get; private set; }
}
public class DummyProvider : INotificationProvider<DummyNotificationOutput, DummyNotificationInput>
{
public string FriendlyName { get; set; }
public DummyNotificationOutput GenerateNotificationOutput(DummyNotificationInput data)
{
throw new NotImplementedException();
}
}
Now I would like my engine to have a list of provider:
var providersList = new List<INotificationProvider<INotificationOutput, INotificationInput>>();
The problem is that I cannot to the following:
providersList.Add(new DummyProvider<DummyNotificationOutput, DummyNotificationInput>());
There must be a solution. Am I using the wrong approach?
The second generic type argument to INotificationProvider isn't covariant (at a conceptual level), but you're trying to use it as if it were. It is actually contravariant.
In your list of INotificationProvider objects you've defined the input notification as an INotificationInput. This means objects added to this list need to be able to accept any type of INotificationInput as input to their GenerateNotificationOutput function. You're trying to add an object that only knows how to handle DummyNotificationInput objects. It would fail if it were passed some other type of input.
Either your provider needs to accept INotificationInput objects, if you want to be able to add it to that list, or the list needs to define all of the objects as accepting DummyNotificationInput.
As Servy has already answered, you can't really do this due to what you providersList is expecting
With this in mind, it may actually be simpler to just make INotificationProvider non-generic:
public interface INotificationProvider
{
/// <summary>
/// Friendly Name for logging/tracing usage
/// </summary>
string FriendlyName { get; set; }
/// <summary>
/// Generates and returns an INotificationOutput from data
/// </summary>
/// <param name="data"></param>
/// <returns></returns>
INotificationOutput GenerateNotificationOutput(INotificationInput data);
}
Then the DummyProvider becomes:
public class DummyProvider : INotificationProvider
{
public string FriendlyName { get; set; }
public INotificationOutput GenerateNotificationOutput(INotificationInput data)
{
throw new NotImplementedException();
}
}
Now, probably not what you had in mind - you are expecting to pass DummyNotificationInput instances to DummyProvider
You could just type check in your Provider code
public class DummyProvider : INotificationProvider
{
public string FriendlyName { get; set; }
public INotificationOutput GenerateNotificationOutput(INotificationInput data)
{
if (!(data is DummyNotificationInput)) throw new ArgumentException("Invalid type specified", "data");
return something...;
}
}
Obviously, you lose design time checking - but if you really need to put them in a covariant list you can't provide an implementor that has a derived generic type argument

Can i JSON serialize a object which contains a list?

I've run into a issue when using JsonConvert.SerializeObject(addToBasketView) as it seems not to like tha list that i pass into it - until i passed in the list it was working fine - are you not able to pass a list?
Here is my code:
controller:
//We need a list of Automation scripts - this was in Apollo but now we need to pass it in teh message
var automationList = AutomationHelper(retailerId, productId);
var addToBasketView = new AddToBasketView
{
Url = retailerProduct.DeepLink,
Password = (password),
Username = (username),
RetailerProductJson = new RetailerProductJson(retailerProduct),
WidgetImpressionId = id,
Quantity = qty,
MessageId = messageId,
AutomationList = automationList
};
// now turn it into a string
var json = JsonConvert.SerializeObject(addToBasketView);
Addtobasketview.cs:
using System;
using System.Collections.Generic;
namespace WidgetData
{
/// <summary>
/// This is the view that we will give to the AddToBasketForm
/// </summary>
[Serializable]
public class AddToBasketView
{
/// <summary>
/// The Username for the retailer
/// </summary>
public String Username { get; set; }
/// <summary>
/// The password for the retailer
/// </summary>
public String Password { get; set; }
/// <summary>
/// The URl of the thing they want added to the site
/// </summary>
public String Url { get; set; }
/// <summary>
/// The retailer product selected - from this I can get the retailer and the product.
/// </summary>
public RetailerProductJson RetailerProductJson { get; set; }
/// <summary>
/// The widget impression id so that we can attach to the addTobaskets table for recording purposes
/// </summary>
public int WidgetImpressionId { get; set; }
/// <summary>
/// set the quantity
/// </summary>
public int Quantity { get; set; }
/// <summary>
/// The MessageId this is so we can identify it when waiting for a response
/// </summary>
public String MessageId { get; set; }
/// <summary>
/// Automation script list
/// </summary>
public List<AutomationStepScript> AutomationList { get; set; }
}
}
Edit:
AutomationStepScript.cs
using System;
using System.ComponentModel.DataAnnotations;
using System.Web.Mvc;
namespace WidgetData
{
/// <summary>
/// Extends the class created by Linq To SQL, so that we can control in our models section the validity of the data
/// </summary>
[MetadataType(typeof(AuotmationStepScriptValidation))]
public partial class AutomationStepScript
{
/// <summary>
/// Override the to string so that we can use in the delete method to capture details of this record before it is deleted
/// </summary>
/// <returns></returns>
public override string ToString()
{
return string.Format("AutomationStepScript Id = {0}, AutomationStepId = {1}, Sort Order = {2}, Description = {3}, Script = {4}, Evaluate = {5}", Id,AutomationStepId,SortOrder,Description,Script,Evaluate);
}
}
/// <summary>
/// Class to validate my AuotmationStepScript
/// </summary>
public class AuotmationStepScriptValidation
{
/// <summary>
/// AutomationId
/// </summary>
[Required(ErrorMessage = "AutomationStepId is a required field")]
[Range(1, int.MaxValue, ErrorMessage = "AutomationStepId must be valid")]
public int AutomationStepId { get; set; }
/// <summary>
/// Display SortOrder
/// </summary>
[Range(0, int.MaxValue, ErrorMessage = "SortOrder must be valid")]
public int SortOrder { get; set; }
/// <summary>
/// Description
/// </summary>
[StringLength(256, ErrorMessage = "Description maximum length is 256", MinimumLength = 1)]
public String Description { get; set; }
/// <summary>
/// Script
/// </summary>
[AllowHtml]
[StringLength(8000, ErrorMessage = "Script maximum length is 8000")]
public String Script { get; set; }
/// <summary>
/// Evaluate
/// </summary>
public int Evaluate { get; set; }
}
}
AutomationHelper (called in teh controller to get the list:
/// <summary>
/// The helper function to fetch the data so that we can get the scripts for each step for this automation (which we will find from the localParams)
/// </summary>
/// <param name="retailerId"> </param>
/// <param name="productId"> </param>
/// <returns>WE return a List of AutomationStepScripts, this object will be selected further with Lambda expression"></returns>
public List<AutomationStepScript> AutomationHelper(int retailerId, int productId)
{
List<AutomationStepScript> automationStepScripts = null;
var automationStepScriptRepository = new AutomationStepScriptRepository();
var productRepository = new ProductRepository();
var retailerCategoryRepository = new RetailerCategoryRepository();
var product = productRepository.GetProduct(productId);
var categoryId = product.CategoryId;
var retailerCategory = retailerCategoryRepository.GetRetailerCategoryByRetailerCategory(retailerId, categoryId);
// DO we have a retailer category?
if (retailerCategory != null)
{
// Yes, without a valid retailer category we cannot possibly find the automation
// Now here we have the RetailerCategory. The next question is Does this RetailerCategory HAVE an automation set up for it
if (retailerCategory.AutomationId != null)
{
// Yes, we have an automation. So lets get all of the Scripts for all of the steps for this automation
// Get All scripts for all steps for this automation.
automationStepScripts = automationStepScriptRepository.GetAllAutomationStepScriptsForAutomation((int)retailerCategory.AutomationId).ToList();
}
}
return automationStepScripts;
}
I am serializinf it as it bacomes teh body of a message which i am using in SQS which i then use later.
Please let me know if you need any more information.
Thanks.

Categories