Rx.Net: Chaining subscribers - alternative approach? - system.reactive

How can I re-write this code so that I don't have to chain Subscribers like below? Reason for asking is, this style will limit in an observable depending on another observable due to the style of the code, it can get confusing.
var results = myService
.GetData(accountId) // returns IObservable
.Subscribe(data =>
{
new MyWork().Execute(data) // returns IObservable
.Subscribe(result =>
{
myResults.Add(result);
WriteLine($"Result Id: {result.Id}");
WriteLine($"Result Status: {result.Pass}");
});
});
Added after 1st reply from Peter Bons
Below is the code for MyWork class that has the Execute Method
public class MyWork
{
public virtual IObservable<MyResult> Execute(MyData data)
{
MyResult result = null;
return IsMatch(data)
.Do(isMatch =>
{
if (isMatch)
{
result = new MyResult(1, true);
}
})
.Select(_ => result);
}
public IObservable<bool> IsMatch(MyData data)
{
return true;
}
}

It's really quite simple.
var results =
myService
.GetData(accountId)
.SelectMany(data => new MyWork().Execute(data))
.Subscribe(result =>
{
myResults.Add(result);
Console.WriteLine($"Result Id: {result.Id}");
Console.WriteLine($"Result Status: {result.Pass}");
});
If ever you are subscribing within a subscription then you are doing something wrong. Keep that in mind. There is almost always a way to make it a pure query with a single subscription.
Just to help out with testing, here's the code required to make this a Minimal, Complete, and Verifiable example.
public static class myService
{
public static IObservable<MyData> GetData(int x)
=> Observable.Return(new MyData());
}
public class MyWork
{
public virtual IObservable<MyResult> Execute(MyData data)
{
MyResult result = null;
return IsMatch(data)
.Do(isMatch =>
{
if (isMatch)
{
result = new MyResult() { Id = 1, Pass = true};
}
})
.Select(_ => result);
}
public IObservable<bool> IsMatch(MyData data)
{
return Observable.Return(true);
}
}
public class MyResult
{
public int Id;
public bool Pass;
}
public class MyData { }

Related

Subscribe to Close, but close only if item was saved

My scenario:
In an MVVM pattern, the view should be closed when a command is executed on ViewModel, but only if the item was successfully saved.
The View looks like:
public class CentreUpdateWindow : ReactiveWindow<CentreUpdateViewModel>
{
public CentreUpdateWindow()
{
this.InitializeComponent();
this.WhenActivated(d =>
d(ViewModel!.SubmitCommand.Subscribe(CloseIfSuccessfullySaved))
);
}
private void CloseIfSaved(Centre? obj)
{
if (ViewModel!.SuccessfullySaved)
Close(obj);
}
// ...
And the ViewModel:
public class CentreUpdateViewModel : ViewModelBase, IId
{
// ...
public ReactiveCommand<Unit, dtoo.Centre?> SubmitCommand { get; }
private bool _SuccessfullySaved;
public bool SuccessfullySaved
{
get { return _SuccessfullySaved; }
protected set { this.RaiseAndSetIfChanged(ref _SuccessfullySaved, value); }
}
The question:
The code works fine, but I'm not comfortable with the if (ViewModel!.SuccessfullySaved). I guess should be a way to write subscribe expression more accurate.
Is there a more elegant way to Subscribe on WhenActivated more "reactiveuistic" ?
public CentreUpdateWindow()
{
this.InitializeComponent();
this.WhenActivated(d =>
d(
ViewModel
.WhenAnyValue(x => x.SuccessfullySaved)
.CombineLatest(ViewModel!.SubmitCommand,
(saved, obj) => (saved, obj))
.Where(s => s.saved)
.Select(s => s.obj)
.Subscribe(Close)
));
}

context.GetArgument() returning null with ByteGraphType

I´m learning how to use CustomScalar in graphql-dotnet.
I have a tinyint column in my table and from what I have read, I´m supposed to use byte on this column in C#. After research I found out that I need to create a ByteGraphType, but I´m having trouble doing that.
I got the ByteGraphType example from this link https://github.com/graphql-dotnet/graphql-dotnet/issues/458, so I think it will work.
With this code, I can query the table, however, my mutation is not working. I didn´t find an example to demonstrate how the mutation would look like with a byte column. I tried as is stated in my code example, but in this line (var avaliacao = context.GetArgument("avaliacao");), my argument avaliacao.Nota is returning null and I´m not sure on how to proceed.
Can someone help me?
Thank you
THAT´S MY CODE
//Model
[Column("nota")]
public byte Nota { get; set; }
//Type
Field<ByteGraphType>("Nota", resolve: context => context.Source.Nota);
//InputType
Field<ByteGraphType>("nota");
//Query
Field<ListGraphType<AvaliacaoType>>(
"avaliacoes",
resolve: context => contextServiceLocator.AvaliacaoRepository.All());
//Mutation
Field<AvaliacaoType>(
"createAvaliacao",
arguments: new QueryArguments(
new QueryArgument<NonNullGraphType<AvaliacaoInputType>> { Name = "avaliacao" }
),
resolve: context =>
{
var schema = new Schema();
schema.RegisterValueConverter(new ByteValueConverter());
var avaliacao = context.GetArgument<Avaliacao>("avaliacao");
avaliacao.Nota.AstFromValue(schema, new ByteGraphType());
return contextServiceLocator.AvaliacaoRepository.Add(avaliacao);
});
//ByteGraphType
using GraphQL.Language.AST;
using GraphQL.Types;
using System;
namespace Api.Helpers
{
public class ByteGraphType : ScalarGraphType
{
public ByteGraphType()
{
Name = "Byte";
}
public override object ParseLiteral(IValue value)
{
var byteVal = value as ByteValue;
return byteVal?.Value;
}
public override object ParseValue(object value)
{
if (value == null)
return null;
try
{
var result = Convert.ToByte(value);
return result;
}
catch (FormatException)
{
return null;
}
}
public override object Serialize(object value)
{
return ParseValue(value).ToString();
}
public class ByteValueConverter : IAstFromValueConverter
{
public bool Matches(object value, IGraphType type)
{
return value is byte;
}
public IValue Convert(object value, IGraphType type)
{
return new ByteValue((byte)value);
}
}
public class ByteValue : ValueNode<byte>
{
public ByteValue(byte value)
{
Value = value;
}
protected override bool Equals(ValueNode<byte> node)
{
return Value == node.Value;
}
}
}
}
What I need is to be able to save a record of a table that has a tinyint column. If I change the type in my code to int, I can mutate, but can´t query.
I changed my CustomScalar and it worked:
using GraphQL.Language.AST;
using GraphQL.Types;
using System;
namespace Api.Helpers
{
public class ByteGraphType : ScalarGraphType
{
public ByteGraphType()
{
Name = "Byte";
Description = "ByteGraphType";
}
/// <inheritdoc />
public override object Serialize(object value)
{
return ParseValue(value).ToString();
}
/// <inheritdoc />
public override object ParseValue(object value)
{
byte result;
if (byte.TryParse(value?.ToString() ?? string.Empty, out result))
{
return result;
}
return null;
}
/// <inheritdoc />
public override object ParseLiteral(IValue value)
{
if (value is StringValue)
{
return ParseValue(((StringValue)value).Value);
}
return null;
}
}
}

Creating a hot observable and adding things to it

I am trying to create a hot observable where I can add stuff to it. Here's an outline of the basic class
public class MyObservable
{
public IObservable<string> Stream;
public MyObservable()
{
Observable.Create...?
}
public void AddString(string eventDescription)
{
//Add to Stream
}
}
Somewhere else in the code I want to be able to do something like
var ob = new MyObservable();
MyObservable.Add("User created");
Then somewhere else something like:
ob.Stream.Subscribe(Console.WriteLine);
I am not really sure how I am supposed to add strings to the observable
edit: I've tried doing something like this, but I'm not sure if maybe I'm not doing things in the way it's supposed to be done
private IObserver<string> _observer;
public void Add(string e)
{
if(Stream == null)
{
Stream = Observable.Create<string>(
(IObserver<string> observer) =>
{
_observer = observer;
observer.OnNext(e);
return Disposable.Empty;
});
}
else
{
_observer.OnNext(e);
}
}
You should do a little more reading on the contracts of observables and observers
Regardless, what you are looking for is a Subject, which implements both the Observable and Observer interfaces.
If you still want to wrap it it would look like so:
public class MyObservable
{
private Subject<string> subject;
public IObservable<string> Stream
{
get { return this.subject.AsObservable();
}
public MyObservable()
{
subject = new Subject<string>();
}
public void AddString(string eventDescription)
{
//Add to Stream
this.subject.OnNext(eventDescription);
}
}

ASP.NET Web Api CRUD operation in VS 2010 web application

I tried to make ASP.NET Web Api CRUD operation in VS 2010 web application, but why the result is not returning all entire row from source table.
This is my code :
Route/Globax.asax
protected void Application_Start(object sender, EventArgs e)
{
RouteTable.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{id}", // browse with localhost:7031/api/product
//routeTemplate: "{controller}/{id}", // browse with localhost:7031/product
defaults: new { id = System.Web.Http.RouteParameter.Optional }
);
Controller/ProductController.cs :
public class ProductController : ApiController
{
NorthwindEntities db = new NorthwindEntities();
public List<Product> GetAll()
{
return db.Products.ToList<Product>();// ;
}
View/ViewProduct.aspx :
<script src="Script/jquery-1.7.1.min.js" type="text/javascript"></script>
<script type="text/javascript">
$(function () {
$('#<%= cviewproduct.ClientID %>').click(function (e) {
getProducts();
e.preventDefault();
});
});
function getProducts() {
$.getJSON("/api/product",
function (data) {
$.each(data, function (key, val) {
//var str = val.ProductName;
// alert(str);
var row = '<tr> <td>' + val.ProductName + '</td><td>' + val.ProductID + '</td><tr/>';
$(row).appendTo($('#tblproduct'));
});
});
}
</script>
Bellow Is The Result of product Controller via 'http://localhost:7031/api/product' :
Bellow Is The Result of getProducts() function :
Please help me.
Any idea or suggestion ?
When you execute $.getJSON("/api/product", ..., you are not getting back XML as you posted. You are getting back JSON.
As a first step, I suggest you download and install Fiddler2. Open it, and use the Composer tab to execute a GET for http://localhost:7031/api/product. That should show you the JSON returned, which will be different from the XML. Post that JSON to your original question and we should be able to help further.
My guess is that the JSON is not properly formatted. What does your WebApiConfig.cs class look like?
Update
Yes, there is a better way. First, create an ApiModel (ApiModel is to WebAPI what ViewModel is to MVC):
public class ProductApiModel
{
public int ProductId { get; set; }
public string ProductName { get; set; }
}
Now, return this instead of an entity from your controller:
public class ProductController : ApiController
{
public IEnumerable<ProductApiModel> GetAll()
{
var products = db.Products
.OrderBy(x => x.ProductID)
.Select(x => new ProductApiModel
{
ProductId = x.ProductID,
ProductName = x.ProductName
});
return products;
}
}
If you used AutoMapper, you could make your controller code a bit shorter:
public class ProductController : ApiController
{
public IEnumerable<ProductApiModel> GetAll()
{
var entities = db.Products.OrderBy(x => x.ProductID);
var models = Mapper.Map<ProductApiModel[]>(entities);
return models;
}
}
After do some research, i finaly found (Please Correct Me, if i'm wrong) :
JavaScriptSerializer failed to serialize the entire objects tree from relations
between the entity and other entities (Product and Catagory).
So .. I made ​​some changes to my code, and the results are as expected
Controller/ProductController.cs :
From :
public class ProductController : ApiController
{
NorthwindEntities db = new NorthwindEntities();
public List<Product> GetAll()
{
return db.Products.ToList<Product>();// ;
}
To :
public class ProductController : ApiController
{
public string GetAll()
{
var product = db.Products.OrderBy(x => x.ProductID).Select(x => new
{
ProductId = x.ProductID,
ProductName = x.ProductName
});
return JsonConvert.SerializeObject(product);
}
View/ViewProduct.aspx :
From :
function getProducts() {
$.getJSON("/api/product",
function (data) {
$.each(data, function (key, val) {
var row = '<tr> <td>' + val.ProductName
+ '</td><td>' + val.ProductID + '</td><tr/>';
$(row).appendTo($('#tblproduct'));
});
});
To :
$.getJSON("/api/product",
function (data) {
var obj = $.parseJSON(data);
$.each(obj, function (key, val) {
var row = '<tr> <td>' + val.ProductId
+ '</td><td>' + val.ProductName + '</td><tr/>';
$(row).appendTo($('#tblproduct'));
});
});
Or... is there a better way than this,
if I still want to pass an entity object instead of a string object
(List GetAll ()... instead GetAll string ()....)
Regards,
Andrian
So.. This My Final Working Code
Models :
namespace MyLabs1.Models
{
public class ProductApi
{
[Key]
public Int32 ProductID { get; set; }
public string ProductName { get; set; }
}
}
Controller/ProductController.cs :
public IEnumerable<ProductApi> getall()
{
Mapper.CreateMap<Product, ProductApi>();
var entities = db.Products.OrderBy(x => x.ProductID).ToList();
var models = Mapper.Map<ProductApi[]>(entities);
return models;
}
OR
public List<ProductApi> GetAll()
{
var products = db.Products
.OrderBy(x => x.ProductID)
.Select(x => new ProductApi
{
ProductID = x.ProductID,
ProductName = x.ProductName
}).ToList();
return products;
}
View/ViewProduct.aspx :
function getProducts() {
$.getJSON("/api/product",
function (data) {
$.each(data, function (key, val) {
var row = '<tr> <td>' + val.ProductName + '</td><td>' + val.ProductID + '</td><tr/>';
$(row).appendTo($('#tblproduct'));
});
});
}
Hope This Will Be Helpful to Others
Regards,
Andrian

Reactive Extension in msword

I am wondering if it is possible to use Reactive Extensions in Word. I have seen this where Jeff shows how to wire up a workbook open event in excel http://social.msdn.microsoft.com/Forums/en/rx/thread/5ace45b1-778b-4ddd-b2ab-d5c8a1659f5f.
I wondering if I could do the same sort of thing in word.
I have got this far....
public static class ApplicationExtensions
{
public static IObservable<Word.Document> DocumentBeforeSaveAsObservable(this Word.Application application)
{
return Observable.Create<Word.Document>(observer =>
{
Word.ApplicationEvents4_DocumentBeforeSaveEventHandler handler = observer.OnNext;
application.DocumentBeforeSave += handler;
return () => application.DocumentBeforeSave -= handler;
});
}
}
but I receive the error No overload for 'OnNext' matches delegate 'Microsoft.Office.Interop.Word.ApplicationEvents4_DocumentBeforeSaveEventHandler
Can anyone point me in the right direction.
Regards
Mike
Your problem is an issue of delegate signatures.
IObserver<T>.OnNext is defined as void (T value)
whereas ApplicationEvents4_DocumentBeforeSaveEventHandler is defined as void (Document doc, ref bool SaveAsUI, ref bool Cancel)
If you only need to emit the Document (and not the other details, like making it cancelable), you can do something like this:
public static IObservable<Word.Document> DocumentBeforeSaveAsObservable(
this Word.Application application)
{
return Observable.Create<Word.Document>(observer =>
{
Word.ApplicationEvents4_DocumentBeforeSaveEventHandler handler =
(doc, ref saveAsUI, ref cancel) => observer.OnNext(doc);
application.DocumentBeforeSave += handler;
return () => application.DocumentBeforeSave -= handler;
});
}
If you do require all the data, you'll need to create a wrapper class of some kind an IObservable sequence can only emit a single type:
public class DocumentBeforeSaveEventArgs : CancelEventArgs
{
public Document Document { get; private set; }
public bool SaveAsUI { get; private set; }
public DocumentBeforeSaveEventArgs(Document document, bool saveAsUI)
{
this.Document = document;
this.SaveAsUI = saveAsUI;
}
}
And then you can use it like so:
public static IObservable<Word.Document> DocumentBeforeSaveAsObservable(
this Word.Application application)
{
return Observable.Create<Word.Document>(observer =>
{
Word.ApplicationEvents4_DocumentBeforeSaveEventHandler handler =
(doc, ref saveAsUI, ref cancel) =>
{
var args = new DocumentBeforeSaveEventArgs(doc, saveAsUI);
observer.OnNext(args);
cancel = args.Cancel;
};
application.DocumentBeforeSave += handler;
return () => application.DocumentBeforeSave -= handler;
});
}