Issue of WCF: is it my own mistake? - wcf

I defined the method below:
[OperationContract]
[FaultContract(typeof(UserFriendlyError))]
bool MakeOutInvoice(Dictionary<string, string> pk, string usergid);
In my website, I added the service reference, the vs 2010 generated these code below:
[System.CodeDom.Compiler.GeneratedCodeAttribute("System.Xml", "4.0.30319.17929")]
[System.SerializableAttribute()]
[System.Diagnostics.DebuggerStepThroughAttribute()]
[System.ComponentModel.DesignerCategoryAttribute("code")]
[System.Xml.Serialization.XmlTypeAttribute(AnonymousType=true, Namespace="http://schemas.microsoft.com/2003/10/Serialization/Arrays")]
public partial class ArrayOfKeyValueOfstringstringKeyValueOfstringstring : object, System.ComponentModel.INotifyPropertyChanged {
private string keyField;
private string valueField;
/// <remarks/>
[System.Xml.Serialization.XmlElementAttribute(IsNullable=true, Order=0)]
public string Key {
get {
return this.keyField;
}
set {
this.keyField = value;
this.RaisePropertyChanged("Key");
}
}
/// <remarks/>
[System.Xml.Serialization.XmlElementAttribute(IsNullable=true, Order=1)]
public string Value {
get {
return this.valueField;
}
set {
this.valueField = value;
this.RaisePropertyChanged("Value");
}
}
public event System.ComponentModel.PropertyChangedEventHandler PropertyChanged;
protected void RaisePropertyChanged(string propertyName) {
System.ComponentModel.PropertyChangedEventHandler propertyChanged = this.PropertyChanged;
if ((propertyChanged != null)) {
propertyChanged(this, new System.ComponentModel.PropertyChangedEventArgs(propertyName));
}
}
}
public bool MakeOutInvoice(ArrayOfKeyValueOfstringstringKeyValueOfstringstring[] pk, string usergid) {
LPM.Web.LPTMSFinancialServiceRef.MakeOutInvoiceRequest inValue = new LPM.Web.LPTMSFinancialServiceRef.MakeOutInvoiceRequest();
inValue.pk = pk;
inValue.usergid = usergid;
LPM.Web.LPTMSFinancialServiceRef.MakeOutInvoiceResponse retVal = ((LPM.Web.LPTMSFinancialServiceRef.ILPTMSFinancialService)(this)).MakeOutInvoice(inValue);
return retVal.MakeOutInvoiceResult;
}
In my opinion, it should generate the same parameter as the WCF.
Why does it replace generating the new type parameter with the Dictionary?
Thanks for your help!

Related

Consume WSDL proxy class using c#

I have generated a proxy class but I don't know how to consume it.
I also instantiated the class and accessed the class but I don't know how to get the response
My Access code
Controller class to access the proxy :
ProjectByElementsQuerySelectionByElements query = new
ProjectByElementsQuerySelectionByElements();
ProjectByElementsQueryMessage_sync sync = new ProjectByElementsQueryMessage_sync{
ProjectSelectionByElements = query
};
Proxy Soap class :
This is the automatically generated from wsdl file and is a proxy class that I want to consume.
public partial class ProjectStatusByQueryResponse {
private ProjectLifeCycleStatusCode projectLifeCycleStatusCodeField;
private UPTODATEOUTOFDATE_UpToDatenessStatusCode schedulingUpToDatenessStatusCodeField;
private StartingStatusCode startingStatusCodeField;
private ProjectTaskLifeCycleStatusCode taskLifeCycleStatusCodeField;
private BlockingStatusCode taskBlockingStatusCodeField;
/// <remarks/>
[System.Xml.Serialization.XmlElementAttribute(Form=System.Xml.Schema.XmlSchemaForm.Unqualified)]
public ProjectLifeCycleStatusCode ProjectLifeCycleStatusCode {
get {
return this.projectLifeCycleStatusCodeField;
}
set {
this.projectLifeCycleStatusCodeField = value;
}
}
public partial class ProjectByElementsQuerySelectionByElements {
private ProjectSelectionByResponsibleUnitID[] selectionByResponsibleUnitIDField;
private ProjectSelectionByProjectId[] selectionByProjectIDField;
private ProjectSelectionByProjectName[] selectionByProjectNameField;
private ProjectSelectionByProjectType[] selectionByProjectTypeField;
private ProjectSelectionByCustomerID[] selectionByCustomerIDField;
private ProjectSelectionByRequestingUnitID[] selectionByRequestingUnitIDField;
private bool selectionByBillableIndicatorField;
private bool selectionByBillableIndicatorFieldSpecified;
private ProjectSelectionByInterCompanyIndicator[] selectionByIntercompanyIndicatorField;
private ProjectSelectionByResponsibleEmployeeID[] selectionByResponsibleEmployeeIDField;
/// <remarks/>
[System.Xml.Serialization.XmlElementAttribute("SelectionByResponsibleUnitID", Form=System.Xml.Schema.XmlSchemaForm.Unqualified)]
public ProjectSelectionByResponsibleUnitID[] SelectionByResponsibleUnitID {
get {
return this.selectionByResponsibleUnitIDField;
}
set {
this.selectionByResponsibleUnitIDField = value;
}
}
/// <remarks/>
[System.Xml.Serialization.XmlElementAttribute("SelectionByProjectID", Form=System.Xml.Schema.XmlSchemaForm.Unqualified)]
public ProjectSelectionByProjectId[] SelectionByProjectID {
get {
return this.selectionByProjectIDField;
}
set {
this.selectionByProjectIDField = value;
}
}
/// <remarks/>
[System.Xml.Serialization.XmlElementAttribute("SelectionByProjectName", Form=System.Xml.Schema.XmlSchemaForm.Unqualified)]
public ProjectSelectionByProjectName[] SelectionByProjectName {
get {
return this.selectionByProjectNameField;
}
set {
this.selectionByProjectNameField = value;
}
}
/// <remarks/>
[System.Xml.Serialization.XmlElementAttribute("SelectionByProjectType", Form=System.Xml.Schema.XmlSchemaForm.Unqualified)]
public ProjectSelectionByProjectType[] SelectionByProjectType {
get {
return this.selectionByProjectTypeField;
}
set {
this.selectionByProjectTypeField = value;
}
}
/// <remarks/>
[System.Xml.Serialization.XmlElementAttribute("SelectionByCustomerID", Form=System.Xml.Schema.XmlSchemaForm.Unqualified)]
public ProjectSelectionByCustomerID[] SelectionByCustomerID {
get {
return this.selectionByCustomerIDField;
}
set {
this.selectionByCustomerIDField = value;
}
}
/// <remarks/>
[System.Xml.Serialization.XmlElementAttribute("SelectionByRequestingUnitID", Form=System.Xml.Schema.XmlSchemaForm.Unqualified)]
public ProjectSelectionByRequestingUnitID[] SelectionByRequestingUnitID {
get {
return this.selectionByRequestingUnitIDField;
}
set {
this.selectionByRequestingUnitIDField = value;
}
}
/// <remarks/>
[System.Xml.Serialization.XmlElementAttribute(Form=System.Xml.Schema.XmlSchemaForm.Unqualified)]
public bool SelectionByBillableIndicator {
get {
return this.selectionByBillableIndicatorField;
}
set {
this.selectionByBillableIndicatorField = value;
}
}
/// <remarks/>
[System.Xml.Serialization.XmlIgnoreAttribute()]
public bool SelectionByBillableIndicatorSpecified {
get {
return this.selectionByBillableIndicatorFieldSpecified;
}
set {
this.selectionByBillableIndicatorFieldSpecified = value;
}
}
/// <remarks/>
[System.Xml.Serialization.XmlElementAttribute("SelectionByIntercompanyIndicator", Form=System.Xml.Schema.XmlSchemaForm.Unqualified)]
public ProjectSelectionByInterCompanyIndicator[] SelectionByIntercompanyIndicator {
get {
return this.selectionByIntercompanyIndicatorField;
}
set {
this.selectionByIntercompanyIndicatorField = value;
}
}
/// <remarks/>
[System.Xml.Serialization.XmlElementAttribute("SelectionByResponsibleEmployeeID", Form=System.Xml.Schema.XmlSchemaForm.Unqualified)]
public ProjectSelectionByResponsibleEmployeeID[] SelectionByResponsibleEmployeeID {
get {
return this.selectionByResponsibleEmployeeIDField;
}
set {
this.selectionByResponsibleEmployeeIDField = value;
}
}
}
The proxy class file is added to the project to instantiate the proxy class to call the wcf service. If you don’t understand how to call the wcf service in this way, you can refer to ServiceModel Metadata Utility Tool.
In addition, you can also call the wcf service by adding a service reference. This is the simplest way. Right-click the project and select addservicereference in add, and then add the service reference.

How to use operation inside proxy class as sync only?

I followed article to include WCF service into my ASP.NET core application.
It seems that Generated Reference.cs file automatically included Async operations. Is it possible to make these operation sync only? Either in Reference.cs using them on client side.?
Generated Reference.cs file looks like follows-
//------------------------------------------------------------------------------
// <auto-generated>
// This code was generated by a tool.
// //
// Changes to this file may cause incorrect behavior and will be lost if
// the code is regenerated.
// </auto-generated>
//------------------------------------------------------------------------------
namespace SimpleServiceReference
{
using System.Runtime.Serialization;
[System.Diagnostics.DebuggerStepThroughAttribute()]
[System.CodeDom.Compiler.GeneratedCodeAttribute("dotnet-svcutil", "0.5.0.0")]
[System.Runtime.Serialization.DataContractAttribute(Name="CompositeType", Namespace="http://schemas.datacontract.org/2004/07/SimpleService")]
public partial class CompositeType : object
{
private bool BoolValueField;
private string StringValueField;
[System.Runtime.Serialization.DataMemberAttribute()]
public bool BoolValue
{
get
{
return this.BoolValueField;
}
set
{
this.BoolValueField = value;
}
}
[System.Runtime.Serialization.DataMemberAttribute()]
public string StringValue
{
get
{
return this.StringValueField;
}
set
{
this.StringValueField = value;
}
}
}
[System.CodeDom.Compiler.GeneratedCodeAttribute("dotnet-svcutil", "0.5.0.0")]
[System.ServiceModel.ServiceContractAttribute(ConfigurationName="SimpleServiceReference.ISimpleService")]
public interface ISimpleService
{
[System.ServiceModel.OperationContractAttribute(Action="http://tempuri.org/ISimpleService/GetData", ReplyAction="http://tempuri.org/ISimpleService/GetDataResponse")]
System.Threading.Tasks.Task<string> GetDataAsync(int value);
[System.ServiceModel.OperationContractAttribute(Action="http://tempuri.org/ISimpleService/GetDataUsingDataContract", ReplyAction="http://tempuri.org/ISimpleService/GetDataUsingDataContractResponse")]
System.Threading.Tasks.Task<SimpleServiceReference.CompositeType> GetDataUsingDataContractAsync(SimpleServiceReference.CompositeType composite);
}
[System.CodeDom.Compiler.GeneratedCodeAttribute("dotnet-svcutil", "0.5.0.0")]
public interface ISimpleServiceChannel : SimpleServiceReference.ISimpleService, System.ServiceModel.IClientChannel
{
}
[System.Diagnostics.DebuggerStepThroughAttribute()]
[System.CodeDom.Compiler.GeneratedCodeAttribute("dotnet-svcutil", "0.5.0.0")]
public partial class SimpleServiceClient : System.ServiceModel.ClientBase<SimpleServiceReference.ISimpleService>, SimpleServiceReference.ISimpleService
{
/// <summary>
/// Implement this partial method to configure the service endpoint.
/// </summary>
/// <param name="serviceEndpoint">The endpoint to configure</param>
/// <param name="clientCredentials">The client credentials</param>
static partial void ConfigureEndpoint(System.ServiceModel.Description.ServiceEndpoint serviceEndpoint, System.ServiceModel.Description.ClientCredentials clientCredentials);
public SimpleServiceClient() :
base(SimpleServiceClient.GetDefaultBinding(), SimpleServiceClient.GetDefaultEndpointAddress())
{
this.Endpoint.Name = EndpointConfiguration.BasicHttpBinding_ISimpleService.ToString();
ConfigureEndpoint(this.Endpoint, this.ClientCredentials);
}
public SimpleServiceClient(EndpointConfiguration endpointConfiguration) :
base(SimpleServiceClient.GetBindingForEndpoint(endpointConfiguration), SimpleServiceClient.GetEndpointAddress(endpointConfiguration))
{
this.Endpoint.Name = endpointConfiguration.ToString();
ConfigureEndpoint(this.Endpoint, this.ClientCredentials);
}
public SimpleServiceClient(EndpointConfiguration endpointConfiguration, string remoteAddress) :
base(SimpleServiceClient.GetBindingForEndpoint(endpointConfiguration), new System.ServiceModel.EndpointAddress(remoteAddress))
{
this.Endpoint.Name = endpointConfiguration.ToString();
ConfigureEndpoint(this.Endpoint, this.ClientCredentials);
}
public SimpleServiceClient(EndpointConfiguration endpointConfiguration, System.ServiceModel.EndpointAddress remoteAddress) :
base(SimpleServiceClient.GetBindingForEndpoint(endpointConfiguration), remoteAddress)
{
this.Endpoint.Name = endpointConfiguration.ToString();
ConfigureEndpoint(this.Endpoint, this.ClientCredentials);
}
public SimpleServiceClient(System.ServiceModel.Channels.Binding binding, System.ServiceModel.EndpointAddress remoteAddress) :
base(binding, remoteAddress)
{
}
public System.Threading.Tasks.Task<string> GetDataAsync(int value)
{
return base.Channel.GetDataAsync(value);
}
public System.Threading.Tasks.Task<SimpleServiceReference.CompositeType> GetDataUsingDataContractAsync(SimpleServiceReference.CompositeType composite)
{
return base.Channel.GetDataUsingDataContractAsync(composite);
}
public virtual System.Threading.Tasks.Task OpenAsync()
{
return System.Threading.Tasks.Task.Factory.FromAsync(((System.ServiceModel.ICommunicationObject)(this)).BeginOpen(null, null), new System.Action<System.IAsyncResult>(((System.ServiceModel.ICommunicationObject)(this)).EndOpen));
}
public virtual System.Threading.Tasks.Task CloseAsync()
{
return System.Threading.Tasks.Task.Factory.FromAsync(((System.ServiceModel.ICommunicationObject)(this)).BeginClose(null, null), new System.Action<System.IAsyncResult>(((System.ServiceModel.ICommunicationObject)(this)).EndClose));
}
private static System.ServiceModel.Channels.Binding GetBindingForEndpoint(EndpointConfiguration endpointConfiguration)
{
if ((endpointConfiguration == EndpointConfiguration.BasicHttpBinding_ISimpleService))
{
System.ServiceModel.BasicHttpBinding result = new System.ServiceModel.BasicHttpBinding();
result.MaxBufferSize = int.MaxValue;
result.ReaderQuotas = System.Xml.XmlDictionaryReaderQuotas.Max;
result.MaxReceivedMessageSize = int.MaxValue;
result.AllowCookies = true;
return result;
}
throw new System.InvalidOperationException(string.Format("Could not find endpoint with name \'{0}\'.", endpointConfiguration));
}
private static System.ServiceModel.EndpointAddress GetEndpointAddress(EndpointConfiguration endpointConfiguration)
{
if ((endpointConfiguration == EndpointConfiguration.BasicHttpBinding_ISimpleService))
{
return new System.ServiceModel.EndpointAddress("http://localhost:49945/SimpleService.svc");
}
throw new System.InvalidOperationException(string.Format("Could not find endpoint with name \'{0}\'.", endpointConfiguration));
}
private static System.ServiceModel.Channels.Binding GetDefaultBinding()
{
return SimpleServiceClient.GetBindingForEndpoint(EndpointConfiguration.BasicHttpBinding_ISimpleService);
}
private static System.ServiceModel.EndpointAddress GetDefaultEndpointAddress()
{
return SimpleServiceClient.GetEndpointAddress(EndpointConfiguration.BasicHttpBinding_ISimpleService);
}
public enum EndpointConfiguration
{
BasicHttpBinding_ISimpleService,
}
}
}
You can call async method synchronously and there are several ways to do this:
For Task<T> use:
var result = Task.Run(AsyncMethod).Result;
// if the Task fails, GetResult() will just throw an `AggregateException`
// or use
var result AsyncMethod.GetAwaiter().GetResult();
// if the Task fails, GetResult() will just throw the exception caused directly
For Task (not Task<T>) use:
AsyncMethod.RunSynchronously();

Embedded Neo4j delete node and Lucene legacy indexing - node_auto_indexing out of sync issue

I'm trying to delete node with fields in node_auto_indexing.
When I try to delete node using repository.delete(id).
Right after that I'm trying to get deleted Node by its id and I get following exception:
java.lang.IllegalStateException: This index (Index[__rel_types__,Relationship]) has been marked as deleted in this transaction
at org.neo4j.index.impl.lucene.LuceneTransaction$DeletedTxDataBoth.illegalStateException(LuceneTransaction.java:475)
at org.neo4j.index.impl.lucene.LuceneTransaction$DeletedTxDataBoth.removed(LuceneTransaction.java:470)
at org.neo4j.index.impl.lucene.LuceneTransaction.remove(LuceneTransaction.java:112)
at org.neo4j.index.impl.lucene.LuceneXaConnection.remove(LuceneXaConnection.java:116)
at org.neo4j.index.impl.lucene.LuceneIndex.remove(LuceneIndex.java:215)
at org.springframework.data.neo4j.support.typerepresentation.AbstractIndexBasedTypeRepresentationStrategy.remove(AbstractIndexBasedTypeRepresentationStrategy.java:113)
at org.springframework.data.neo4j.support.typerepresentation.AbstractIndexBasedTypeRepresentationStrategy.preEntityRemoval(AbstractIndexBasedTypeRepresentationStrategy.java:100)
at org.springframework.data.neo4j.support.mapping.EntityRemover.removeRelationship(EntityRemover.java:63)
at org.springframework.data.neo4j.support.mapping.EntityRemover.removeNode(EntityRemover.java:51)
at org.springframework.data.neo4j.support.mapping.EntityRemover.removeNodeEntity(EntityRemover.java:45)
at org.springframework.data.neo4j.support.mapping.EntityRemover.remove(EntityRemover.java:85)
at org.springframework.data.neo4j.support.Neo4jTemplate.delete(Neo4jTemplate.java:267)
at org.springframework.data.neo4j.repository.AbstractGraphRepository.delete(AbstractGraphRepository.java:276)
at org.springframework.data.neo4j.repository.AbstractGraphRepository.delete(AbstractGraphRepository.java:282)
Also, when I'm trying to delete node via Cypher query
#Query("MATCH ()-[r]-(p:Product) WHERE id(p) = {productId} DELETE r, p")
void deleteProduct(#Param("productId") Long productId);
I'm getting another exception after looking this deleted Node by its Id:
java.lang.IllegalStateException: No primary SDN label exists .. (i.e one starting with _)
at org.springframework.data.neo4j.support.typerepresentation.LabelBasedNodeTypeRepresentationStrategy.readAliasFrom(LabelBasedNodeTypeRepresentationStrategy.java:126)
at org.springframework.data.neo4j.support.typerepresentation.LabelBasedNodeTypeRepresentationStrategy.readAliasFrom(LabelBasedNodeTypeRepresentationStrategy.java:39)
at org.springframework.data.neo4j.support.mapping.TRSTypeAliasAccessor.readAliasFrom(TRSTypeAliasAccessor.java:36)
at org.springframework.data.neo4j.support.mapping.TRSTypeAliasAccessor.readAliasFrom(TRSTypeAliasAccessor.java:26)
at org.springframework.data.convert.DefaultTypeMapper.readType(DefaultTypeMapper.java:102)
at org.springframework.data.convert.DefaultTypeMapper.getDefaultedTypeToBeUsed(DefaultTypeMapper.java:165)
at org.springframework.data.convert.DefaultTypeMapper.readType(DefaultTypeMapper.java:142)
at org.springframework.data.neo4j.support.mapping.Neo4jEntityConverterImpl.read(Neo4jEntityConverterImpl.java:78)
at org.springframework.data.neo4j.support.mapping.Neo4jEntityPersister$CachedConverter.read(Neo4jEntityPersister.java:170)
at org.springframework.data.neo4j.support.mapping.Neo4jEntityPersister.createEntityFromState(Neo4jEntityPersister.java:189)
at org.springframework.data.neo4j.support.Neo4jTemplate.createEntityFromState(Neo4jTemplate.java:224)
at org.springframework.data.neo4j.repository.AbstractGraphRepository.createEntity(AbstractGraphRepository.java:62)
at org.springframework.data.neo4j.repository.AbstractGraphRepository.findOne(AbstractGraphRepository.java:127)
at org.springframework.data.neo4j.repository.AbstractGraphRepository.delete(AbstractGraphRepository.java:282)
How to correctly delete node that participates in Lucene Legacy Indexing node_auto_indexing ? How to remove this Node from Lucene index ?
UPDATED:
This is my Neo4jConfig:
#Configuration
#EnableNeo4jRepositories(basePackages = "com.example")
#EnableTransactionManagement
public class Neo4jConfig extends Neo4jConfiguration implements BeanFactoryAware {
#Resource
private Environment environment;
private BeanFactory beanFactory;
public Neo4jConfig() {
setBasePackage("com.example");
}
#Bean(destroyMethod = "shutdown")
public GraphDatabaseService graphDatabaseService() {
GraphDatabaseService graphDb = new GraphDatabaseFactory()
.newEmbeddedDatabaseBuilder("target/example-test-db")
.setConfig(GraphDatabaseSettings.node_keys_indexable, "name,description")
.setConfig(GraphDatabaseSettings.node_auto_indexing, "true")
.newGraphDatabase();
return graphDb;
}
/**
* Hook into the application lifecycle and register listeners that perform
* behaviour across types of entities during this life cycle
*
*/
#Bean
protected ApplicationListener<BeforeSaveEvent<BaseEntity>> beforeSaveEventApplicationListener() {
return new ApplicationListener<BeforeSaveEvent<BaseEntity>>() {
#Override
public void onApplicationEvent(BeforeSaveEvent<BaseEntity> event) {
BaseEntity entity = event.getEntity();
if (entity.getCreateDate() == null) {
entity.setCreateDate(new Date());
} else {
entity.setUpdateDate(new Date());
}
}
};
}
#Override
public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
this.beanFactory = beanFactory;
}
public BeanFactory getBeanFactory() {
return beanFactory;
}
}
Base entity for entities in the project:
public class BaseEntity {
private Date createDate;
private Date updateDate;
public BaseEntity() {
}
public Date getCreateDate() {
return createDate;
}
public void setCreateDate(Date createDate) {
this.createDate = createDate;
}
public Date getUpdateDate() {
return updateDate;
}
public void setUpdateDate(Date updateDate) {
this.updateDate = updateDate;
}
}
and the Vote entity that I tried to delete:
#NodeEntity
public class Vote extends BaseEntity {
private static final String VOTED_ON = "VOTED_ON";
private final static String VOTED_FOR = "VOTED_FOR";
private static final String CREATED_BY = "CREATED_BY";
#GraphId
private Long id;
#RelatedTo(type = VOTED_FOR, direction = Direction.OUTGOING)
private Decision decision;
#RelatedTo(type = VOTED_ON, direction = Direction.OUTGOING)
private Criterion criterion;
#RelatedTo(type = CREATED_BY, direction = Direction.OUTGOING)
private User author;
private double weight;
private String description;
public Vote() {
}
public Vote(Decision decision, Criterion criterion, User author, double weight, String description) {
this.decision = decision;
this.criterion = criterion;
this.author = author;
this.weight = weight;
this.description = description;
}
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public Decision getDecision() {
return decision;
}
public void setDecision(Decision decision) {
this.decision = decision;
}
public Criterion getCriterion() {
return criterion;
}
public void setCriterion(Criterion criterion) {
this.criterion = criterion;
}
public User getAuthor() {
return author;
}
public void setAuthor(User author) {
this.author = author;
}
public double getWeight() {
return weight;
}
public void setWeight(double weight) {
this.weight = weight;
}
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
#Override
public boolean equals(Object o) {
if (this == o)
return true;
if (o == null || getClass() != o.getClass())
return false;
Vote vote = (Vote) o;
if (id == null)
return super.equals(o);
return id.equals(vote.id);
}
#Override
public int hashCode() {
return id != null ? id.hashCode() : super.hashCode();
}
#Override
public String toString() {
return ToStringBuilder.reflectionToString(this, ToStringStyle.SHORT_PREFIX_STYLE);
}
}
Thanks to #MichaelHunger and Neo4j this issue has been fixed in Neo4j 2.2.2 and SDN 3.4.0.M1

using RavenDB with ServiceStack

I read this post by Phillip Haydon about how to use NHibernate/RavenDB with ServiceStack.
I don't see the point about getting the IDocumentStore and open new session every time i need something from the db like this:
public class FooService : ServiceBase<Foo>
{
public IDocumentStore RavenStore{ get; set; }
protected override object Run(ProductFind request)
{
using (var session = RavenStore.OpenSession())
{
// Do Something...
return new FooResponse{/*Object init*/};
}
}
}
Why cant i just use one session per request and when the request is ended, commit the changes or roll them back according to the response status?
If my approach is fine, than how can i implement it?
here is my attempt:
I created this class:
public class RavenSession : IRavenSession
{
#region Data Members
private readonly IDocumentStore _store;
private IDocumentSession _innerSession;
#endregion
#region Properties
public IDocumentSession InnerSession
{
get { return _innerSession ?? (_innerSession = _store.OpenSession()); }
}
#endregion
#region Ctor
public RavenSession(IDocumentStore store)
{
_store = store;
}
#endregion
#region Public Methods
public void Commit()
{
if (_innerSession != null)
{
try
{
InnerSession.SaveChanges();
}
finally
{
InnerSession.Dispose();
}
}
}
public void Rollback()
{
if (_innerSession != null)
{
InnerSession.Dispose();
}
}
#endregion
#region IDocumentSession Delegation
public ISyncAdvancedSessionOperation Advanced
{
get { return InnerSession.Advanced; }
}
public void Delete<T>(T entity)
{
InnerSession.Delete(entity);
}
public ILoaderWithInclude<object> Include(string path)
{
return InnerSession.Include(path);
}
public ILoaderWithInclude<T> Include<T, TInclude>(Expression<Func<T, object>> path)
{
return InnerSession.Include<T, TInclude>(path);
}
public ILoaderWithInclude<T> Include<T>(Expression<Func<T, object>> path)
{
return InnerSession.Include(path);
}
public T Load<T>(string id)
{
return InnerSession.Load<T>(id);
}
public T[] Load<T>(params string[] ids)
{
return InnerSession.Load<T>(ids);
}
public T Load<T>(ValueType id)
{
return InnerSession.Load<T>(id);
}
public T[] Load<T>(IEnumerable<string> ids)
{
return InnerSession.Load<T>(ids);
}
public IRavenQueryable<T> Query<T, TIndexCreator>() where TIndexCreator : AbstractIndexCreationTask, new()
{
return InnerSession.Query<T, TIndexCreator>();
}
public IRavenQueryable<T> Query<T>()
{
return InnerSession.Query<T>();
}
public IRavenQueryable<T> Query<T>(string indexName)
{
return InnerSession.Query<T>(indexName);
}
public void Store(dynamic entity, string id)
{
InnerSession.Store(entity, id);
}
public void Store(object entity, Guid etag, string id)
{
InnerSession.Store(entity, etag, id);
}
public void Store(object entity, Guid etag)
{
InnerSession.Store(entity, etag);
}
public void Store(dynamic entity)
{
InnerSession.Store(entity);
}
#endregion
}
And now my service looks like this:
public class FooService : ServiceBase<Foo>
{
public IRavenSession RavenSession { get; set; }
protected override object Run(ProductFind request)
{
// Do Something with RavenSession...
return new FooResponse {/*Object init*/};
}
}
but i still need to find a way to know when the request is ended for commit/rollback the changes.
the best way i found is by using ResponseFilters:
public class AppHost : AppHostBase
{
public AppHost()
: base("", typeof (Foo).Assembly, typeof (FooService).Assembly)
{
}
public override void Configure(Container container)
{
// Some Configuration...
this.ResponseFilters.Add((httpReq, httpResp, respnseDto) =>
{
var currentSession = (RavenSession) this.Container.Resolve<IRavenSession>();
if (!httpResp.IsErrorResponse())
{
currentSession.Commit();
}
else
{
currentSession.Rollback();
}
});
// Some Configuration...
}
}
I am sure that there is a better way to do this but how?
I just included this on the Configure method for the AppHost
var store = new DocumentStore()
{
Url = "http://127.0.0.1:8080",
DefaultDatabase = "Test"
}.Initialize();
container.Register(store);
container.Register(c => c.Resolve<IDocumentStore>().OpenSession()).ReusedWithin(ReuseScope.Request);
You can put it aside on module and initialize it.
Then in your services just add a constructor that accepts IDocumentSession
public HelloService : Service {
private readonly IDocumentSession session;
public HelloService(IDocumentSession session) {
this.session = session;
}
}
And you're good to go.
Filtering the response in ServiceStack
The ways to introspect the Response in ServiceStack is with either:
The Response Filter or Response Filter Attributes or other custom hooks
Overriding AppHost.ServiceExceptionHandler or custom OnAfterExecute() hook
Some other notes that might be helpful:
ServiceStack's built-in IOC (Funq) now supports RequestScope
You can add IDisposable to a base class which gets called immediately after the service has finished executing, e.g. if you were to use an RDBMS:
public class FooServiceBase : IService, IDisposable
{
public IDbConnectionFactory DbFactory { get; set; }
private IDbConnection db;
public IDbConnection Db
{
get { return db ?? (db = DbFactory.OpenDbConnection()); }
}
public object Any(ProductFind request)
{
return new FooResponse {
Result = Db.Id<Product>(request.Id)
};
}
public void Dispose()
{
if (db != null) db.Dispose();
}
}
I tried the answer given by Felipe Leusin but it has not worked for me. The main thing that I want to achieve is having a single DocumentSession.SaveChanges call per request. After looking at the RacoonBlog DocumentSession lifecycle management and at ServiceStack request lifecycle events I put together a configuration that works for me:
public override void Configure(Funq.Container container)
{
RequestFilters.Add((httpReq, httpRes, requestDto) =>
{
IDocumentSession documentSession = Container.Resolve<IDocumentStore>().OpenSession();
Container.Register<IDocumentSession>(documentSession);
});
ResponseFilters.Add((httpReq, httpRes, requestDto) =>
{
using (var documentSession = Container.Resolve<IDocumentSession>())
{
if (documentSession == null)
return;
if (httpRes.StatusCode >= 400 && httpRes.StatusCode < 600)
return;
documentSession.SaveChanges();
}
});
var documentStore = new DocumentStore
{
ConnectionStringName = "RavenDBServer",
DefaultDatabase = "MyDatabase",
}.Initialize();
container.Register(documentStore);
I am using funq with RequestScope for my RavenSession, and now i update it to:
public class RavenSession : IRavenSession, IDisposable
{
#region Data Members
private readonly IDocumentStore _store;
private readonly IRequestContext _context;
private IDocumentSession _innerSession;
#endregion
#region Properties
public IDocumentSession InnerSession
{
get { return _innerSession ?? (_innerSession = _store.OpenSession()); }
}
#endregion
#region Ctor
public RavenSession(IDocumentStore store, IRequestContext context)
{
_store = store;
_context = context;
}
#endregion
#region IDocumentSession Delegation
public ISyncAdvancedSessionOperation Advanced
{
get { return InnerSession.Advanced; }
}
public void Delete<T>(T entity)
{
InnerSession.Delete(entity);
}
public ILoaderWithInclude<object> Include(string path)
{
return InnerSession.Include(path);
}
public ILoaderWithInclude<T> Include<T, TInclude>(Expression<Func<T, object>> path)
{
return InnerSession.Include<T, TInclude>(path);
}
public ILoaderWithInclude<T> Include<T>(Expression<Func<T, object>> path)
{
return InnerSession.Include(path);
}
public T Load<T>(string id)
{
return InnerSession.Load<T>(id);
}
public T[] Load<T>(params string[] ids)
{
return InnerSession.Load<T>(ids);
}
public T Load<T>(ValueType id)
{
return InnerSession.Load<T>(id);
}
public T[] Load<T>(IEnumerable<string> ids)
{
return InnerSession.Load<T>(ids);
}
public IRavenQueryable<T> Query<T, TIndexCreator>() where TIndexCreator : AbstractIndexCreationTask, new()
{
return InnerSession.Query<T, TIndexCreator>();
}
public IRavenQueryable<T> Query<T>()
{
return InnerSession.Query<T>();
}
public IRavenQueryable<T> Query<T>(string indexName)
{
return InnerSession.Query<T>(indexName);
}
public void Store(dynamic entity, string id)
{
InnerSession.Store(entity, id);
}
public void Store(object entity, Guid etag, string id)
{
InnerSession.Store(entity, etag, id);
}
public void Store(object entity, Guid etag)
{
InnerSession.Store(entity, etag);
}
public void Store(dynamic entity)
{
InnerSession.Store(entity);
}
#endregion
#region Implementation of IDisposable
public void Dispose()
{
if (_innerSession != null)
{
var httpResponse = _context.Get<IHttpResponse>();
try
{
if (!httpResponse.IsErrorResponse())
{
_innerSession.SaveChanges();
}
}
finally
{
_innerSession.Dispose();
}
}
}
#endregion
}
but this would not work because:
1) although i am using RequestScope, no one is register the IRequestContext of the request so funq cant resolve my RavenSession.
2) funq does not run the Dispose method after the request is done, which is odd.

WCF: MessageContract handle OnDeserialized Event

How can I handle an OnDeserialized event using MessageContract?
Need to do some data verification and transformation after receiving the message (but before executing methods).
For DataContract it was solved by declaration attribute.
But for MessageContract it doesn't work.
Is there any way to do this?
You are better of using WCF extension points instead of serialization. Specifically IOperationInvoker.
EDIT
Example:
Service and message definitions below. Note the new MyValidationBeforeInvokeBehavior attribute.
[ServiceContract]
public interface IService1
{
[OperationContract]
string GetData(int value);
[OperationContract]
[MyValidationBeforeInvokeBehavior]
AddPatientRecordResponse AddPatientRecord(PatientRecord composite);
}
[MessageContract(IsWrapped = false, ProtectionLevel = ProtectionLevel.None)]
public class AddPatientRecordResponse
{
[MessageHeader(ProtectionLevel = ProtectionLevel.None)]
public Guid recordID;
[MessageHeader(ProtectionLevel = ProtectionLevel.None)]
public string patientName;
[MessageBodyMember(ProtectionLevel = ProtectionLevel.None)]
public string status;
}
[MessageContract(IsWrapped = false, ProtectionLevel = ProtectionLevel.None)]
public class PatientRecord
{
[MessageHeader(ProtectionLevel = ProtectionLevel.None)]
public Guid recordID;
[MessageHeader(ProtectionLevel = ProtectionLevel.None)]
public string patientName;
//[MessageHeader(ProtectionLevel = ProtectionLevel.EncryptAndSign)]
[MessageHeader(ProtectionLevel = ProtectionLevel.None)]
public string SSN;
[MessageBodyMember(ProtectionLevel = ProtectionLevel.None)]
public string comments;
[MessageBodyMember(ProtectionLevel = ProtectionLevel.None)]
public string diagnosis;
[MessageBodyMember(ProtectionLevel = ProtectionLevel.None)]
public string medicalHistory;
}
public class Service1 : IService1
{
public string GetData(int value)
{
return string.Format("You entered: {0}", value);
}
public AddPatientRecordResponse AddPatientRecord(PatientRecord patient)
{
var response = new AddPatientRecordResponse
{
patientName = patient.patientName,
recordID = patient.recordID,
status = "Sucess"
};
return response;
}
}
Hook into wcf extensibility
public class MyValidationBeforeInvokeBehavior : Attribute, IOperationBehavior
{
public void Validate(OperationDescription operationDescription)
{
}
public void ApplyDispatchBehavior(OperationDescription operationDescription, DispatchOperation dispatchOperation)
{
dispatchOperation.Invoker = new MyValidationBeforeInvoke(dispatchOperation.Invoker);
}
public void ApplyClientBehavior(OperationDescription operationDescription, ClientOperation clientOperation)
{
}
public void AddBindingParameters(OperationDescription operationDescription, BindingParameterCollection bindingParameters)
{
}
}
Custom operation invoker:
public class MyValidationBeforeInvoke : IOperationInvoker
{
private readonly IOperationInvoker _original;
public MyValidationBeforeInvoke(IOperationInvoker original)
{
_original = original;
}
public object[] AllocateInputs()
{
return _original.AllocateInputs();
}
public object Invoke(object instance, object[] inputs, out object[] outputs)
{
var validator = new ValidatePatientRecord((PatientRecord) inputs[0]);
if (validator.IsValid())
{
var ret = _original.Invoke(instance, inputs, out outputs);
return ret;
}
else
{
outputs = new object[] {};
var patientRecord = (PatientRecord) inputs[0];
var returnMessage = new AddPatientRecordResponse
{
patientName = patientRecord.patientName,
recordID = patientRecord.recordID,
status = "Validation Failed"
};
return returnMessage;
}
}
public IAsyncResult InvokeBegin(object instance, object[] inputs, AsyncCallback callback, object state)
{
return _original.InvokeBegin(instance, inputs, callback, state);
}
public object InvokeEnd(object instance, out object[] outputs, IAsyncResult result)
{
return _original.InvokeEnd(instance, out outputs, result);
}
public bool IsSynchronous
{
get { return _original.IsSynchronous; }
}
}
The essence is that we never invoke the service call because it will never get there due to the validation error. We can also return what happened to the client.
Validation class and client call (for completes):
public class ValidatePatientRecord
{
private readonly PatientRecord _patientRecord;
public ValidatePatientRecord(PatientRecord patientRecord)
{
_patientRecord = patientRecord;
}
public bool IsValid()
{
return _patientRecord.patientName != "Stack Overflow";
}
}
client:
class Program
{
static void Main(string[] args)
{
var patient = new PatientRecord { SSN = "123", recordID = Guid.NewGuid(), patientName = "Stack Overflow" };
var proxy = new ServiceReference1.Service1Client();
var result = proxy.AddPatientRecord(patient);
Console.WriteLine(result.status);
Console.ReadLine();
}
}