I have an OData query as below.
http://localhost:65202/api/odata/StaffBookings?$select=ID&$filter=Staff/ID eq 1&$expand=Staff($select=ID),Event($select=ID,EventDate;$expand=Client($select=ID,Company))
How can I call it using Refit?
Thanks
Regards
You can use the following OdataParameters class to set the properties. Then add OdataParameters as a parameter in your function signature.
[Get("/v1/odata/{resource}")]
Task<HttpResponseMessage> GetAdHocDataAsync(
[Header("Authorization")] string bearerAuthorization,
string resource,
OdataParameters odataParams
);
Here's the OdataParameters class you can modify to for your needs
public class OdataParameters
{
private readonly bool _count;
public OdataParameters(bool count = false, int? top = null, int? skip = null, string filter = null,
string select = null, string orderBy = null)
{
_count = count;
Top = top;
Skip = skip;
Filter = filter;
Select = select;
OrderBy = orderBy;
}
[AliasAs("$count")] public string Count => _count ? "true" : null;
[AliasAs("$top")] public int? Top { get; }
[AliasAs("$skip")] public int? Skip { get; }
[AliasAs("$filter")] public string Filter { get; }
[AliasAs("$select")] public string Select { get; }
[AliasAs("$orderBy")] public string OrderBy { get; }
}
Related
I have a PostgreSQL query which is beyond the capacity of a LINQ statement as far as I know or at least I haven't been able to figure it out, so I decided I would use raw SQL. Following are the relevant code snippets.
Controller:
public async Task<IActionResult> Index(string sortOrder, string currentFilter, string searchString, int? page)
{
ViewBag.CurrentSort = sortOrder;
ViewBag.ConOrgSortParm = String.IsNullOrEmpty(sortOrder) ? "Client_desc" : "Client";
ViewBag.AssignedSortParm = String.IsNullOrEmpty(sortOrder) ? "Assigned_desc" : "Assigned";
ViewBag.ExpiresSortParm = String.IsNullOrEmpty(sortOrder) ? "Expires_desc" : "Expires";
ViewBag.LastActiveSortParm = String.IsNullOrEmpty(sortOrder) ? "LastActive_desc" : "LastActive";
ViewBag.IDSortParm = String.IsNullOrEmpty(sortOrder) ? "Id_desc" : "";
if (searchString != null)
{
page = 1;
}
else
{
searchString = currentFilter;
}
ViewBag.CurrentFilter = searchString;
var results = _context.NodeIndexViewModel.ToList();
if (!String.IsNullOrEmpty(searchString))
{
results = results.Where(s => s.ContractingOrg.Contains(searchString)
|| s.NodeID.ToString().StartsWith(searchString));
}
int pageSize = 10;
int pageNumber = (page ?? 1);
return View(await results.ToPagedListAsync(pageNumber, pageSize));
}
NodeIndexViewModel:
using NpgsqlTypes;
using System.ComponentModel.DataAnnotations;
using static UnidAdmin.Models.Bundle;
namespace UnidAdmin.Models
{
public class NodeIndexViewModel
{
public int NodeID { get; set; }
public string? Name { get; set; }
public String? AssignedOrg { get; set; }
public String? ContractingOrg { get; set; }
public DateTime? Expiry { get; set; }
[DisplayFormat(DataFormatString = "{0:MMM dd,yyyy}", ApplyFormatInEditMode = true)]
public DateTime? LastActive { get; set; }
[DisplayFormat(DataFormatString = "{0:MMM dd,yyyy}", ApplyFormatInEditMode = true)]
public NodeType? NodeType { get; set; }
public String? Comment { get; set; }
}
}
DbContext ModelBuilder:
modelBuilder.Entity<NodeIndexViewModel>()
.ToSqlQuery("select DISTINCT ON(s.id) s.id as NodeID, s.name as Name, bn.node_type as NodeType, t.short_name as AssignedOrg, o.short_name as ContractingOrg, bn.end_utc as Expiry, s.active_date as LastActive, s.comment as Comment from admin.node s FULL OUTER JOIN admin.bundle_node bnn on s.id = bnn.node_id FULL OUTER JOIN admin.bundle bn on bnn.bundle_id = bn.id FULL OUTER JOIN admin.agreement ag on bn.agreement_id = ag.id FULL OUTER JOIN admin.organization o on ag.org_id = o.id JOIN admin.organization t on t.id = s.org_id order by s.id, bn.end_utc")
.HasKey(m => m.NodeID);
DbContext DbSet:
public DbSet<NodeIndexViewModel> NodeIndexViewModel { get; set; }
The initial load of the View works perfectly. The View code isn't relevant because the issue is arising when trying to filter the list in the controller with the following lines of code:
if (!String.IsNullOrEmpty(searchString))
{
results = results.Where(s => s.ContractingOrg.Contains(searchString)
|| s.NodeID.ToString().StartsWith(searchString));
}
The solution won't build and I am getting CSO266 casting error.
This is the suggested fix:
results = (List<NodeIndexViewModel>)results.Where(s => s.ContractingOrg.Contains(searchString)
|| s.NodeID.ToString().StartsWith(searchString));
And this is the error message:
InvalidCastException: Unable to cast object of type 'WhereListIterator1[UnidAdmin.Models.NodeIndexViewModel]' to type 'System.Collections.Generic.List1[UnidAdmin.Models.NodeIndexViewModel]'.
Try this
var results = from s in _context.NodeIndexViewModel
select s;
if (!String.IsNullOrEmpty(searchString))
{
results = results.Where(s => s.ContractingOrg.Contains(searchString)
|| s.NodeID.StartsWith(searchString));
}
int pageSize = 10;
int pageNumber = (page ?? 1);
return View(await results.OrderBy(x => something).ToPagedListAsync(pageNumber, pageSize));
If you use list in the initial query, that would defeat the purpose of having PageList. As it loads everything out and consume memory. If you really need to use toList() on the initial query, you will need to include to list in the filter as well. So you will need to put tolist() on the filtering.
Finally IpageList won't work without ordering. So you need to put an order in order to get the pagelist to work.
The code below does not compile. The compiler error is
Cannot implicitly convert type 'System.Collections.Generic.List<AnonymousType#1>' to 'System.Collections.Generic.List<AnimeVoter.Models.MyTitleObject>' d:\dev\mvc\AnimeVoter\AnimeVoter\Controllers\SearchController.cs
Below is the code.
[HttpPost]
public ActionResult Search(FormCollection collection)
{
string filter = collection["search_fld"];
List<MyTitleObject> list =
(
from O in db.Titles
join K in db.TitleDescriptions on O.Id equals K.TitleId
where O.Name.ToLower() == filter.ToLower()
select new { Name = O.Name, Description = K.Description, UrlImage = O.UrlImage, Votes = O.Votes }
).ToList(); // this is where the error points at
return View(list);
}
where MyTitleObject is defined as
public class MyTitleObject
{
public string Name { get; set; }
public string Description { get; set; }
public string UrlImage { get; set; }
public int Votes { get; set; }
}
In summary what I am trying to achieve is to send the resulting list to the view.
You just need to specify the type on the select line:
select new MyTitleObject { Name = O.Name, Description = K.Description, UrlImage = O.UrlImage, Votes = O.Votes }
I admit, I'm confused:
I'm trying to return a simple object that I've converted to JSON as follows:
viewModel.updateCoder = function (coder) {
var coderJson = ko.toJSON(coder);
var coderJsonString = ko.utils.stringifyJson(coderJson);
$.ajax({
url: "provider/UpdateCoder",
type: 'POST',
dataType: 'text',
data: coderJsonString,
contentType: 'text/csv',
success: function () { alert("Updated!"); }
});
My RouteTable entry looks like this:
routes.MapRoute(
"UpdateCoder",
"provider/UpdateCoder/{coderDTO}", // URL with parameters
new { controller = "Provider", action = "UpdateCoder", coderDTO = UrlParameter.Optional }
);
my Controler action looks like this:
[AcceptVerbs(HttpVerbs.Post)]
public string UpdateCoder( string coderDTO )
{
var rslt = "success";
//var coder = coderDTO.CoderDTOToCoder();
return rslt;
}
What I get in the UpdateCoder parameter ( string coderDTO ) is a null;
This is my fall-back position I'd rather send a JSON object (the coderJson) to the action but I get an error: "No parameterless constructor defined for this object." When I do that I'm changing the parameter type as follows:
[AcceptVerbs(HttpVerbs.Post)]
public string UpdateCoder( **CoderDTO coderDTO** )
{
var rslt = "success";
//var coder = coderDTO.CoderDTOToCoder();
return rslt;
}
along with: ValueProviderFactories.Factories.Add(new JsonValueProviderFactory()); in the Global.asax
the CoderDTO class looks like this:
public class CoderDTO
{
public Int32 Id { get; set; }
public String CoderCode { get; set; }
public String Sal { get; set; }
public String LName { get; set; }
public String FName { get; set; }
public String MI { get; set; }
public String Facility { get; set; }
public String Title { get; set; }
public Boolean? IsContract { get; set; }
public Boolean? IsInactive { get; set; }
public Boolean? IsDeleted { get; set; }
public String Comments { get; set; }
public String AlternateId { get; set; }
public int CasesCoded { get; set; }
public CoderDTO(Coder coder)
{
Id = coder.Id;
CoderCode = coder.CoderCode;
Sal = coder.Sal;
LName = coder.LName;
FName = coder.FName;
MI = coder.MI;
Facility = coder.Facility;
Title = coder.Title;
if (coder.IsContract != null) IsContract = coder.IsContract;
if (coder.IsInactive != null) IsInactive = coder.IsInactive;
if (coder.IsDeleted != null) IsDeleted = coder.IsDeleted;
Comments = coder.Comments;
AlternateId = coder.AlternateId;
}
public Coder CoderDTOToCoder()
{
var coder = new Coder
{
Id = Id,
CoderCode = CoderCode,
Sal = Sal,
LName = LName,
FName = FName,
MI = MI,
Facility = Facility,
Title = Title
};
coder.IsContract = IsContract ?? false;
coder.IsInactive = IsInactive ?? false;
coder.IsDeleted = IsDeleted ?? false;
coder.Comments = Comments;
coder.AlternateId = AlternateId;
return coder;
}
}
The coderJsonString looks like this:
{"Id":201,"CoderCode":"GP ","Sal":null,"LName":null,"FName":null,"MI":null,"IsContract":false,"IsInactive":false,"Comments":null,"CasesCoded":0,"isBeingEdited":false}
It's been a long day!! Thanks for any help, I'm having dinner!!
I found the answer to the question of why I can't return a JSON that deserializes to my CoderDTO object: My object did not have a parameterless public constructor. I had a constructor parameter of a Coder which populated the CoderDTO. I split that off into a separate method and that now works.
Thanks to a post by
StackOverflow - ASP.NET MVC 3 JSONP: Does this work with JsonValueProviderFactory?
I think that your best bet is trying to figure out why you can't deserialize to your DTO. You will need to at least add a default constructor to it.
public CoderDTO() { }
For your current situation with passing strings, I think that you would want to call it like:
viewModel.updateCoder = function (coder) {
var coderJson = ko.toJSON(coder);
var coderJsonString = ko.utils.stringifyJson({ coderDTO: coderJson });
$.ajax({
url: "provider/UpdateCoder",
type: 'POST',
dataType: 'text',
data: coderJsonString,
contentType: 'application/json; charset=utf-8',
success: function () { alert("Updated!"); }
});
So, basically you create an object with the parameter name and value and stringify it. The coderJson is double-encoded in this case.
Try changing your action to use JsonResult:
[AcceptVerbs(HttpVerbs.Post)]
public JsonResult UpdateCoder(CoderDTO coderDTO)
{
var rslt = "success";
//var coder = coderDTO.CoderDTOToCoder();
return Json(rslt);
}
I have a search form where I can search on one of two fields. Only one of the two is required. Is there a way to cleanly do this in the MVC provided validation?
If this is server validation, you could create a Custom Validation Attribute for this:
[ConditionalRequired("Field1","Field2")]
public class MyModel()
{
public string Field1 { get; set; }
public string Field2 { get; set; }
}
Then you can create a custom validation attribute.. something like the following:
[AttributeUsage( AttributeTargets.Class, AllowMultiple = true, Inherited = true )]
public sealed class ConditionalRequiredAttribute : ValidationAttribute
{
private const string _defaultErrorMessage = "Either '{0}' or '{1}' must be provided.";
public string FirstProperty { get; private set; }
public string SecondProperty { get; private set; }
public ConditionalRequiredAttribute( string first, string second )
: base( _defaultErrorMessage )
{
FirstProperty = first;
SecondProperty = second;
}
public override string FormatErrorMessage( string name )
{
return String.Format( CultureInfo.CurrentUICulture, ErrorMessageString,
FirstProperty, SecondProperty );
}
public override bool IsValid( object value )
{
PropertyDescriptorCollection properties = TypeDescriptor.GetProperties( value );
string originalValue = properties.Find( FirstProperty, true ).GetValue( value ).ToString(); // ToString() MAY through a null reference exception.. i haven't tested it at all
string confirmValue = properties.Find( SecondProperty, true ).GetValue( value ).ToString( );
return !String.IsNullOrWhiteSpace( originalValue ) || !String.IsNullOrWhiteSpace( confirmValue ); // ensure at least one of these values isn't null or isn't whitespace
}
}
Its a little verbose, but it gives you the flexibility to able this attribute directly to your model as opposed to applying it to each individual field
I'm using LINQ-to-SQL for CRUD functionality, and DataContractJsonSerializer to serialize the object to JSON. I am also using ASP.NET MVC's data binding to post values to an MVC action that does the inserting. The problem is that it will serialize all of the properties except the Id property. I've got the model set up as so:
[Serializable]
[DataContract(Name = "campaign")]
[Table(Name = "hl.campaigns")]
public class Campaign
{
[DataMember(Name = "id")]
[Column(Name = "id", AutoSync = AutoSync.OnInsert, IsDbGenerated = true, IsPrimaryKey = true)]
public Int32 Id { get; set; }
[DataMember(Name = "createdBy")]
[Column(Name = "created_by")]
public Int32 CreatedBy { get; set; }
[DataMember(Name = "createdOnUtc")]
[Column(Name = "created_on_utc")]
public DateTime CreatedOnUtc { get; set; }
[DataMember(Name = "name")]
[Column(Name = "name", DbType = "NVarChar(256)")]
public String Name { get; set; }
/* more properties here */
}
Here is my custom JsonDataContractActionResult:
public class JsonDataContractActionResult : ActionResult
{
public JsonDataContractActionResult(Object data)
{
this.Data = data;
}
public Object Data { get; private set; }
public override void ExecuteResult(ControllerContext context)
{
var serializer = new DataContractJsonSerializer(this.Data.GetType());
String output = String.Empty;
using (var ms = new MemoryStream())
{
serializer.WriteObject(ms, this.Data);
output = Encoding.Default.GetString(ms.ToArray());
}
context.HttpContext.Response.ContentType = "application/json";
context.HttpContext.Response.Write(output);
}
}
Here's the action (JsonContract() returns a JsonDataContractActionResult):
[AcceptVerbs(HttpVerbs.Post)]
public ActionResult Modify([Bind(Prefix = "campaign")] Campaign campaign)
{
if (campaign.Id == 0)
{
try
{
CoreDB.Campaigns.InsertOnSubmit(campaign);
CoreDB.SubmitChanges();
return JsonContract(campaign);
}
catch(Exception ex)
{
// TODO: error handling
}
}
return null; // TODO: modification
}
The only thing I can think of is that somehow data binding is preventing the Id property from being serialized because it was populated after it was deserialized from the form data. Any suggestions?
What does the regular Json() method return for this object?
According to this post... there might be an issue with the automatic backing fields in C#:
http://aaron-powell.spaces.live.com/blog/cns!91A824220E2BF369!150.entry