I have a method called in get that receive in input (from the query string) some parameters that represent an object:
public override async Task<IActionResult> Index([FromQuery]MyFilter filter)
{
...
}
public class MyFilter : BaseFilter
{
public List<string> Rules{ get; set; } = new List<string>();
public string SearchString { get; set; }
}
If I call my method with this query string the property Rules is empty:
?filter.SearchString=&filter.Rules[]=foo&filter.Rules[]=bar
but if I call it with this query string, the property Rules is filled correctly:
?filter.SearchString=&filter.Rules[0]=foo&filter.Rules[1]=bar
So the field "Role"s is a html select so when user fill this field I do not know indexes.
Does someone have the same problem (and a solution for it)?
I think its better to use:
?SearchString=&Rules[]=foo&Rules[]=bar
Related
I am trying to create an action that would look like controller/action?param1=val¶m2=val with the HttpGet annotation.
What I have is:
[HttpGet]
public IActionResult Index(SomeClass obj)
{
// do stuff
return View(something);
}
I can access the action via controller/Index?obj.param1=val&obj.param2=val, but is there a way to avoid obj.param1 and obj.param2 in the query string and have something like controller/Index?page=val&amount=val.
Putting those parameters in the annotation like this didn't work: [HttpGet("/page={obj.subobject.param1}&amount={obj.subobject.param2}")]
Assuming the default model binding setup, you can just pass the parameter names directly and ASP.NET Core will automatically put the values into the SomeClass object:
public IActionResult Test(SomeClass obj)
{
return Json(obj);
}
public class SomeClass
{
public string Foo { get; set; }
public string Bar { get; set; }
}
When opening the URL /Home/Test?foo=baz&bar=qux you will now see that the object is properly filled with the Foo and Bar properties.
I would like to enhance final result that ModelBinder returns.
For example:
public class MyModel
{
public int Order {get;set;}
[MyUpperCaseAttribute]
public string Title {get;set;}
}
In API method I expect that all string properties in MyModel which has MyUpperCaseAttribute is in upper case.
For example:
[HttpPost("AddRecord")]
public async Task<ActionResult<int>> AddRecord(MyModel model)
{
model.Title should be upper case, even if send from client in lower case.
}
My idea was to override default ModelBinder and enumerate through all properties and check if property is string and has MyUpperCaseAttribute and correct property value to upper case. I check documentation, but doesn't examples doesn't fill right, since they completely redesign what is returned. I would like to just modify result properties.
What would be the best option to achieve desired behaviour?
Important: (edited):
It would be nice if directive attributes could be stacked:
public class MyModel
{
public int Order {get;set;}
[MyUpperCaseAttribute]
[RemoveSpacesAttribute]
public string Title {get;set;}
}
Edited:
It looks similar to this, but if not other, this is ASP.NET Core, and on link is just ASP.NET. Method, properties, interfaces... are not the same.
I should say, that it would be nice if JSON case rule would work:
public class MyModel
{
public int Order {get;set;}
public string Title {get;set;}
}
should work if {order: 1, title: "test"} (notice lowercase) is send from JavaScript.
This might not be the 'best' option, but I would just use .ToUpper() extension method instead of a custom attribute filter.
public class MyModel
{
private string _title;
public int Order {get;set;}
public string Title { get => _title.ToUpper(); set => _title = value.ToUpper(); }
}
There's a big red herring here, and that's the fact that it appears that this is the sort of thing that could and should be accomplished via model binding. Unfortunately, that's not the case in ASP.Net Core Web API: because the incoming data is JSON, it is in fact handled by input formatters, not model binders. Therefore, in order to achieve the desired effect, you need to create your own custom input formatter that replaces the standard JsonInputFormatter.
First the attribute:
[AttributeUsage(AttributeTargets.Property)]
public class ToUppercaseAttribute : Attribute
{
}
Then we decorate our model class with it:
public class MyModel
{
public int Order { get; set; }
[ToUppercase]
public string Title { get; set; }
}
Now create our custom input formatter that checks for that attribute and transforms the output if necessary. In this case, it simply wraps and delegates to JsonInputFormatter to do the heavy lifting as normal, then modifies the result if it finds our ToUppercaseAttribute attribute on any string property:
public class ToUppercaseJsonInputFormatter : TextInputFormatter
{
private readonly JsonInputFormatter _jsonInputFormatter;
public ToUppercaseJsonInputFormatter(JsonInputFormatter jsonInputFormatter)
{
_jsonInputFormatter = jsonInputFormatter;
foreach (var supportedEncoding in _jsonInputFormatter.SupportedEncodings)
SupportedEncodings.Add(supportedEncoding);
foreach (var supportedMediaType in _jsonInputFormatter.SupportedMediaTypes)
SupportedMediaTypes.Add(supportedMediaType);
}
public override Task<InputFormatterResult> ReadRequestBodyAsync(InputFormatterContext context, Encoding encoding)
{
var result = _jsonInputFormatter.ReadRequestBodyAsync(context, encoding);
foreach (var property in context.ModelType.GetProperties().Where(p => p.PropertyType.IsAssignableFrom(typeof(string))
&& p.CustomAttributes.Any(a => a.AttributeType.IsAssignableFrom(typeof(ToUppercaseAttribute)))))
{
var value = (string)property.GetValue(result.Result.Model);
property.SetValue(result.Result.Model, value.ToUpper());
}
return result;
}
}
Next we create an extension method that makes it simple to substitute the default JsonInputFormatter with our custom formatter:
public static class MvcOptionsExtensions
{
public static void UseToUppercaseJsonInputFormatter(this MvcOptions opts)
{
if (opts.InputFormatters.FirstOrDefault(f => f is JsonInputFormatter && !(f is JsonPatchInputFormatter)) is JsonInputFormatter jsonInputFormatter)
{
var jsonInputFormatterIndex = opts.InputFormatters.IndexOf(jsonInputFormatter);
opts.InputFormatters[jsonInputFormatterIndex] = new ToUppercaseJsonInputFormatter(jsonInputFormatter);
}
}
}
And finally, call that method to effect the replacement in Startup.cs:
public class Startup
{
public void ConfigureServices(IServiceCollection services)
{
services
.AddMvc(options => options.UseToUppercaseJsonInputFormatter());
}
}
Et voilĂ !
You can do this thing inside your MyUpperCaseAttribute as follows:
public class MyUpperCaseAttribute : ValidationAttribute
{
protected override ValidationResult IsValid(object value, ValidationContext validationContext)
{
if(value != null)
{
validationContext.ObjectType
.GetProperty(validationContext.MemberName)
.SetValue(validationContext.ObjectInstance, value.ToString().ToUpper(), null);
}
return null;
}
}
Property value will be converted to UpperCase during Model Binding. I have checked it in my side and it works perfectly.
I am very new to C# and ServiceStack and I am working on a small project that consists on calling a third party API and loading the data I get back from the API into a relational database via ServiceStack's ORMLite.
The idea is to have each endpoint of the API have a reusable model that determines how it should be received in the API's response, and how it should be inserted into the database.
So I have something like the following:
[Route("/api/{ApiEndpoint}", "POST")]
public class ApiRequest : IReturn<ApiResponse>
{
public Int32 OrderId { get; set; }
public DateTime PurchaseDate { get; set; }
public String ApiEndpoint { get; set; }
}
public class ApiResponse
{
public Endpoint1[] Data { get; set; }
public String ErrorCode { get; set; }
public Int32 ErrorNumber { get; set; }
public String ErrorDesc { get; set; }
}
public class Endpoint1
{
[AutoIncrement]
public Int32 Id { get; set; }
[CustomField("DATETIME2(7)")]
public String PurchaseDate { get; set; }
[CustomField("NVARCHAR(50)")]
public String Customer { get; set; }
[CustomField("NVARCHAR(20)")]
public String PhoneNumber { get; set; }
public Int32 Amount { get; set; }
}
My first class represents the API's request with its route, the second class represents the API's response. The API's response is the same for all endpoints, but the only thing that varies is the structure of the Data field that comes back from that endpoint. I've defined the structure of one of my endpoints in my Endpoint1 class, and I am using it in my API's response class. As you can see, I am also defining a few attributes on my Endpoint1 class to help the ORM make better decisions later when inserting the data.
Ok, so the issue is that I have about 15 endpoints and I don't want to create 15 ApiResponse classes when I know the only thing that changes is that first Data field in the class.
So I made something like this:
public class DataModels
{
public Type getModel(String endpoint)
{
Dictionary<String, Type> models = new Dictionary<String, Type>();
models.Add("Endpoint1", typeof(Endpoint1));
// models.Add("Endpoint2", typeof(Endpoint2));
// models.Add("Endpoint3", typeof(Endpoint3));
// and so forth...
return models[endpoint];
}
}
I would like for getModel() to be called when the request is made so that I can pass in the ApiEndpoint field in the ApiRequest class and store the type that I want my Data field to have so that I can dynamically change it in my ApiResponse class.
In addition, there is the ORM part where I iterate over every endpoint and create a different table using the model/type of each endpoint. Something like this:
endpoints.ForEach(
(endpoint) =>
{
db.CreateTableIfNotExists<Endpoint1>();
// inserting data, doing other work etc
}
);
But again, I'd like to be able to call getModel() in here and with that define the model of the specific endpoint I am iterating on.
I've attempted calling getModel() on both places but I always get errors back like cannot use variable as a typeand others... so I am definitely doing something wrong.
Feel free to suggest a different approach to getModel(). This is just what I came up with but I might be ignoring a much simpler approach.
When I DID understand you correctly, you have different API-Calls which all return the same object. The only difference is, that the field "Data" can have different types.
Then you can simply change the type of data to object:
public object Data { get; set; }
And later simply cast this to the required object:
var data1=(Endpoint1[]) response.Data;
You're going to have a very tough time trying to dynamically create .NET types dynamically which requires advanced usage of Reflection.Emit. It's self-defeating trying to dynamically create Request DTOs with ServiceStack since the client and metadata services needs the concrete Types to be able to call the Service with a Typed API.
I can't really follow your example but my initial approach would be whether you can use a single Service (i.e. instead of trying to dynamically create multiple of them). Likewise with OrmLite if the Schema of the POCOs is the same, it sounds like you would be able to flatten your DataModel and use a single database table.
AutoQuery is an example of a feature which dynamically creates Service Implementations from just a concrete Request DTO, which is effectively the minimum Type you need.
So whilst it's highly recommended to have explict DTOs for each Service you can use inheritance to reuse the common properties, e.g:
[Route("/api/{ApiEndpoint}/1", "POST")]
public ApiRequest1 : ApiRequestBase<Endpoint1> {}
[Route("/api/{ApiEndpoint}/2", "POST")]
public ApiRequest2 : ApiRequestBase<Endpoint1> {}
public abstract class ApiRequestBase<T> : IReturn<ApiResponse<T>>
{
public int OrderId { get; set; }
public DateTime PurchaseDate { get; set; }
public string ApiEndpoint { get; set; }
}
And your Services can return the same generic Response DTO:
public class ApiResponse<T>
{
public T[] Data { get; set; }
public String ErrorCode { get; set; }
public Int32 ErrorNumber { get; set; }
public String ErrorDesc { get; set; }
}
I can't really understand the purpose of what you're trying to do so the API design is going to need modifications to suit your use-case.
You're going to have similar issues with OrmLite which is a Typed code-first POCO ORM where you're going to run into friction trying to use dynamic types which don't exist at Runtime where you'll likely have an easier time executing Dynamic SQL since it's far easier to generate a string than a .NET Type.
With that said GenericTableExpressions.cs shows an example of changing the Table Name that OrmLite saves a POCO to at runtime:
const string tableName = "Entity1";
using (var db = OpenDbConnection())
{
db.DropAndCreateTable<GenericEntity>(tableName);
db.Insert(tableName, new GenericEntity { Id = 1, ColumnA = "A" });
var rows = db.Select(tableName, db.From<GenericEntity>()
.Where(x => x.ColumnA == "A"));
Assert.That(rows.Count, Is.EqualTo(1));
db.Update(tableName, new GenericEntity { ColumnA = "B" },
where: q => q.ColumnA == "A");
rows = db.Select(tableName, db.From<GenericEntity>()
.Where(x => x.ColumnA == "B"));
Assert.That(rows.Count, Is.EqualTo(1));
}
Which uses these extension methods:
public static class GenericTableExtensions
{
static object ExecWithAlias<T>(string table, Func<object> fn)
{
var modelDef = typeof(T).GetModelMetadata();
lock (modelDef)
{
var hold = modelDef.Alias;
try
{
modelDef.Alias = table;
return fn();
}
finally
{
modelDef.Alias = hold;
}
}
}
public static void DropAndCreateTable<T>(this IDbConnection db, string table)
{
ExecWithAlias<T>(table, () => {
db.DropAndCreateTable<T>();
return null;
});
}
public static long Insert<T>(this IDbConnection db, string table, T obj, bool selectIdentity = false)
{
return (long)ExecWithAlias<T>(table, () => db.Insert(obj, selectIdentity));
}
public static List<T> Select<T>(this IDbConnection db, string table, SqlExpression<T> expression)
{
return (List<T>)ExecWithAlias<T>(table, () => db.Select(expression));
}
public static int Update<T>(this IDbConnection db, string table, T item, Expression<Func<T, bool>> where)
{
return (int)ExecWithAlias<T>(table, () => db.Update(item, where));
}
}
But it's not an approach I'd take personally, if I absolutely needed (and I'm struggling to think of a valid use-case outside of table-based Multitenancy or sharding) to save the same schema in multiple tables I'd just be using inheritance again, e.g:
public class Table1 : TableBase {}
public class Table2 : TableBase {}
public class Table3 : TableBase {}
I have a need to pass into a controller a viewmodel as per below.
[HttpPost]
public JsonResult GetSearchResultsJson(SearchCriteria searchCriteria)
{
}
SearchCriteria is defined as:
public class SearchCriteria
{
public SearchData searchData { get; set; }
public SearchMode searchMode { get; set; }
}
Where SearchMode is:
public class Searchmode
{
public int? mode { get; set; }
public int? pageNumber { get; set; }
public int? pageSize { get; set; }
}
And SearchData has 61 properties that define what items are to search for.
public class SearchData
{
public string name {get;set;}
....
public int age {get;set;}
}
I populate an object using jQuery and pass that to the controller. .Net converts this object into an object of type SearchCriteria. All is working, but when the PagedListPager control is rendered, how do i emulate the jQuery used to create the object?
At the moment I have the following code:
#Html.PagedListPager(Model.DocumentsPaged, pageNumber => Url.Action("GetSearchResultsJson", XXXXXXXXXXXXXXXXXXXXXXX),pLRO)
And do not know what to put in the bit marked as XXXXXXXXXXXXXXXXXXXXXXX.
Within jQuery, I can modify the pageNumber property of the SerachMode object and this does provide me with the correct page, but it is precisely this property that I need to update within the Html.PagedListPager helper.
As described in example here you can pass the page.
I really suggest you to clone the example code and play a bit with it.
Probably you have to add your search parameters as well, in case you lose them server-side.
Your method GetSearchResultsJson(SearchCriteria searchCriteria) is marked [HttpPost], so you can leave the URL parameters blank and just use Url.Action("GetSearchResultsJson"). The search parameters for a POST go in the body of the request instead of the URL.
Because the Html.PagedListPager method expects a 'page' parameter (assuming you are using the method from the PagedList.Mvc NuGet package), you may want to write your search function like this:
GetSearchResultsJson(int page, int SearchCriteria searchCriteria)
I have a business layer that has some IEnumerable methods. I used those methods in my controller and get result after make methods result to .ToList().
Now there is a new requirement. I have to filter action result data per user access. When users call an action method, only one business layer method called, but I need different result per user. For example: user A can load all data but user B only access 10 rows data according by his permission.
Code:
BL:
public IEnumerable<Card> GetCardList(int Id)
{
return unitOfWork.GetCardList(Id);
}
Controller:
//...
CardBl.GetCardList(id).ToList();
//...
I tried to filter data in a CustomActionFilter class by overriding OnActionExecuting or OnResultExecuting methods, but I'm confused!
Actually I want to make this kind of filtering automate by attributes instead of adding where clause for every method.
I would appreciate if anyone suggest a solution.
Update:
My Model:
public class CardModels
{
public int Id { get; set; }
public string CardName { get; set; }
public string CardDescription { get; set; }
public int CityId { get; set; }
}
CityId is the property that I have to filter data based on.