On the client side of a REST query, my Java client, using Jackson (om.fasterxml.jackson.dataformat:jackson-dataformat-xml:2.11.0) makes the following call:
XmlMapper xmlMapper = new XmlMapper();
xmlMapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);
ByteArrayOutputStream streamTemplate = new ByteArrayOutputStream();
xmlMapper.writeValue(streamTemplate, template);
streamTemplate.flush();
The class it is serializing has:
#JacksonXmlProperty(localName = "Datasources")
private List<DataSource> datasources;
And Datasource has several properties, none named Datasources.
But the XML created has:
SqlServer
SQL
System.Data.SqlClient
Data Source=mssql.windward.net;Initial Catalog=Northwind;User ID=demo;Password=demo
Why the two Datasources nodes? On the server side which is a C# ASP.NET WebApi2 app, it is not deserializing the Datasource part, and I assume that's due to the embedded Datasources node.
How do I avoid this?
Trns out the answer is simple. Annotate as follows:
#JacksonXmlElementWrapper(localName = "Datasources")
#JacksonXmlProperty(localName = "Datasource")
private List<DataSource> datasources;
Related
I need to build a Web API from ASP.NET Core without Entity Framework. It's an existing database that has some custom stored procedures and we do not want to use EF.
I searched this topic and can't find anything about it, is this even possible?
This is possible.
The first problem you will run into is getting the database connection string. You will want to import the configuration to do so. In a controller, it might look like this:
private readonly IConfiguration _configuration;
public WeatherForecastController(ILogger<WeatherForecastController> logger, IConfiguration configuration)
{
_logger = logger;
_configuration = configuration;
}
Add using System.Data and using System.Data.SqlClient (you'll need NuGet for SqlClient) as well as using Microsoft.Extensions.Configuration. With access to the database, you are writing code "old style", for example:
[HttpGet]
[Route("[controller]/movies")]
public IEnumerable<Movie> GetMovies()
{
List<Movie> movies = new List<Movie>();
string connString = ConfigurationExtensions.GetConnectionString(_configuration, "RazorPagesMovieContext");
SqlConnection conn = new SqlConnection(connString);
conn.Open();
SqlDataAdapter sda = new SqlDataAdapter("SELECT * FROM Movie", conn);
DataSet ds = new DataSet();
sda.Fill(ds);
DataTable dt = ds.Tables[0];
sda.Dispose();
foreach (DataRow dr in dt.Rows)
{
Movie m = new Movie
{
ID = (int)dr["ID"],
Title = dr["Title"].ToString(),
ReleaseDate = (DateTime)dr["ReleaseDate"],
Genre = dr["Genre"].ToString(),
Price = (decimal)dr["Price"],
Rating = dr["Rating"].ToString()
};
movies.Add(m);
}
conn.Close();
return movies.ToArray();
}
The connection string name is in appsettings.json.
"ConnectionStrings": {
"RazorPagesMovieContext": "Server=localhost;Database=Movies;Trusted_Connection=True;MultipleActiveResultSets=true"
}
Yes it is possible. Just implement the API by yourself. Or here is also sample for the identity scaffold, without EF.
https://markjohnson.io/articles/asp-net-core-identity-without-entity-framework/
Just used Dapper as our ORM in a project rather than EF.
https://dapper-tutorial.net/
It is similar to ADO.Net, but it has some additionally features that we leveraged and it was really clean to implement.
I realize this is an old question, but it came up in a search I ran so I figured I'd add to the answers given.
First, if the custom stored procedures are your concern, you can still run them using Entity Framework's .FromSql method (see here for reference: https://www.entityframeworktutorial.net/efcore/working-with-stored-procedure-in-ef-core.aspx)
The relevant info is found at the top of the page:
EF Core provides the following methods to execute a stored procedure:
1. DbSet<TEntity>.FromSql(<sqlcommand>)
2. DbContext.Database.ExecuteSqlCommand(<sqlcommand>)
If you are avoiding Entity Framework for other reasons, it's definitely possible to use any database connection method you want in ASP.NET Core. Just implement your database connection methods using whatever library is relevant to your database and set up your controller to return the data in whatever format you want. Most, if not all, of Microsoft's examples return Entity Framework entities, but you can easily return any data format you want.
As an example, this controller method returns a MemoryStream object after running a query against an MS SQL server (note, in most cases where you want data returned it's my understanding that it should be a "GET" method, not "POST" as is done here, but I needed to send and use information in the HttpPost body)
[HttpPost]
[Route("Query")]
public ActionResult<Stream> Query([FromBody]SqlDto content)
{
return Ok(_msSqlGenericService.Query(content.SqlCommand, content.SqlParameters));
}
Instead of a MemoryStream, you could return a generic DataTable or a List of any custom class you want. Note that you'll also need to determine how you are going to serialize/deserialize your data.
I am trying to enable web api to support both JSON and XML as return type. While serializing complex datattype to XML I got circular reference errors, so I decorated my main class with DataContract(IsReference = true), now XML serialization is working and json serialization not working.
thanks in advance.
PS: i am able to serialize simple dto classes to both xml and json, but for complex datatypes the problem is coming.
Set the followings in App_Start/WebApiConfig.cs
config.Formatters.JsonFormatter.SupportedMediaTypes.Add(new MediaTypeHeaderValue("text/html"));
GlobalConfiguration.Configuration.Formatters.Clear();
GlobalConfiguration.Configuration.Formatters.Add(new JsonMediaTypeFormatter());
GlobalConfiguration.Configuration.Formatters.Add(new XmlMediaTypeFormatter());
Set application/json or application/xml to Accept header in request-side.
Result will be decided based on result type formatter. Hence I wrote 2 different methods for each return type. To solve, circular reference errors while generating xml i followed below steps.
Serialize class result
Deserialize class result.
Write it to xml.
var json = JsonConvert.SerializeObject(result);
var rO = JsonConvert.DeserializeObject<TClass>(json);
return Ok(ReturnAsXml(rO), Configuration.Formatters.XmlFormatter);
protected virtual XElement ReturnAsXml<T>(T data)
{
Type t = data.GetType();
DataContractSerializer serializer = new DataContractSerializer(t);//, extraTypes);
StringWriter sw = new StringWriter();
XmlTextWriter xw = new XmlTextWriter(sw);
serializer.WriteObject(xw, data);
var o = XElement.Parse(sw.ToString());
return o;
}
I get the following error when I try to access Lazy loaded #oneToMany relationship attribute in a method present in Entity.
Error:
Caused by: Exception [EclipseLink-7097] (Eclipse Persistence Services - 2.6.3.v20160428-59c81c5): org.eclipse.persistence.exceptions.ValidationException
Exception Description: Operation not supported: [instantiateForUnitOfWorkValueHolder].
at org.eclipse.persistence.exceptions.ValidationException.operationNotSupported(ValidationException.java:1496)
at org.eclipse.persistence.internal.indirection.ProtectedValueHolder.instantiateForUnitOfWorkValueHolder(ProtectedValueHolder.java:61)
at org.eclipse.persistence.internal.indirection.UnitOfWorkValueHolder.instantiateImpl(UnitOfWorkValueHolder.java:160)
at org.eclipse.persistence.internal.indirection.UnitOfWorkValueHolder.instantiate(UnitOfWorkValueHolder.java:234)
at org.eclipse.persistence.internal.indirection.DatabaseValueHolder.getValue(DatabaseValueHolder.java:89)
at org.eclipse.persistence.indirection.IndirectList.buildDelegate(IndirectList.java:271)
at org.eclipse.persistence.indirection.IndirectList.getDelegate(IndirectList.java:455)
at org.eclipse.persistence.indirection.IndirectList$1.<init>(IndirectList.java:597)
at org.eclipse.persistence.indirection.IndirectList.listIterator(IndirectList.java:596)
at org.eclipse.persistence.indirection.IndirectList.iterator(IndirectList.java:555)
at com.order.modelGroupBase.getStatus(GroupBase.java:205)
Entity:
#Entity
#Table(name="GROUP")
#Customizer(GroupBaseCustomizer.class)
#Inheritance(strategy=InheritanceType.SINGLE_TABLE)
#ClassExtractor(GroupClassExtractor.class)
#InstantiationCopyPolicy
#Cacheable
#Cache( alwaysRefresh=true,
refreshOnlyIfNewer=true,
expiry=300000,
coordinationType = CacheCoordinationType.SEND_NEW_OBJECTS_WITH_CHANGES)
public class GroupBase {
#OneToMany(cascade=CascadeType.ALL, mappedBy="groupBase", targetEntity=Groups.class)
#PrivateOwned
private List groups = new ArrayList<>();
public OrderGroupStatus getStatus() {
//error is at this line when I try to iterate..
for (Iterator itr = getGroups().iterator(); itr.hasNext();) {
Groups relationship = (Groups) itr.next();
//some operation..
}
}
}
I've looked upon in eclipselink forum but they don't address this issue explicitly and some links say it's related to weaving.But in my application I haven't enabled weaving at all and I also don't intend to do.
This code works fine on Eclipselink 2.3.2 without JPA. Now I'm using Eclipselink 2.6.3 with JPA 2.0 and IBM Websphere 8.5.5.8.
Note: This issue occurs randomly not every time and also when ever a new object is created is what I have observed.
After many hit and trails and correcting my lazy loading settings, I've found out that using different fetchType on same entity in other entities can cause this issue in SHARED_CACHE mode.
#OneToMany(cascade=CascadeType.ALL, mappedBy="groupBase", targetEntity=Groups.class)
#PrivateOwned
private List groups = new ArrayList<>();
By default #OneToMany is lazily fetched but in another entity for the same entity 'Groups' I had configured fetchType as EAGER.
When I made both fetch types to LAZY this error stopped occurring.
I am having trouble understanding how to use the DataContractSerializer in WCF REST
I am using a channel factory like so:
Uri uri = new Uri("http://localhost:50000/people");
WebChannelFactory<IPersonService> chFactory = new WebChannelFactory<IPersonService>(uri);
IPersonService iPerson = chFactory.CreateChannel();
than can call the channel methods directly from the channel like this
List<Person> allPeople = new List<Person>();
allPeople = iPerson.getAll();
This has what I got so far as how to use the DataContractSerializer so I can output the response
MemoryStream stream = new MemoryStream();
<--------------- how to i read iPerson.getAll() into stream? --------->
XmlDictionaryReader reader = XmlDictionaryReader.CreateTextReader(stream, new XmlDictionaryReaderQuotas());
DataContractSerializer dcs = new DataContractSerializer(typeof(Person));
List<Person> allpeople2 = (List<Person>)dcs.ReadObject(reader, true);
reader.Close();
stream.Close();
I am not exactly sure how to put these pieces together to make it all work.
I think you made it a bit complicated...
i would start a new project following this introduction page: http://msdn.microsoft.com/en-us/magazine/dd315413.aspx
When you configure the serialization issues on web.config, you just have to declare attributes on your interfaces / classes and you don't have to write a single line of serializing / deserializing code for your objects (unless you need to get customized serialization which in your case not needed)
by the url provided "http://localhost:50000/people", i assumed you are looking for a RESTful service, so just in case you need some more advanced features you can look at that as well: https://github.com/mikeobrien/WcfRestContrib
I decided to open a new question about this matter, maybe expanding this question, not having found a precise answer about the issue anywhere on the Internet.
I want to use protobuf-net to serialize/deserialize messages exchanged between my WCF client and service. The service is self-hosted in a Windows Service. Both client and service are configured programmatically, using a custom binding very similar to wsHttpBinding. Service reference code is generated using "Add Service Reference" option in Visual Studio. The ORM used on the WCF service is EntityFramework 4 and it's code is generated using EF 4.x POCO Generator. More info about my service configuration can be found in a question I started here (that's where I described that my current serializer is DataContractSerialzizer).
I have only tested protobuf-net with one service operation which returns a list of custom DTOs.
Here is the operation (be advised that I just did a copy-paste of my code to here, there might be some fields named in my domestic language, not English):
public static List<OsobaView> GetListOsobas()
{
Database DB = new Database(); // EF object context
var retValue = DB.Baza.Osoba
.Select(x => new OsobaView
{
ID = x.ID,
Prezime = x.Prezime,
Ime = x.Ime,
Adresa = x.Adresa,
DatumRodjenja = x.DatumRodjenja,
JMBG = x.JMBG
});
return retValue.ToList();
}
Here is the definition of OsobaView class:
[ProtoContract]
public class OsobaView
{
[ProtoMember(1)]
public int ID;
[ProtoMember(2)]
public string Prezime;
[ProtoMember(3)]
public string Ime;
[ProtoMember(4)]
public string Adresa;
[ProtoMember(5)]
public DateTime DatumRodjenja;
[ProtoMember(6)]
public string JMBG;
}
As I am using "Add Service Reference" to generate the reference code, I had to use one of the two work-arounds in order to have my client recognize ProtoContracts and members:
using a shared assembly for DTOs (which is not an ideal solution in my case except for custom DTOs, due to the fact that I pass EF-generated POCOs to the client)
using ProtoPartialMember approach
I used both of them and I used both v1 and v2 of protobuf-net, all solutions yielded similar results which led me to believe my client is not deserializing at all. Read on.
Let's consider cases where I used the ProtoPartialMember approach. At first I used v2. I love the way ProtoOperationBehavior can be used. Here is the service operation to be invoked:
[ProtoBuf.ServiceModel.ProtoBehavior]
public List<OsobaView> GetListOsobas()
{
return OsobaQueries.GetListOsobas();
}
Here is how I replaced DataContractSerializerOperationBehavior with ProtoOperationBehavior for the needed service operation on client side:
OperationDescription op = Service.Proxy.Endpoint.Contract.Operations.Find("GetListOsobas");
if (op != null)
{
DataContractSerializerOperationBehavior dcsBehavior = op.Behaviors.Find<DataContractSerializerOperationBehavior>();
if (dcsBehavior != null)
op.Behaviors.Remove(dcsBehavior);
op.Behaviors.Add(new ProtoBuf.ServiceModel.ProtoOperationBehavior(op));
}
And of course, here is the above mentioned work-around implementation for DTO:
[ProtoPartialMember(1, "ID")]
[ProtoPartialMember(2, "Prezime")]
[ProtoPartialMember(3, "Ime")]
[ProtoPartialMember(4, "Adresa")]
[ProtoPartialMember(5, "DatumRodjenja")]
[ProtoPartialMember(6, "JMBG")]
[ProtoContract]
public partial class OsobaView
{
}
Now when I call this service operation from my client, I get null. But Fiddler disagrees. It clearly says, in response header:
Content-Length: 1301963
Content-Type: application/soap+xml; charset=utf-8
...and in the message body:
<s:Body>
<GetListOsobasResponse xmlns="http://tempuri.org/">
<proto>CkMIpHES .../* REALLY LONG RESPONSE */... IyMDAxOA==</proto>
</GetListOsobasResponse>
</s:Body>
Then I thought, let's try with v1. On the service side, I haven't changed much. I just removed the reference to v2 .DLL and replaced it with a reference to v1 .DLL. On the client side, I had to remove the code to add ProtoOperationBehavior to my service operation behaviors and added the following line instead:
Service.Proxy.Endpoint.Behaviors
.Add(new ProtoBuf.ServiceModel.ProtoEndpointBehavior());
I fired it up, invoked the operation, and this time the result is not null. This time it is a list of blank fields. Again, Fiddler couldn't agree because it again said the same what it said before. The same content length and the same message body.
What's going on here?
P.S. If it's worth anything, here is the WCF configuration:
CustomBinding customBinding = new CustomBinding();
customBinding.CloseTimeout = TimeSpan.FromMinutes(10);
customBinding.OpenTimeout = TimeSpan.FromMinutes(10);
customBinding.ReceiveTimeout = TimeSpan.FromMinutes(10);
customBinding.SendTimeout = TimeSpan.FromMinutes(10);
HttpsTransportBindingElement httpsBindingElement = new HttpsTransportBindingElement();
httpsBindingElement.AllowCookies = false;
httpsBindingElement.BypassProxyOnLocal = false;
httpsBindingElement.HostNameComparisonMode = HostNameComparisonMode.StrongWildcard;
httpsBindingElement.MaxBufferPoolSize = 20480000;
httpsBindingElement.MaxBufferSize = 20480000;
httpsBindingElement.MaxReceivedMessageSize = 20480000;
httpsBindingElement.RequireClientCertificate = true;
httpsBindingElement.UseDefaultWebProxy = true;
TransportSecurityBindingElement transportSecurityElement = new TransportSecurityBindingElement();
transportSecurityElement.EndpointSupportingTokenParameters.SignedEncrypted.Add(new UserNameSecurityTokenParameters());
transportSecurityElement.EndpointSupportingTokenParameters.SetKeyDerivation(false);
TransactionFlowBindingElement transactionFlowElement = new TransactionFlowBindingElement();
TextMessageEncodingBindingElement textMessageEncoding = new TextMessageEncodingBindingElement();
textMessageEncoding.MaxReadPoolSize = 20480000;
textMessageEncoding.MaxWritePoolSize = 20480000;
textMessageEncoding.ReaderQuotas = XmlDictionaryReaderQuotas.Max;
ReliableSessionBindingElement reliableSessionElement = new ReliableSessionBindingElement();
reliableSessionElement.ReliableMessagingVersion = ReliableMessagingVersion.WSReliableMessagingFebruary2005;
customBinding.Elements.Add(transportSecurityElement);
customBinding.Elements.Add(transactionFlowElement);
customBinding.Elements.Add(textMessageEncoding);
customBinding.Elements.Add(reliableSessionElement);
customBinding.Elements.Add(httpsBindingElement);
EndpointAddress endpoint = new EndpointAddress(new Uri(ServiceAddress));
Service.Proxy = new BazaService.BazaClient(customBinding, endpoint);
Service.Proxy.ClientCredentials.ClientCertificate.SetCertificate(StoreLocation.CurrentUser, StoreName.My, X509FindType.FindBySubjectName, CertificateSubject);
CustomBehavior behavior = Service.Proxy.Endpoint.Behaviors.Find<CustomBehavior>();
if (behavior == null)
{
Service.Proxy.Endpoint.Behaviors.Add(new CustomBehavior()); // message inspector
}
Service.Proxy.Endpoint.Contract.Behaviors.Add(new CyclicReferencesAwareContractBehavior(true));
Service.Proxy.Endpoint.Behaviors.Add(new ProtoBuf.ServiceModel.ProtoEndpointBehavior());
/* code used for protobuf-net v2
OperationDescription op = Service.Proxy.Endpoint.Contract.Operations.Find("GetListOsobas");
if (op != null)
{
DataContractSerializerOperationBehavior dcsBehavior = op.Behaviors.Find<DataContractSerializerOperationBehavior>();
if (dcsBehavior != null)
op.Behaviors.Remove(dcsBehavior);
op.Behaviors.Add(new ProtoBuf.ServiceModel.ProtoOperationBehavior(op));
} */
Service.Proxy.ClientCredentials.UserName.UserName = LogOn.UserName;
Service.Proxy.ClientCredentials.UserName.Password = LogOn.Password;
Service.Proxy.Open();
EDIT
To provide even more information, I have read what's written there but it didn't help. I have deleted the service reference generated by Visual Studio and created my own, sharing the whole service contract, but nothing has changed.
After concentrating a bit better, I decided to restart the solution from scratch. I created one class library for the EDMX with it's POCOs, one for ServiceContract and DataContracts and one for the actual WCF service implementation. Then I shared those two libraries containing ServiceContract and DataContracts, and POCOs with the WCF client and tried again, which yielded the same results as before. After trying some other operations which didn't use protobuf-net for serialization, turned out they behaved the same as the first one, resulting in empty fields (!).
The thing was that, I screwed my WCF client's .datasource files while refactoring after I decided to use the assembly sharing technique. So this was a typical PEBKAC, it of course works fine when done properly. Great work with protobuf-net, Marc Gravell!