I'm building an OData Web API using ASPNetCore. The EdmModel is build like
ODataConventionModelBuilder builder = new ODataConventionModelBuilder();
builder.EntitySet<MyType>("MyType");
edmModel = builder.GetEdmModel();
This is MyType
public class MyType
{
[Key]
public string Id { get; set; }
[Required]
public Uri Uri { get; set; }
}
This generates an EdmModel like this
<Schema Namespace="Test" xmlns="http://docs.oasis-open.org/odata/ns/edm">
<EntityType Name="MyType">
<Key>
<PropertyRef Name="Id" />
</Key>
<Property Name="Uri" Type="System.Uri" Nullable="false" />
</EntityType>
</Schema>
<Schema Namespace="System" xmlns="http://docs.oasis-open.org/odata/ns/edm">
<ComplexType Name="Uri">
<Property Name="Segments" Type="Collection(Edm.String)" />
</ComplexType>
</Schema>
When I query the OData controller I'm getting this response
{
"#odata.context": "https://localhost:44389/v3/$metadata#MyType/$entity",
"Id": "ID-1234",
"Uri": {
"Segments": [
"/",
"path/",
"to/",
"document"
]
}
}
That's not at all what I want.
How can I get this to return me the complete URI as a string?
Is there an attribute I need to add to the model, or is there a way to configure the builder differently?
Related
I am new to struts2. What I am trying is to get data from the client and insert into the db.But while doing this setImageFile(File fie) isnt called and thus imageFile object is null. While other set methods are called and is thus respective members contains values.
action.class
import com.opensymphony.xwork2.ActionSupport;
import java.io.File;
import java.io.FileInputStream;
import java.io.InputStream;
import java.sql.*;
import java.util.Map;
import javax.servlet.http.Part;
import org.apache.struts2.interceptor.SessionAware;
import org.apache.struts2.dispatcher.SessionMap;
public class register extends ActionSupport implements SessionAware{
SessionMap<String, String> sessionMap;
private String name;
private String email;
private String dob;
private String address;
private File imageFile;
private String imageFileFileName;
private String imageFileContentType;
#Override
public void setSession(Map map)
{
sessionMap = (SessionMap)map;
sessionMap.put("", name);
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
public String getDob() {
return dob;
}
public void setDob(String dob) {
this.dob = dob;
}
public String getAddress() {
return address;
}
public void setAddress(String address) {
this.address = address;
}
public File getImageFile() {
return imageFile;
}
public void setFile(File imageFile) {
this.imageFile = imageFile;
}
public void setSessionMap(SessionMap<String, String> sessionMap) {
this.sessionMap = sessionMap;
}
public String execute()
{
try
{
Class.forName("org.postgresql.Driver");
Connection conn=DriverManager.getConnection("jdbc:postgresql://localhost:5432/DemoDB", "postgres", "postgres");
InputStream imageInputStream = new FileInputStream(imageFile);
String query = "insert into userProfile('Name','Address','email','dob','img') values(?,?,?,?,?)";
Date date = Date.valueOf(dob);
PreparedStatement preparedStatement = conn.prepareStatement(query);
preparedStatement.setString(0, name);
preparedStatement.setString(1, address);
preparedStatement.setString(2, email);
preparedStatement.setDate(3, date);
preparedStatement.setBinaryStream(4, imageInputStream, imageFile.length());
preparedStatement.execute();
}
catch(Exception e)
{
System.out.println(e.getMessage());
}
return "success";
}
}
In the above code imageFile is null. And othe members contains value
struts.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE struts PUBLIC "-//Apache Software Foundation//DTD Struts
Configuration 2.1//EN" "http://struts.apache.org/dtds/struts-2.1.dtd">
<struts>
<package name="register" namespace="/" extends="tiles-default">
<action name="register" class="com.passportseva.register">
<result name="success" type="tiles">login</result>
<result name="input" type="tiles">register</result>
<result name="error">layoutmanager.jsp</result>
</action>
</package>
</struts>
This is jsp where the action is called
registration.jsp
<%#page contentType="text/html" pageEncoding="UTF-8"%>
<%# taglib prefix="s" uri="/struts-tags" %>
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>JSP Page</title>
</head>
<body>
<form action="register" method="POST">
<div>
<div><h1>Name</h1></div>
<input type="text" name="name" hint="name"/>
</div>
<div>
<div><h1>Email</h1>></div>
<input type="text" name="email" hint="email"/>
</div>
<div>
<div><h1>DOB</h1></div>
<input type="date" name="dob"/>
</div>
<div>
<div><h1>Address</h1></div>
<input type="area" name="address" hint="address"/>
</div>
<div>
<s:file name="imageFile" label="image"/>
</div>
<input type="submit" value="register"/>
</form>
</body>
</html>
I used tiles here
tiles.xml
<!DOCTYPE tiles-definitions PUBLIC
"-//Apache Software Foundation//DTD Tiles Configuration 2.0//EN"
"http://tiles.apache.org/dtds/tiles-config_2_0.dtd">
<tiles-definitions>
<definition name="login" template="/layoutmanager.jsp">
<put-attribute name="title" value="Login Page"/>
<put-attribute name="body" value="/login.jsp"/>
</definition>
<definition name="register" template="/layoutmanager.jsp">
<put-attribute name="title" value="Passport seva registration"/>
<put-attribute name="body" value="/registration.jsp"/>
</definition>
</tiles-definitions>
The output says that invalid field value for imageFile.
Use Struts Form tag with enctype attribute.
<s:form action="register" method="POST" enctype="multipart/form-data">
</s:form>
I am introducing API versioning to an existing API. The existing JSON uses Pascal casing for its property names e.g. "FooBar": "foo". For v2 of the API, I would like to use the common camel casing, "fooBar": "foo". I need to keep v1 Pascal casing so that it does not impact any client that is already pulling that version of the API.
My project is
ASP.NET MVC 5.2.7
ASP.NET WEB API 5.2.7
ASP.NET ODATA 7.4.0
ASP.NET WEB API Versioning 4.0.0
My configuration is as follows
public static class WebApiConfig
{
public static void Register(HttpConfiguration configuration)
{
configuration.AddApiVersioning(options => options.ReportApiVersions = true);
var modelBuilder = new VersionedODataModelBuilder(configuration);
AppDomain.CurrentDomain.GetAssemblies()
.SelectMany(x => x.GetTypes())
.Where(x => typeof(IModelConfiguration).IsAssignableFrom(x) && !x.IsInterface && !x.IsAbstract)
.ForEach(t => modelBuilder.ModelConfigurations.Add((IModelConfiguration)Activator.CreateInstance(t)));
var models = modelBuilder.GetEdmModels();
configuration.MapVersionedODataRoutes("odata-bypath", "api/v{apiVersion}", models, builder =>
{
builder.AddService<IODataPathHandler>(Singleton, sp => new DefaultODataPathHandler { UrlKeyDelimiter = Parentheses });
builder.AddService<ODataUriResolver>(Singleton, sp => new UnqualifiedCallAndEnumPrefixFreeResolver { EnableCaseInsensitive = true });
});
configuration.Count().Filter().OrderBy().Expand().Select().MaxTop(null);
configuration.MapHttpAttributeRoutes();
}
}
After reading through the docs and specifically Versioned ODataModelBuilder I have not found a way to change the casing based on which version of the API the model is being built for. I can get it to be all Pascal casing or all camel casing, but not v1 Pascal casing and v2 camel casing.
Adjusting the above configuration
var modelBuilder = new VersionedODataModelBuilder( configuration )
{
ModelBuilderFactory = () => new ODataConventionModelBuilder().EnableLowerCamelCase()
};
would use camel casing (yes I know the explicit call is unnecessary since it is the default). Then I built my own extension method ODataConventialModelBuilder().EnablePascalCase() that mimicked EnableLowerCamelCase() method to get Pascal casing to work.
var modelBuilder = new VersionedODataModelBuilder( configuration )
{
ModelBuilderFactory = () => new ODataConventionModelBuilder().EnablePascalCase()
};
However, I could never find a way to know which version of the API I was building the model for.
At one point, I thought I had it using OnModelCreating to add
((ODataConventionModelBuilder) builder).OnModelCreating += new PascalCaser().ApplyCase;
to each of the v1 IModelConfiguration classes, but it didn't work once I was building multiple models.
Is there a way to change the JSON property naming based on which API version the model is for?
Using the OData Model Configurations approach described here
first add a class that derives from IModelConfiguration to your project.
Something like this:
public class VersionedModelConfiguration : IModelConfiguration
{
private void ConfigureV1(ODataModelBuilder builder)
{
builder.EntitySet<Product>("Products");
}
private void ConfigureV2(ODataModelBuilder builder)
{
if (builder.GetType().Equals(typeof(ODataConventionModelBuilder)))
{
((ODataConventionModelBuilder)builder).EnableLowerCamelCase();
}
builder.EntitySet<Product>("Products");
}
public void Apply(ODataModelBuilder builder, ApiVersion apiVersion)
{
switch (apiVersion.MajorVersion)
{
case 1:
ConfigureV1(builder);
break;
case 2:
ConfigureV2(builder);
break;
default:
ConfigureV1(builder);
break;
}
}
}
Then in Register method:
// ...
var modelBuilder = new VersionedODataModelBuilder(configuration)
{
ModelBuilderFactory = () => new ODataConventionModelBuilder(),
ModelConfigurations = { new VersionedModelConfiguration() }
};
var models = modelBuilder.GetEdmModels();
// ...
Don't be tempted to leave out the line ModelBuilderFactory = () => new ODataConventionModelBuilder()
/api/v1/$metadata:
<?xml version="1.0" encoding="UTF-8"?>
<edmx:Edmx xmlns:edmx="http://docs.oasis-open.org/odata/ns/edmx" Version="4.0">
<edmx:DataServices>
<Schema xmlns="http://docs.oasis-open.org/odata/ns/edm" Namespace="NS.Models">
<EntityType Name="Product">
<Key>
<PropertyRef Name="Id" />
</Key>
<Property Name="Id" Type="Edm.Int32" Nullable="false" />
<Property Name="Name" Type="Edm.String" />
</EntityType>
</Schema>
<Schema xmlns="http://docs.oasis-open.org/odata/ns/edm" Namespace="Default">
<EntityContainer Name="Container">
<EntitySet Name="Products" EntityType="NS.Models.Product" />
</EntityContainer>
</Schema>
</edmx:DataServices>
</edmx:Edmx>
/api/v2/$metadata:
<?xml version="1.0" encoding="UTF-8"?>
<edmx:Edmx xmlns:edmx="http://docs.oasis-open.org/odata/ns/edmx" Version="4.0">
<edmx:DataServices>
<Schema xmlns="http://docs.oasis-open.org/odata/ns/edm" Namespace="NS.Models">
<EntityType Name="Product">
<Key>
<PropertyRef Name="id" />
</Key>
<Property Name="id" Type="Edm.Int32" Nullable="false" />
<Property Name="name" Type="Edm.String" />
</EntityType>
</Schema>
<Schema xmlns="http://docs.oasis-open.org/odata/ns/edm" Namespace="Default">
<EntityContainer Name="Container">
<EntitySet Name="Products" EntityType="NS.Models.Product" />
</EntityContainer>
</Schema>
</edmx:DataServices>
</edmx:Edmx>
I have the following models:
WeatherForecast:
namespace TravelAssistant.Model.HelperModels.Weather
public class WeatherForecast:EntityBase<int>
{
public IList<DayForecast> ForecastDays { get; set; }
public LocationBase ForecastLocation { get; set; }
public void Init(Location.LocationBase location)
{
this.ForecastLocation = location;
}
protected override void Validate()
{
throw new NotImplementedException();
}
}
LocationBase:
namespace TravelAssistant.Model.HelperModels.Location
public class LocationBase:EntityBase<int>
{
public string WorldRegion { get; set; }
public string City { get; set; }
public string Country { get; set; }
public string Zipcode { get; set; }
public string StreetNameNumber { get; set; }
}
where EntityBase defines an id of type T, and the following .hbm files
WeatherForecast.hbm.xml:
<?xml version="1.0" encoding="utf-8" ?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2"
namespace="TravelAssistant.Model.HelperModels.Weather"
assembly="TravelAssistant.Model">
<class name="WeatherForecast" table="WeatherForecasts" lazy="false" >
<id name="Id" column="WeatherForecastID"
type="int" unsaved-value="0">
<generator class="native" />
</id>
<many-to-one name="ForecastLocation"
cascade="all"
class="LocationBase"
column="LocationID"
not-null="false" />
<bag name="ForecastDays" lazy="false" >
<key column="WeatherForecastID"/>
<one-to-many class="DayForecast"></one-to-many>
</bag>
</class>
</hibernate-mapping>
LocationBase.hbm.xml
<id name="Id" column="LocationID" type="int" unsaved-value="0">
<generator class="native" />
</id>
<property name="City">
<column name="City"
sql-type="varchar(150)" not-null="true" />
</property>
<property name="Country">
<column name="Country"
sql-type="varchar(50)" not-null="true" />
</property>
<property name="Zipcode">
<column name="Zipcode"
sql-type="varchar(50)" not-null="false" />
</property>
<property name="StreetNameNumber">
<column name="StreetNameNumber"
sql-type="varchar(150)" not-null="false" />
</property>
<property name="WorldRegion">
<column name="WorldRegion"
sql-type="varchar(100)" not-null="false" />
</property>
</class>
</hibernate-mapping>
The database model is:
WeatherForecasts : WeatherForecastID, LocationID
Locations:LocationID,City,Country,Zipcode, StreetNameNumber,WorldRegion.
When I run the program I get:
An association from the table WeatherForecasts refers to an unmapped class:
TravelAssistant.Model.HelperModels.Weather.LocationBase
Can someone point me where I got it wrong?
Try using the fully qualified class name for the many-to-one association on the WeatherForecast mapping.
<many-to-one
name="ForecastLocation"
cascade="all"
class="TravelAssistant.Model.HelperModels.Location.LocationBase, TravelAssistant.Model"
column="LocationID"
not-null="false" />
I have the following POCO class:
public class Person : Entity
{
public string FirstName { get; set; }
public string MiddleName1 { get; set; }
public string MiddleName2 { get; set; }
public string LastName { get; set; }
public byte? DayOfBirth { get; set; }
public byte? MonthOfBirth { get; set; }
public Int16? YearOfBirth { get; set; }
public string MobileNumber { get; set; }
}
public abstract class Entity
{
public int Id { get; set; }
}
Here is the corresponding edmx xml:
<edmx:Edmx Version="2.0" xmlns:edmx="http://schemas.microsoft.com/ado/2008/10/edmx">
<!-- EF Runtime content -->
<edmx:Runtime>
<!-- SSDL content -->
<edmx:StorageModels>
<Schema Namespace="EntityFramework.Store" Alias="Self" Provider="System.Data.SqlClient" ProviderManifestToken="2008" xmlns:store="http://schemas.microsoft.com/ado/2007/12/edm/EntityStoreSchemaGenerator" xmlns="http://schemas.microsoft.com/ado/2009/02/edm/ssdl">
<EntityContainer Name="EntityFrameworkStoreContainer">
<EntitySet Name="People" EntityType="EntityFramework.Store.People" store:Type="Tables" Schema="dbo" />
</EntityContainer>
<EntityType Name="People">
<Key>
<PropertyRef Name="Id" />
</Key>
<Property Name="Id" Type="int" Nullable="false" />
<Property Name="FirstName" Type="nvarchar" Nullable="false" MaxLength="50" />
<Property Name="MiddleName1" Type="nvarchar" MaxLength="50" />
<Property Name="MiddleName2" Type="nvarchar" MaxLength="50" />
<Property Name="LastName" Type="nvarchar" Nullable="false" MaxLength="50" />
<Property Name="DayOfBirth" Type="tinyint" />
<Property Name="MonthOfBirth" Type="tinyint" />
<Property Name="YearOfBirth" Type="smallint" />
<Property Name="MobileNumber" Type="varchar" MaxLength="20" />
</EntityType>
</Schema>
</edmx:StorageModels>
<!-- CSDL content -->
<edmx:ConceptualModels>
<Schema Namespace="EntityFramework" Alias="Self" xmlns:annotation="http://schemas.microsoft.com/ado/2009/02/edm/annotation" xmlns="http://schemas.microsoft.com/ado/2008/09/edm">
<EntityContainer Name="TheCleavesEntities" annotation:LazyLoadingEnabled="true">
<EntitySet Name="People" EntityType="EntityFramework.Person" />
</EntityContainer>
<EntityType Name="Person">
<Key>
<PropertyRef Name="Id" />
</Key>
<Property Name="Id" Type="Int32" Nullable="false" />
<Property Name="FirstName" Type="String" Nullable="false" MaxLength="50" Unicode="true" FixedLength="false" />
<Property Name="MiddleName1" Type="String" MaxLength="50" Unicode="true" FixedLength="false" />
<Property Name="MiddleName2" Type="String" MaxLength="50" Unicode="true" FixedLength="false" />
<Property Name="LastName" Type="String" Nullable="false" MaxLength="50" Unicode="true" FixedLength="false" />
<Property Name="DayOfBirth" Type="Byte" Nullable="true" />
<Property Name="MonthOfBirth" Type="Byte" Nullable="true" />
<Property Name="YearOfBirth" Type="Int16" Nullable="true" />
<Property Name="MobileNumber" Type="String" MaxLength="20" Unicode="false" FixedLength="false" />
</EntityType>
</Schema>
</edmx:ConceptualModels>
<!-- C-S mapping content -->
<edmx:Mappings>
<Mapping Space="C-S" xmlns="http://schemas.microsoft.com/ado/2008/09/mapping/cs">
<EntityContainerMapping StorageEntityContainer="EntityFrameworkStoreContainer" CdmEntityContainer="TheCleavesEntities">
<EntitySetMapping Name="People"><EntityTypeMapping TypeName="EntityFramework.Person"><MappingFragment StoreEntitySet="People">
<ScalarProperty Name="Id" ColumnName="Id" />
<ScalarProperty Name="FirstName" ColumnName="FirstName" />
<ScalarProperty Name="MiddleName1" ColumnName="MiddleName1" />
<ScalarProperty Name="MiddleName2" ColumnName="MiddleName2" />
<ScalarProperty Name="LastName" ColumnName="LastName" />
<ScalarProperty Name="DayOfBirth" ColumnName="DayOfBirth" />
<ScalarProperty Name="MonthOfBirth" ColumnName="MonthOfBirth" />
<ScalarProperty Name="YearOfBirth" ColumnName="YearOfBirth" />
<ScalarProperty Name="MobileNumber" ColumnName="MobileNumber" />
</MappingFragment></EntityTypeMapping></EntitySetMapping>
</EntityContainerMapping>
</Mapping>
</edmx:Mappings>
</edmx:Runtime>
<!-- EF Designer content (DO NOT EDIT MANUALLY BELOW HERE) -->
<Designer xmlns="http://schemas.microsoft.com/ado/2008/10/edmx">
<Connection>
<DesignerInfoPropertySet>
<DesignerProperty Name="MetadataArtifactProcessing" Value="EmbedInOutputAssembly" />
</DesignerInfoPropertySet>
</Connection>
<Options>
<DesignerInfoPropertySet>
<DesignerProperty Name="ValidateOnBuild" Value="true" />
<DesignerProperty Name="EnablePluralization" Value="True" />
<DesignerProperty Name="IncludeForeignKeysInModel" Value="False" />
</DesignerInfoPropertySet>
</Options>
<!-- Diagram content (shape and connector positions) -->
<Diagrams>
<Diagram Name="TheCleaves">
<EntityTypeShape EntityType="EntityFramework.Person" Width="1.5" PointX="0.75" PointY="0.75" Height="2.7494921874999996" IsExpanded="true" />
</Diagram>
</Diagrams>
</Designer>
</edmx:Edmx>
When I load up an entity (there's only one row in the database at present) with the code:
IEnumerable<Person> people = context.People.Where(x => true);
I'm finding that the Id property of the returned Person object is 0 (it's 1 in the database). Anyone have any idea why it appears not to be set?
Thanks
Ok it is not problem with inheritance as I initially thought - the inheritance will work. This is most probably combination of two problems:
Id is generated in the database but EF doesn't know about them. Both SSDL and CSDL part of EDMX should define Id with StoreGeneratedPattern.Identity. This will force EF to reload Id when entity is inserted.
I believe you are using same context instance for saving entity and calling the query. Now you meet identity map pattern. Despite the data retrieved from the query EF will use instance internally stored in its per-context cache. Because of the first problem the cached instance has Id with default int value = 0.
I would like to include the value of a scalar function as a read-only property of an entity, since that I could include that value in a filter, is such a thing possible?
The solution is to add a DefiningQuery like in the following sample:
<!-- SSDL content -->
<EntitySet Name="Emp" EntityType="TestModel.Store.Emp" >
<DefiningQuery>
SELECT EMPNO, ENAME, JOB, MGR, HIREDATE, SAL, COMM, DEPTNO, dbo.MyFunc(DEPTNO) AS DNAME FROM EMP
</DefiningQuery>
</EntitySet>
<EntityType Name="Emp">
<Key>
<PropertyRef Name="EMPNO" />
</Key>
<Property Name="EMPNO" Type="int" Nullable="false" />
...
<Property Name="DNAME" Type="varchar" MaxLength ="20" />
</EntityType>
...
<!-- CSDL content -->
...
<EntityType Name="Emp">
<Key>
<PropertyRef Name="EMPNO" />
</Key>
<Property Name="EMPNO" Type="Int32" Nullable="false" />
...
<Property Name="DNAME" Type="String" MaxLength="20" Unicode="false" FixedLength="false" />
</EntityType>
<!-- C-S mapping content -->
...
<ScalarProperty Name="EMPNO" ColumnName="EMPNO" />
...
<ScalarProperty Name="DNAME" ColumnName="DNAME" />
...
The usage example:
using(TestEntities4 db = new TestEntities4()) {
var q = from e in db.Emp where e.DNAME == "RESEARCH" select e;
}
In EF4 you can define a partial class of the same name as your Entity Framework class in the same namespace and add read-only properties to that. I've just done that to expose the description of a connected Entity object as a read-only property of the original object.
namespace same.as.the.data.model
{
public partial class Order
{
public string CustomerName
{
get { return Customer.Name; }
}
}
}