How to address entity that uses composite identity key in OData Url? - url

I have an entity OrderItem that has OrderId and ProductId integer fields and these two fields form the identity key/primary key for this table.
I would like to use OData/Web API to expose such entities through a service and to be able to select OrderItem instances by they composite ID.
What should be the format of the URL?
Are there any best practices for handling such scenarios?

Composite keys in the URL use syntax like this:
~/OrderItems(OrderId=1234,ProductId=1234)
The "grammar" is defined in the OData ABNF Construction Rules (see the definition for "compoundKey")
An example usage can be found in OASIS' OData Version 4.0. Part 2: URL Conventions Plus Errata 03
Note that the "composite key" (aka "complex key predicate") has been around since OData 1.0.

First, you have to make sure that you explicitly mention that it has a composite key, in configuration file
builder.EntityType<OrderItem>().HasKey(t => new { t.OrderId, t.ProductId});
Then, the action should have the following header
public SingleResult<OrderItem> Get([FromODataUri] string keyOrderId, [FromODataUri] string keyProductId)
Please note the prefix(key) used for both parameters!
This is OData V4. Please also refer to https://odata.github.io/WebApi/13-06-KeyValueBinding/

Related

How to have one single RESTier 0.6.0 service picking the database depending on the request?

Scenario A: Suppose you have several databases on the same SQL Server/SQL Azure instance sharing the exact same structure, where the database names are convention based, like db001, db002, ... and a single RESTier service must be able to address the correct database for each REST call, depending on some characteristic to be provided on the request header or on the URI path or query string.
Scenario B: A single RESTier service must be able to address more than one connection string, defined on the web.config file, depending on some characteristic to be provided on the request.
In both scenarios the base issue is the same, a single RESTier service to be able to address requests for more than one database, where the client must submit on each request a hint to the database to be used, a typical multitenant scenario. I'm using RESTier version 0.6 and the entity sets are automatically exposed from a Entity Framework model:
public class Entities : EntityFrameworkApi<SalesEntities> {
Question is, how would you advise to implement this kind of multi-tenant scenarios?
You could use a parameterized routePrefix like below and parse that part of the URL (HttpContext.Current.Request.RawUrl).
config.MapRestierRoute<Entities>("EntitiesApi", "api/{entitiesConfig}", new RestierBatchHandler(server));
Retrieve the correct connectionString from your Web.config and use that to create the DbContext:
Match match = Regex.Match(HttpContext.Current.Request.RawUrl, #"/api/(?<entitiesConfig>[A-Za-z0-9\-]+)/", RegexOptions.IgnoreCase);
string entitiesConfig = "DefaultEntitiesContext";
if (match.Success)
entitiesConfig = match.Groups["entitiesConfig"];
string connectionString = System.Configuration.ConfigurationManager.ConnectionStrings[entitiesConfig].ConnectionString;
var db = new SalesEntities(connectionString);
For your scenario, I think you will have two DbContext, each one connect to one DB instance, then will something like this will work for you?
In the configure method, we call MapRestierRoute twice, and each one will handle one DbContext with different route prefix, and when user initiate the request, the request URL will contain prefix and will auto route to related DB instance.
config.MapRestierRoute>( "TrippinApi", "api/TrippinA", new RestierBatchHandler(server));
config.MapRestierRoute>( "TrippinApi", "api/TrippinB", new RestierBatchHandler(server));
Let me if you have any issues with this.

VB entitytype has no key defined. define the key for this entitytype

I am asking for a solution for VB not C#. Hence the "VB" tag at the start of my question.
Trying to create a controller for my model, however, I keep getting "entitytype has no key defined. define the key for this entitytype". I am using VB MVC5.
The solution is to import dataannotations and dataannotations.schema to your model, compile it and then add your controller. Also, make certain that your class has a int property.

How to only return a subset of specific fields from Model in REST APIController using .NET MVC4

I am writing a Web API that clients can make requests to to return data in XML format. I am implementing this in .NET using Enterprise Foundation and MVC4.
I am struggling a bit with how to only return a subset of some fields from my Models in my Controllers.
For arguments sake, lets say I have a Product model that contains attributes "Id", "Name", "Price" and "Actual Cost" (I am using an example from http://www.asp.net/web-api/overview/creating-web-apis/using-web-api-with-entity-framework/using-web-api-with-entity-framework,-part-6)
I need to expose a Web API for clients to query a specific Product to get its name and price, but in this response I don't want to return the "Actual Cost" property (because this is our secret).
Now in the link I provide this is exactly the problem they are attempting to solve by the use of DTO's (they define a DTO called ProductDTO that contains only the subsets I want to return). I have implemented this solution and I am indeed now able to return only the fields I specify in the DTO.
The problem is that the naming used for the returned entity in XML is now ProductDTO rather than Product, i.e. the returned XML is
{"ProductDTO":[{"Id":1,"Name":"Tomato Soup","Price":1.39}, {"Id":3,"Name":"Yo yo","Price":6.99]}
rather than
{"Product":[{"Id":1,"Name":"Tomato Soup","Price":1.39}, {"Id":3,"Name":"Yo yo","Price":6.99]}
That means that all of our clients currently using our API and expects a "Product" to be returned will now get a "ProductDTO" returned, which means that they will have to make changes to their code and which is unacceptable. I need to provide them with a "Product" with only the relevant set of sub-fields as they are currently getting. How do I achieve this? I cannot simply "ignore" a data member as suggested in prevent property from being serialized in web api because I also have some API cases where I indeed DO need to return ALL the attributes and not only a subset.
Just some background: We have an existing API server interface that was written in Ruby on Rails and we are now moving this over to C# and .NET MVC4. We also have a bunch of client applications already interfacing to our existing, older, Ruby on Rails API Server and we don't want clients to make any changes to their code. We are simply moving our API server code over from Ruby on Rails to C#. In Ruby on Rails I was simply able to apply a filter to the XML Serializer when I need to only return a subset of attributes on certain calls.
If you want to continue down the DTO route that you have started, which IMHO is a good idea as it gives you control of what you export without polluting your internal classes with export specific stuff, you can add a DataContract attribute to your ProductDTO class.
[DataContract(Name="Product")]
public class ProductDTO
{
[DataMember]
public int Id {get;set;}
[DataMember]
public string Name {get;set;}
}
The default XML formatter used in Web API is the DataContractSerializer.
You can read more about this here
Suppose you have a class like
public class product
{
public string Name{get; set;}
..
}
and you don't want to appear it in the response you can just use [XMLIgnore] attribute
[XMLIgnore]
public string Name{get; set;}
hopes this helps.

Is it possible to extend Breeze Metadata received from the server?

Wondering if anyone knows of any way to extend or configure Breeze so that the server returns additional info in the entity metadata? I'd like to use this additional data to assist with validation.
Assume I have an entity model like so with some Data Annotations applied:
public class Person {
[RegularExpression(#"^$|^http\://[a-zA-Z0-9\-\.]+\.[a-zA-Z]{2,3}(/\S*)?",
ErrorMessage="The Website address does not appear to be valid.")]
public string Website { get; set; }
[Required(ErrorMessage="The Name field is required."),
MaxLength(150, ErrorMessage = "The Name field cannot exceed 150 characters."),
MinLength(5, ErrorMessage = "The Name field must be at least 5 characters.")]
public string Name { get; set; }
//...
}
Right now, Breeze only hooks up a MaxLength and Required Validator based on the metadata it receives since this is all it supports out of the box. If Breeze could include in the metadata the info described in the Data Annotation Attributes on the server entity, I'm thinking it would then be possible for Breeze to automatically add additional stock validators to the client EntityType (e.g. for RegEx, Range, MinLength, etc... ). This would cover the majority of basic validation use cases. Or, it could also allow developers to inspect the metadata and pull out useful info like the regEx string which we could use to hook up our own custom RegEx validator.
Also, is there any way to have Breeze include the value of the ErrorMessage validation attribute property in the metadata and then have the breeze client use that instead of the default required and maxLength messageTemplates? This would mean you would only have to define the error message in one place on the server and wouldn't have to customize it for each entity.
I'm trying to avoid having to create and register a bunch of custom validators on the client for what seems like basic validations that could be handled by Breeze automatically.
Thanks,
Richard
It's a great question.
We haven't yet done a good job of documenting how the server serializes metadata but this should be coming "real soon now". However, if you take a look at the json coming over the wire you'll notice that validators are serialized simply by name. This name is then looked up among the registered validators ( or validator factories) on the client and then added to the client side metadata. So the idea would be to register you validator "implementation" on the client with a unique name, and then have the server reference this name when sending metadata down from the server.
Hopefully this will be clearer in a week or so once we have documented how to create your own server side metadata to send down to the client.
Hmmm, one year has passed. Any news on this topic? I fully agree with RWHepburn that defining all validation rules on the server-side and have it available in breeze on the client side would be a perfect thing. This is what data annotations in EF are for - making it easier!

Working with enumerations and passing it in my web service

I have an ASP.NET MVC solution and also a WCF service for data access. In my WebUI I have some enums like this:
public enum CompanyColumsEnumViewModel
{
CompanyName,
CompanyPostCode,
CompanyCity
}
This enum allows me to perform a search based on specific columns. For example: searching for '2000' in the CompanyPostCode field.
When a user submit a search formular, I call my service and I pass several parameters like this:
var companies = _requestServiceClient.GetCompanies(term, column);
Where term = '2000' and column = 'CompanyPostCode'
My question is what is the best:
pass column as en enumeration in the web service? (1)
pass column as simple string in my web service? (2)
(1) IEnumerable< Company> GetCompanies(string term, CompanyColumsEnumViewModel column)
(2) IEnumerable< Company> GetCompanies(string term, string column)
If I work with enumerations I have to declare these enumerations on both side: one in my WebUI and one in my web service?
Thanks.
If your enum is part of your ServiceContract (as in it exists as a parameter) then you need to attribute it up as a DataContract:
[DataContract]
public enum CompanyColumsEnumViewModel
{
[EnumMember]
CompanyName,
[EnumMember]
CompanyPostCode,
[EnumMember]
CompanyCity
}
Then, you can discover this in a service reference on the client side. This, I think, would be prefered to a string column.
You can declare your enum as normal in a separate file. Custom objects can explicitly be given defined data contracts and members, but if the enum is exposed as a parameter in a WCF service, then WCF is smart enough to go back and serialize it out to the WSDL itself.
This works the same for all types. If you have a class as a parameter in a WCF method, and that class has this enum as a property, then the class and the enum would both be exposed through the WSDL, and would be auto-generated on the client.

Resources