Using CommandLineParser I can define a set of mutually exclusive options like this (taken from the wiki):
class OptionsMutuallyExclusive
{
//define commands in set(group) named web
[Option(SetName = "web")]
public string DocumentRoot { get; set; }
[Option(SetName = "web")]
public bool EnableJavaScript { get; set; }
//define other commands in set(group) named ftp
[Option(SetName = "ftp")]
public string FtpDirectory { get; set; }
[Option(SetName = "ftp")]
public bool AnonymousLogin { get; set; }
//Common: the next option can be used with any set because it's not included in a set
[Option('r')]
public string UrlAddress { get; set; }
}
Now I can parse this my app like this (.Dump() coming from LinqPad)
var result = parser.ParseArguments<OptionsMutuallyExclusive>(args);
result.WithParsed(options => options.Dump()).WithNotParsed(errs => errs.Dump());
And will get a positive result (WithParsed) using the following args:
--enablejavascript --documentroot ~/var/local/website -r http://localhost
--ftpdirectory ~/var/local/ftpsite --anonymouslogin -r ftp://myftp.server
And will get an error (WithNotParsed) on this:
--enablejavascript --anonymouslogin
But: Is there any way to tell, which set was used?
So getting the value web on the first call and the value ftp on the second one? Or by using an interface and/or derived classes getting an typed result like WebOptions and FtpOptions?
Using different classes I can call ParseArguments multiple times until it succeeds but this isn't really nice.
All examples I could find are always trying to determine the current set by testing which values are defined and which are not.
Is there any thing I'm missing?
Any alternative?
Related
Let's say we have a realm results taken with
RealmDb.All<Entry>();
Then I want to do some search over those results using not yet supported techniques, like StartsWith on a function return or on a property which is not mapped in realm etc, so I get a subset
IEnumerable<Entry> subset = bgHaystack;
var results = subset.Where(entry => entry.Content.ToLower().StartsWith(needle));
To get somehow these as part of RealmResults, I extract the entry ids like this:
List<int> Ids = new List<int>();
foreach (Entry entry in entries)
{
Ids.Add(entry.Id);
}
return Ids;
and finally I want to return a subset of RealmResults (not IEnumerable) of only those Entries that contain those ids, how can I do that? IDE says the Contains method is not supported.
Can I use some kind of predicate or a comparer for that?
Entry is my model class
using System.ComponentModel.DataAnnotations.Schema;
using Realms;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System;
namespace Data.Models
{
[Table("entry")]
public class Entry : RealmObject
{
public class EntryType
{
public const byte Word = 1;
public const byte Phrase = 2;
public const byte Text = 3;
};
[Key]
[PrimaryKey]
[Column("entry_id")]
public int Id { get; set; }
[Column("user_id")]
public int UserId { get; set; }
[Column("source_id")]
public int SourceId { get; set; }
[Indexed]
[Column("type")]
public byte Type { get; set; }
[Column("rate")]
public int Rate { get; set; }
[Column("created_at")]
public string CreatedAt { get; set; }
[Column("updated_at")]
public string UpdatedAt { get; set; }
[NotMapped]
public Phrase Phrase { get; set; }
[NotMapped]
public Word Word { get; set; }
[NotMapped]
public Text Text { get; set; }
[NotMapped]
public IList<Translation> Translations { get; }
[NotMapped]
public string Content
{
get {
switch (Type)
{
case EntryType.Phrase:
return Phrase?.Content;
case EntryType.Word:
return Word?.Content;
case EntryType.Text:
return Text?.Content;
}
return "";
}
}
}
}
According to the documentation, Realm .NET supports LINQ, so that's promising. In your specific example, you indicate that StartsWith isn't supported, but I see that on the above page, specifically here.
Now, your example makes clear that Entry is a RealmObject, so it's not clear where you'd possibly get a RealmResult from (nor does their documentation on that page mention a RealmResult). Specifically, the home page indicates that you're really only going to ever work with Realm, RealmObject and Transaction, so I'm going to just assume that you meant that you'll need a resulting RealmObject per their examples.
The way you presently have your data object set up, you're rather stuck calling it like you are (though if I could make a recommendation to simplify it a little bit:
var entries = RealmDb.All<Entry>().ToList();
var results = entries.Where(entry => entry.Content.ToLower().StartsWith(needle));
var ids = results.Select(a => a.Id).ToList();
Now, your big issue with just combining the filter predicate in line 2 with the end of line 1: Content itself is marked with a [NotMapped] attribute. Per the documentation again:
As a general rule, you can only create predicates with conditions that
rely on data in Realm. Imagine a class
class Person : RealmObject
{
// Persisted properties
public string FirstName { get; set; }
public string LastName { get; set; }
// Non-persisted property
public string FullName => FirstName + " " + LastName;
}
Given this class, you can create queries with conditions that apply to
the FirstName and LastName properties but not to the FullName
property. Likewise, properties with the [Ignored] attribute cannot be
used.
Because you're using [NotMapped], I've got to believe that's going to behave similarly to [Ignored] and further, because it's just a computed value, it's not something that Realm is going to be able to process as part of the query - it simply doesn't know it because you didn't map it to the information Realm is storing. Rather, you'll have to compute the Content property when you've actually got the instances of your Entry objects to enumerate through.
Similarly, I expect you'll have issues pulling values from Phrase, Word and Text since they're also not mapped, and thus not stored in the record within Realm (unless you're populating those in code you didn't post before executing your Where filter).
As such, you might instead consider storing separate records as a PhraseEntry, WordEntry, and TextEntry so you can indeed perform exactly that filter and execute it on Realm. What if you instead used the following?
public class Entry : RealmObject
{
[Key]
[PrimaryKey]
[Column("entry_id")]
public int Id { get; set; }
[Column("user_id")]
public int UserId { get; set; }
[Column("source_id")]
public int SourceId { get; set; }
[Column("rate")]
public int Rate { get; set; }
[Column("created_at")]
public string CreatedAt { get; set; }
[Column("updated_at")]
public string UpdatedAt { get; set; }
[Column("content")]
public string Content { get; set; }
[NotMapped]
public IList<Translation> Translations { get; }
}
[Table("wordEntry")]
public class WordEntry : Entry
{
}
[Table("phraseEntry")]
public class PhraseEntry : Entry
{
}
[Table("textEntry")]
public class TextEntry : Entry
{
}
And now, you can offload the filtering to Realm:
var wordEntries = RealmDb.All<WordEntry>.Where(entry =>
entry.Content.StartsWith(needle, StringComparison.OrdinalIgnoreCase)).ToList();
var phraseEntries = RealmDb.All<PhraseEntry>.Where(entry => entry.Content.StartsWith(needle, StringComparison.OrdinalIgnoreCase)).ToList();
var textEntries = RealmDb.All<TextEntry>.Where(entry => entry.Content.StartsWith(needle, StringComparison.OrdinalIgnoreCase)).ToList();
var entries = new List<Entry>();
entries.AddRange(wordEntries);
entries.AddRange(phraseEntries);
entries.AddRange(textEntries);
var ids = entries.Select(entry => entry.Id).ToList();
It's not quite as brief as storing it all in one table, but I'm not immediately seeing any Realm documentation that indicates support for executing the same query against multiple tables simultaneously, so at least this would allow you to leave the filtering to the database and work against a more limited subset of values locally.
Finally, so we have all that and I missed your final question up top. You indicate that you want to return a subset of your entries based on some collection of ids you create. In the logic you provide, you're retrieving all the Id properties in all your results, so there's really no further subset to pull.
That said, let's assume you have a separate list of ids that for whatever complicated reason, you were only able to derive after retrieving the list of Entry types from above (themselves all PhraseEntry, WordEntry or TextEntry objects).
At this point, since you've already pulled all the values from Realm and have them locally, just execute another Where statement against them. Because a List implements IEnumerable, you can thus execute the LINQ locally without any of the Realm restrictions:
var myLimitedIdSet = new List<int>()
{
10, 15, 20, 25 //Really complicated logic to narrow these down locally
};
var resultingEntries = entries.Where(entry => myLimitedIdSet.Contains(entry.Id)).ToList();
And you're set. You'll have only those entries that match the IDs listed in myLimitedIdSet.
Edit to address comment
You see this error because of the detail provided at the top of this page in the documentation. Specifically (and adapting to your code):
The first statement gives you a new instance of Entry of a class that implements IQueryable... This is standard LINQ implementation - you get an object representing the query. The query doesn't do anything until you made a further call that needs to iterate or count the results.
Your error is then derived by taking the result from RealmDb.All<Entry>() and trying to cast it to an IEnumerable<Entry> to operate against it as though you have local data. Until you call ToList() onRealmDb.All` you simply have a LINQ representation of what the call will be, not the data itself. As such, when you further refine your results with a Where statement, you're actually adding that to a narrowed version of the IQueryable statement, which will also fail because you lack the appropriate mapping in the Realm dataset.
To skip the optimization I provided above, the following should resolve your issue here:
var bgHaystack = realm.All<Entry>().ToList(); //Now you have local data
var results = bgHaystack.Where(entry => entry.Content.ToLower().StartsWith(needle));
Unfortunately, given your provided code, I don't expect that you'll see any matches here unless needle is an empty string. Not only is your Content property not part of the Realm data and you thus cannot filter on it within Realm, but neither are your Phrase, Word or Text properties mapped either. As a result, you will only ever see an empty string when getting your Content value.
You can further refine the results variable above to yield only those instances with a provided ID as you see fit with normal LINQ (as again, you'll have pulled the data from Realm in the first line).
var limitedIds = new List<int>{10, 20, 30};
var resultsLimitedById = results.Select(a => limitedIds.Contains(a.Id)).ToList();
I've updated my examples above to reflect the use of ToList() in the appropriate places as well.
I have a desktop app written in c# and I added app id and key id
and used this code to add data to database but the data is always empty or null.
var film = new Film();
film.setName(“soooft”);
film.setGenre(“aaa”);
film.setPlot(“fdgveqw”);
film.setUrl(“gdfwrw”);
var f = Backendless.Data.Of<Film>().Save(film);
I googled Backendless and it's a third-party solution. (See https://github.com/Backendless/.NET-SDK)
Usage gets explained at https://backendless.com/docs/dotnet/data_data_object.html
But I'm suspicious about why you use setName(), setGenre(), setPlot and setUrl in your code. Seems your Film class is missing properties. I would expect you'd be writing this instead:
var film = new Film();
film.Name = “soooft”;
film.Genre = “aaa”;
film.Plot = “fdgveqw”;
film.Url = “gdfwrw”;
But that would mean those fields are declared as public properties in your class like this:
public class Film
{
public string Name { get; set; }
public string Genre { get; set; }
public string Plot { get; set; }
public string Url { get; set; }
}
So I don't know why you have those setName and other methods. The Backendless API specifies that these fields need to be public properties so it can read them through reflection. Your code seems to suggests that they're not proper properties as indicated by their example and my code of the Film() class.
Make sure to use public get/set properties instead of private fields and the data will be saved properly.
The issue is with the following class:
[DebuggerDisplay("{Kind}: {Identifier}")]
public class SocialConnection
{
public virtual Guid UniqueId
{
get { return Id; }
set { Id = value; }
}
// SocialConnectionKind is an enumeration
public virtual SocialConnectionKind Kind { get; set; }
public virtual string Identifier { get; set; }
}
Kind property never gets serialized: when I request an object which has an associated SocialConnection I never get the whole property.
BTW, if I manually call JsonConvert.SerializeObject it gets serialized. It should be something with the default media-type formatter but I can't figure out the solution so far.
The issue which was causing this serialization problem was very simple. Check SocialConnectionKind enumeration:
public enum SocialConnectionKind
{
Skype,
Facebook,
Twitter,
LinkedIn,
Hangouts
}
Did you already notice what could be the problem? The issue wouldn't be reproduced if the value would be any excepting Skype!
Why? Enumerations start with 0 and see how I've configured my WebAPI's HttpConfiguration:
config.Formatters.JsonFormatter.SerializerSettings.DefaultValueHandling =
DefaultValueHandling.Ignore;
Which is the default value of the Enum default underlying type int? Yes, it's 0.
So, what solved the issue?
public enum SocialConnectionKind
{
Skype = 1, // <--- The issue was solved starting the enumeration from 1!
Facebook,
Twitter,
LinkedIn,
Hangouts
}
Another approach
As #Avner Shahar-Kashtan have pointed out in some comment, I could also solve this issue using [JsonProperty(DefaultValueHandling = DefaultValueHandling.Include)] attribute:
[DebuggerDisplay("{Kind}: {Identifier}")]
public class SocialConnection
{
public virtual Guid UniqueId
{
get { return Id; }
set { Id = value; }
}
// SocialConnectionKind is an enumeration
[JsonProperty(DefaultValueHandling = DefaultValueHandling.Include)]
public virtual SocialConnectionKind Kind { get; set; }
public virtual string Identifier { get; set; }
}
...and this way there's no need of starting an enumeration from 1.
Anyway, in my particular case, I prefer to stay with the start from 1 approach, because I find cleaner avoid polluting my POCOs with serialization-specific attributes because SocialConnection class lives in a shared library and this serialization issue is an implementation issue in a concrete project.
I'm trying to use Azure Mobile Services to create a backend for an asynchronous multiplayer game. I'm using a sql database and a .NET backend on WAMS, calling the service from the .NET client (Xamarin.iOS specifically atm).
The class for the item being into the db:
public class Match {
public string Id { get; set; }
public int Challengers { get; set; }
string GameData { get; set; }
public List<string> Players { get; set; }
public string LastPlayer { get; set; }
public string Message { get; set; }
public string NextPlayer { get; set; }
public int PlayerGroup { get; set; }
}
I'm inserting it into the database using:
var matchtable = MobileService.GetTable <Match> ();
CurrentMatch = new Match {
Message = variant.ToString () + ", " + CurrentUser + " vs ??",
NextPlayer = CurrentUser,
Players = players,
PlayerGroup = playerGroup,
Challengers = 0,
Game = null,
LastPlayer = null
};
await matchtable.InsertAsync (CurrentMatch);
I'm then doing other things that will affect the match and need to update it again later, but I don't have an Id field for the CurrentMatch to be able to do the update. Everything I can find tells me that I should get the Id field back after the insert (either the method returning something or updating CurrentMatch itself with it), but it must all be talking about a javascript backend or different client or something. The InsertAsync method in the .NET client has no return value (well, technically returns Task) and the CurrentMatch doesn't get updated with the Id field from the call (also makes sense since it's not a ref or out parameter).
How on earth am I supposed to get the Id field for an object I just inserted into the database?
I'm assuming you are using the latest version of the Mobile Services client SDK, in which case you are calling this InsertAsync method here.
You're right that the parameter is not a ref or out parameter, but it can modify the fields of the object you passed in. In this case, it will modify the contents of the Match object.
My guess is that there is another code issue that's interfering. Or, if that code snippet is in a method, make sure it returns a Task and you await it before you check the contents of Id. A simple console log should help here.
If this doesn't solve the problem, then please include more context, otherwise the code you've written should behave as I've said.
I have this class, as a cut down version:
public class SportTableRow
{
public Int32 Won { get; set; }
public Int32 Lost { get; set; }
public Int32 Drawn { get; set; }
public Int32 For { get; set; }
}
When I make a call to the Data via the WebAPI, it looks like this (again cut down)...
public List<SportTableRow> Get()
{
var options = ....
var sport = ....
var locationCode = ...
return SportManager.GetOverallTable(sport, options,
locationCode).TableRows;
}
When I inspect the returned data in debugger, you can see the properties in the list...
But, when I call via fiddler, you can see that a few properties are missing...
...and it seems to be any Int's that are 0, and bool's which are false etc.
Do I need to set anything on the actual class, or something in the JSON serializer?
The JSON serializer JSON.NET is set by default to exclude properties that are set to default values. For Example, boolean=false, int=0, int?=null, object=null, etc. will be excluded from the resulting JSON. The intention is to minimize bandwidth.
You can change this behavior by changing the settings:
System.Web.Http
.GlobalConfiguration.Configuration
.Formatters
.JsonFormatter
.SerializerSettings
.DefaultValueHandling
= Newtonsoft.Json.DefaultValueHandling.Include;
Best add this line in the Global.asax file. But note: This will just add bandwidth with no real benefit, especially if you control the client side too