Error when adding Where or OrderBy clauses to Azure Mobile Apps request - azure

I'm developing an Azure Mobile App service to interface to my Xamarin application.
I've created, connected and successfully populated an SQL Database, but when I try to add some filters to my request, for example an orderby() or where() clauses, it returns me a Bad Request error.
For example, this request: https://myapp.azurewebsites.net/tables/Race?$orderby=iRound%20desc,iYear%20desc&$top=1&ZUMO-API-VERSION=2.0.0 gives me {"message":"The query specified in the URI is not valid. Could not find a property named 'IYear' on type 'MyType'."}.
My configuration method is this:
HttpConfiguration config = new HttpConfiguration();
new MobileAppConfiguration()
.AddTablesWithEntityFramework()
.ApplyTo(config);
config.MapHttpAttributeRoutes();
Database.SetInitializer(new CreateDatabaseIfNotExists<MainDataContext>());
app.UseWebApi(config);
and my DbContext is this:
public class MainDataContext : DbContext
{
private const string connectionStringName = "Name=MS_TableConnectionString";
public MainDataContext() : base(connectionStringName)
{
Database.Log = s => WriteLog(s);
}
public void WriteLog(string msg)
{
System.Diagnostics.Debug.WriteLine(msg);
}
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Conventions.Add(
new AttributeToColumnAnnotationConvention<TableColumnAttribute, string>(
"ServiceTableColumn", (property, attributes) => attributes.Single().ColumnType.ToString()));
}
public DbSet<Race> Race { get; set; }
public DbSet ...ecc...
}
Following this guide, I added a migration after creating my TableControllers. So the TableController for the example type shown above is pretty standard:
[EnableQuery(AllowedQueryOptions = AllowedQueryOptions.All)]
public class RaceController : TableController<Race>
{
protected override void Initialize(HttpControllerContext controllerContext)
{
base.Initialize(controllerContext);
MainDataContext context = new MainDataContext();
DomainManager = new EntityDomainManager<Race>(context, Request);
}
// GET tables/Race
[EnableQuery(AllowedQueryOptions = AllowedQueryOptions.All)]
public IQueryable<Race> GetAllRace()
{
return Query();
}
// GET tables/Race/48D68C86-6EA6-4C25-AA33-223FC9A27959
public SingleResult<Race> GetRace(string id)
{
return Lookup(id);
}
// PATCH tables/Race/48D68C86-6EA6-4C25-AA33-223FC9A27959
public Task<Race> PatchRace(string id, Delta<Race> patch)
{
return UpdateAsync(id, patch);
}
// POST tables/Race
public async Task<IHttpActionResult> PostRace(Race item)
{
Race current = await InsertAsync(item);
return CreatedAtRoute("Tables", new { id = current.Id }, current);
}
// DELETE tables/Race/48D68C86-6EA6-4C25-AA33-223FC9A27959
public Task DeleteRace(string id)
{
return DeleteAsync(id);
}
}
As you can see, I already tried to add the EnableQuery attribute to my TableController, as seen on Google. I also tried to add these filters to the HttpConfiguration object, without any success:
config.Filters.Add(new EnableQueryAttribute
{
PageSize = 10,
AllowedArithmeticOperators = AllowedArithmeticOperators.All,
AllowedFunctions = AllowedFunctions.All,
AllowedLogicalOperators = AllowedLogicalOperators.All,
AllowedQueryOptions = AllowedQueryOptions.All
});
config.AddODataQueryFilter(new EnableQueryAttribute
{
PageSize = 10,
AllowedArithmeticOperators = AllowedArithmeticOperators.All,
AllowedFunctions = AllowedFunctions.All,
AllowedLogicalOperators = AllowedLogicalOperators.All,
AllowedQueryOptions = AllowedQueryOptions.All
});
I don't know what to investigate more, as things seems to be changing too fast for a newbie like me who's first got into Azure.
EDIT
I forgot to say that asking for the complete table, so for example https://myapp.azurewebsites.net/tables/Race?ZUMO-API-VERSION=2.0.0, returns correctly the entire dataset. The problem occurs only when adding some clauses to the request.
EDIT 2
My model is like this:
public class Race : EntityData
{
public int iRaceId { get; set; }
public int iYear { get; set; }
public int iRound { get; set; }
ecc..
}
and the database table that was automatically created is this, including all the properties inherited from EntityData:
Database table schema

Digging into the source code, Azure Mobile Apps sets up camelCase encoding of all requests and responses. It then puts them back after transmission accordign to rules - so iRaceId becomes IRaceId on the server.
The easiest solution to this is to bypass the auto-naming and use a JsonProperty attribute on each property within your server-side DTO and client-side DTO so that they match and will get encoding/decoded according to your rules.
So:
public class Race : EntityData
{
[JsonProperty("id")]
public string Id { get; set; }
[JsonProperty("raceId")]
public int iRaceId { get; set; }
[JsonProperty("year")]
public int iYear { get; set; }
[JsonProperty("round")]
public int iRound { get; set; }
etc..
}

Related

How to get nested element using ServiceStack?

Although I am able to access the SchemaVersion using code below, I cannot access FormatDocID nested element.
Any ideas how can I easily get FormatDocID using ServiceStack and AutoQueryFeature (or similar)?
I put only relevant parts of code here
public override void Configure(Container container)
{
JsConfig.DateHandler = DateHandler.ISO8601;
SetupValidators(container);
SetupIOC(container);
SetupPlugins(container, log);
ContentTypes.Register("application/xml"
, CLXmlSerializer.Serialize, ServiceStack.Text.XmlSerializer.DeserializeFromStream);
SetupMetaDataRedirectionPath();
SetupGlobalResponseFilters();
}
Setup plugins
private void SetupPlugins(Container container)
{
Plugins.Add(new ValidationFeature());
Plugins.Add(new SwaggerFeature());
Plugins.Add(new AutoQueryFeature
{
MaxLimit = 1000,
EnableUntypedQueries = false,
IncludeTotal = true
});
Plugins.Add(new AutoQueryDataFeature {MaxLimit = 100}
.AddDataSource(ctx => ctx.MemorySource(new List<WordDocument>
{
new WordDocument()
{
SchemaVersion = "",
Format = new Word.DocumentFormat()
{
FormatDocID = 254
}
}
}))
);
typeof(RequestLogs).AddAttributes(new RestrictAttribute {VisibilityTo = RequestAttributes.None});
typeof(AssignRoles).AddAttributes(new RestrictAttribute {VisibilityTo = RequestAttributes.None});
typeof(UnAssignRoles).AddAttributes(new RestrictAttribute {VisibilityTo = RequestAttributes.None});
typeof(Authenticate).AddAttributes(new RestrictAttribute {VisibilityTo = RequestAttributes.None});
}
Serializable classes
public abstract class Document
{
public DocumentFormat Format;
public class DocumentFormat
{
[XmlAttribute] public int Version;
public int FormatDocID;
public string DocShortName;
}
}
public class WordDocument : Document
{
[XmlAttribute] public string SchemaVersion { get; set; } = "1.0";
}
Thanks in advance for the answers.
It's not clear what you're trying to achieve or why, AutoQuery creates Auto Queryable APIs where the Response is the API Response serialized in the specified Response Content Type.
If you want to intercept the Typed Response DTO before it's returned you can create a Custom AutoQuery Implementation and introspect the response that way, e.g:
public class MyQueryServices : Service
{
public IAutoQueryData AutoQuery { get; set; }
//Override with custom implementation
public object Any(MyQuery query)
{
var q = AutoQuery.CreateQuery(query, base.Request);
var response = AutoQuery.Execute(query, q);
return response;
}
}
But the AutoQuery Memory Data Source you're using lets you provide your own collection of Typed POCOs as the Data source so you already have access to them when you create it, but the source POCOs should be a flat Type with public properties (in contrast to your class with public fields and nested types) - it's not possible to query nested object graph values.
This is an example of a POCO that doesn't use nested classes, or public fields:
public abstract class Document
{
public int Version { get; set; }
public int FormatDocID { get; set; }
public string DocShortName { get; set; }
}
So the solution if you want to use AutoQuery would be to change your Data Source to use Flat POCOs with public properties otherwise you'd need to create the impl of your Service yourself.

Abstract Azure IMobileServiceTable<T> behind repository

I want my repsository to be independent of the data access technology. Currently I am working on a Xamrin.Forms App that uses Azure Mobile App Services for data access. For performance and flexibility reasons I want my repository to look simmilar like the following:
Task<IEnumerable<IDomainObject>> GetDomainObjectAsync(Func<IQueryable<IDomainObject>, IQueryable<IDomainObject>> query)
Suppose my IDomainObject looks like the following:
public interface IDomainObject
{
string Name { get; }
}
and my DataAccess Object:
internal class AzureDomainObject : IDomainObject
{
public string Name { get; set; }
public string Id { get; set; }
}
As far as I found out and tested I can do the following to query the database within my repository implementation:
public async Task<IEnumerable<IDomainObject>> GetDomainObjectAsync(Func<IQueryable<IDomainObject>, IQueryable<IDomainObject>> query)
{
// _table of type IMobileServiceTable<AzureDomainObject> gotten by MobileServiceClient
var tableQuery = _table.GetQuery();
tableQuery.Query = tableQuery.Query.Take(4); // 1) this was for testing and it works (ordering etc also works)
// tableQuery.Query = query(tableQuery.Query); // 2) this was my initial idea how to use the input param
await _table.ReadAsync(tableQuery);
}
My poblem now is how to use the input param query to replace 1) with 2).
tableQuery.Query expects an IQueryable<AzureDomainObject> but query is of type IQueryable<IDomainObject>.
Neither .Cast<AzureDomainObject>() nor .OfType<AzureDomainObject>() work to convert. Nor does (IQueryable<IAzureDomainObject>)query; work.
Cast and OfType throw NotSupportedException and the hard cast throws an InvalidCastException.
I also tried to extract the Expression from the query input param and assign it to the tableQuery.Query. But then a runtime exception occurs that they are not compatible.
Another idea I had was to use the ReadAsync(string) overload and pass the string representation of the passed query param. But this way I don't know how to generate the string.
So the final question is: Does anyone knows how to hide both AzureDomainObject and IMobileServiceTable from the domain model but keep the flexibility and performance benefits of IQueryable in the repository interface?
According to your description, I checked this issue and here is my implementation for this scenario, you could refer to them.
Model:
public class TodoItem : IDomainObject
{
public string Id { get; set; }
[JsonProperty(PropertyName = "text")]
public string Text { get; set; }
[JsonProperty(PropertyName = "complete")]
public bool Complete { get; set; }
}
public interface IDomainObject
{
string Id { get; set; }
}
Repository:
public interface IAzureCloudTableRepository<T> where T : IDomainObject
{
Task<IEnumerable<T>> GetDomainObjectAsync(Func<IQueryable<T>, IQueryable<T>> query);
}
public class AzureCloudTableRepository<T> : IAzureCloudTableRepository<T> where T : IDomainObject
{
IMobileServiceTable<T> table;
public AzureCloudTableRepository(MobileServiceClient client)
{
this.table = client.GetTable<T>();
}
public async Task<T> CreateItemAsync(T item)
{
await table.InsertAsync(item);
return item;
}
public async Task<IEnumerable<T>> GetDomainObjectAsync(Func<IQueryable<T>, IQueryable<T>> query)
{
var tableQuery = this.table.CreateQuery();
tableQuery.Query = tableQuery.Query.Take(4); //the internal fixed query
tableQuery.Query = query(tableQuery.Query); //the external query
return await tableQuery.ToEnumerableAsync();
}
}
TEST:
var mobileService = new MobileServiceClient("https://{your-app-name}.azurewebsites.net");
var todoitem = new AzureCloudTableRepository<TodoItem>(mobileService);
var items = await todoitem.GetDomainObjectAsync((query) =>
{
return query.Where(q => q.Text!=null);
});

inherited class AutoMapper.AutoMapperMappingException

I am new at automapper and it is a very good stuff easy to use, but now I have a problem with it. Trying to convert my derived class to base and it gives me
AutoMapper.AutoMapperMappingException
Missing type map configuration or unsupported mapping.
Mapping types: ClientEventDb -> EventId
Database.ClientEventDb -> EventId
Destination path: ClientEvent
Source value:
Event:Login
Automapper wants to convert ClientEventDb to EventId? I don't understand why. EventId is an enum...
Please help me I have run out of ideas.
Here is the code which I run:
ClientEventDb[] edbl;
using (var context = new DbEntities())
{
edbl=context.Events.Take(1000).ToArray();
}
Mapper.CreateMap<ClientEventDb, ClientEvent>();
Console.WriteLine("hello");
return edbl.Select(edb => Mapper.Map<ClientEvent>(edb)).ToArray();
Here are my classes
[Table("events", Schema = "public")]
public class ClientEventDb : ClientEvent
{
public ClientEventDb(string userName, EventId happening, object userObject = null)
: base(userName, happening, userObject)
{
}
public ClientEventDb()
{
}
}
[ProtoContract]
[Table("events", Schema = "public")]
public class ClientEvent : ClientEventBase
{
[ProtoMember(1)]
[Column("username")]
public string UserName { get; private set; }
[ProtoMember(2)]
[Column("time")]
public DateTime DateTime { get; private set; }
[ProtoMember(3)]
[Key]
[Column("id")]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public string Id { get; private set; }
[ProtoMember(4)]
[Column("data")]
public byte[] UserObject { get; set; }
public ClientEvent(string userName,EventId happening, object userObject=null) : base(happening)
{
UserName = userName;
DateTime = DateTime.Now;
//UserObject = null;
if (userObject!=null) throw new NotImplementedException();
}
public ClientEvent()
{
}
protected ClientEvent Clone()
{
return (ClientEvent)MemberwiseClone();
}
}
[ProtoContract]
[ProtoInclude(10, typeof(ClientEvent))]
public class ClientEventBase
{
[Column("eventid")]
[ProtoMember(1)]
public int EventIdValue { get; set; } //must be public because of entity framework
[NotMapped]
public EventId EventId
{
get { return (EventId) EventIdValue; }
set { EventIdValue = (int) value; }
}
public ClientEventBase(EventId eventId)
{
EventId = eventId;
}
public ClientEventBase()
{
}
public override string ToString()
{
return String.Format("Event:{0}",EventId);
}
}
public enum EventId
{
Login = 1,
Logout,
ExitApplication,
}
UPDATE
bugfix: ClientEvent [Key] attribute moved to id property
Solution was this (thx to stuartd):
ClientEventDb[] edbl;
using (var context = new DbEntities())
{
edbl=context.Events.ToArray();
}
Mapper.CreateMap<ClientEventDb, ClientEvent>().ConstructUsing((ClientEventDb src) => new ClientEvent());
return edbl.Select(Mapper.Map<ClientEvent>).ToArray();
AutoMapper is confused as its made to map between similar properties in different classes, you are using it incorrectly - you just need to go from the derived class to the base which does not require AutoMapper. You could use this to do what you need....
ClientEventDb[] edbl;
using (var context = new DbEntities())
{
edbl=context.Events.Take(1000).ToArray();
}
return edbl.Cast<ClientEvent>().ToList();
I'd be looking at why you even feel you need a derived ClientEventDb though - understand we dont have the whole picture here but it seems to do nothing in addition to what the base class already does.
The issue is that ClientEvent has two constructors but you have not told AutoMapper which to use.
If you want it to use your constructor with parameters, change your mapping code to this and it will work:
Mapper.CreateMap<ClientEventDb, ClientEvent>()
.ConstructUsing(src => new ClientEvent(src.UserName, src.EventId));
Or to make AutoMapper use the default constructor:
Mapper.CreateMap<ClientEventDb, ClientEvent>()
.ConstructUsing((ClientEventDb src) => new ClientEvent());

ServiceStack Validation Feature Throws Exception

I am trying to implement validation feature in ServiceStack to validate my RequestDTO's before calling db operations.
When i try to validate request dto like
ValidationResult result = this.AddBookingLimitValidator.Validate(request);
the code automatically throws a validation error automatically.
I can not even debug service what is happening behind the scenes ? Can i change that behaviour or am i doing something wrong here.
Thanks.
My Request DTO :
[Route("/bookinglimit", "POST")]
[Authenticate]
public class AddBookingLimit : IReturn<AddBookingLimitResponse>
{
public int ShiftId { get; set; }
public DateTime Date { get; set; }
public int Limit { get; set; }
}
My Response DTO :
public class AddBookingLimitResponse
{
public int Id { get; set; }
public ResponseStatus ResponseStatus { get; set; }
}
Validation class :
public class AddBookingLimitValidator : AbstractValidator<AddBookingLimit>
{
public AddBookingLimitValidator()
{
RuleFor(r => r.Limit).GreaterThan(0).WithMessage("Limit 0 dan büyük olmalıdır");
}
}
Service Implementation :
public AddBookingLimitResponse Post(AddBookingLimit request)
{
ValidationResult result = this.AddBookingLimitValidator.Validate(request);
Shift shift = new ShiftRepository().Get(request.ShiftId);
BookingLimit bookingLimit = new BookingLimit
{
RestaurantId = base.UserSession.RestaurantId,
ShiftId = request.ShiftId,
StartDate = request.Date.AddHours(shift.StartHour.Hour).AddMinutes(shift.StartHour.Minute),
EndDate = request.Date.AddHours(shift.EndHour.Hour).AddMinutes(shift.EndHour.Minute),
Limit = request.Limit,
CreateDate = DateTime.Now,
CreatedBy = base.UserSession.UserId,
Status = (byte)Status.Active
};
return new AddBookingLimitResponse
{
Id = new BookingLimitRepository().Add(bookingLimit)
};
}
AppHost code :
container.RegisterValidators(typeof(AddBookingLimitValidator).Assembly);
Plugins.Add(new ValidationFeature());
And i consume the service in c# code:
try
{
AddBookingLimitResponse response = ClientHelper.JsonClient.Post(new AddBookingLimit
{
Date = DateTime.Parse(DailyBookingLimitDateTextBox.Text),
Limit = Convert.ToInt32(DailyBookingLimitTextBox.Text),
ShiftId = Convert.ToInt32(DailyDayTypeSelection.SelectedValue)
});
WebManager.ShowMessage(UserMessages.SaveSuccessful.FormatString(Fields.BookingLimit));
}
catch (WebServiceException ex)
{
WebManager.ShowMessage(ex.ResponseStatus.Message);
}
Right, ServiceStack validates the request DTO before the service gets called if the ValidationFeature is enabled.
To manually invoke the validator in the service, you have to remove this line from your AppHost first:
Plugins.Add(new ValidationFeature());
Please make sure that the validator property in your service has the type IValidator<>, otherwise it won't be injected by the IoC container if you register your validators with container.RegisterValidators(typeof(AddBookingLimitValidator).Assembly).
public class TestService : Service
{
public IValidator<Request> Validator { get; set; }
public RequestResponse Post(Request request)
{
Validator.Validate(request);
...
}
}

How to save data through Orchard module?

I'm new with orchard.
To learn orchard module development, I am following documentation to try to create a commerce module.
The module consists of product part and product type, which has product part.
When I try to save data in following method:
public ActionResult Create(FormCollection input)
{
var product = contentManager.New<ProductPart>("Product");
product.Description = input["Description"];
product.Sku = input["Sku"];
product.Price =Convert.ToDecimal(input["Price"]);
if (!ModelState.IsValid)
{
return View(product);
}
contentManager.Create(product);
return RedirectToAction("Index");
}
I am getting an error that specific cast is Invalid and part(ContentPart) is null.
public static T New<T>(this IContentManager manager, string contentType)
where T : class, IContent {
var contentItem = manager.New(contentType);
if (contentItem == null)
return null;
var part = contentItem.Get<T>();
if (part == null)
throw new InvalidCastException();
return part;
}
I used content type Product and I have ProductRecord class for storage data, as below:
public class ProductRecord:ContentPartRecord
{
// public virtual int Id { get; set; }
public virtual string Sku { get; set; }
public virtual string Description { get; set; }
public virtual decimal Price { get; set; }
}
public class ProductPart : ContentPart<ProductRecord>
{
/*
public int Id
{
get { return Record.Id; }
set{Record.Id = value;}
}
*/
[Required]
public string Sku
{
get { return Record.Sku; }
set { Record.Sku = value; }
}
[Required]
public string Description
{
get { return Record.Description; }
set{ Record.Description = value;}
}
[Required]
public decimal Price
{
get { return Record.Price; }
set { Record.Price = value; }
}
}
Can anybody tell me what my problem is?
I'm just guessing, but did you declare your record and your ContentType in migration.cs? If you didn't, the content management will be unable to create a content item with your type as it will not know the type in question.
Your migration.cs should look somehow like that:
public class Migrations : DataMigrationImpl
{
public int Create()
{
SchemaBuilder.CreateTable("ProductRecord",
table =>
{
table.ContentPartRecord()
.Column<string>("Sku")
.Column<string>("Description")
.column<decimal>("Price");
});
ContentDefinitionManager.AlterTypeDefinition("Product", cfg => cfg.WithPart("ProductPart"));
return 1;
}
}
On a side note, the naming convention in Orchard is to name the record for a part XXXPartRecord. I don't think your problem lies there though.
I have mentioned this in you other thread.. Orchard Content Type is null
you need
Migrations
public class Migrations : DataMigrationImpl {
public int Create() {
SchemaBuilder.CreateTable("ProductRecord",
table => table
.ContentPartRecord()
.COLUMNS NEED TO BE SPECIFIED
);
ContentDefinitionManager.AlterTypeDefinition("Forum",
cfg => cfg
.WithPart("ProductPart")
.WithPart("CommonPart")
);
Repository
public class ProductPartHandler : ContentHandler {
public ProductPartHandler(IRepository repository) {
Filters.Add(StorageFilter.For(repository));
}
Hope this helps
You could try generating a similar part using the command line utility by pszmyd and see whats different.
http://www.szmyd.com.pl/blog/generating-orchard-content-parts-via-command-line

Resources