struct table inside struct - ASP.NET - allegro webAPI - c#

I stopped in one point. I'm writing a small ASP.NET application with WebAPI from polish site allegro.pl (it's similiar to ebay.com)
This API have a method which returns me some data:
(...)
3. searchArray | SearchResponseType[]
Struct table, where are the information about offers that matches the question
(...)
Inside this struct there 28 types of data which are returned, for ex:
1. sItId | long
Offer id
2. sItName | string
Title of offer
etc..
(...)
And on the end there is a struct table with 2 subfields
28. sItAttribsList | AttribStruct[]
Struct table which contains informations about parameters assigned to offer
1. attribName | string
Name of parameter
2. attribValues | string[]
Table of parameter values
Okay - in my code behind I want to retrieve those information and display them on my page.
I have a model which contains fields like the fields which are returned from WebAPI
namespace allegrotest.Models
{
public class SearchArrayModel
{
public long ID { get; set; }
public string Name { get; set; }
(...)
public struct AttribStruct
{
public string AttribName { get; set; }
public string[] AttribValues { get; set; }
}
public AttribStruct[] AttribStructTable { get; set; }
}
}
And in my controller:
foreach (SearchResponseType item in sercharray)
{
SearchArrayModel searchArrMdl = new SearchArradyModel
{
ID = item.sitid,
Name = item.sitname,
(...)
AttribStructTable = new SearchArrayModel.AttribStruct[]
{
//what now? Because I can't enter to the fields from my Model
}
}
The problem is in the line
AttribStructTable = new SearchArrayModel.AttribStruct[]
I don't know is it declared right or maybe I'm making something bad.
How to solve this?
Or maybe I have something wrong in my Model? Maybe AttribStructTable won't be a table? (If AttribStructTable isn't a table I can get to the fields, otherwise I can't)
Maybe - http://allegro.pl/webapi/documentation.php/show/id,116#method-output
It's the link with the information, it's in Polish, so you have to click "Dane zwracane" - it's "Returned data"

It is standard array initialization. The only difference is that you have an array of structs.
Just use:
AttribStructTable = new []
{
new AttribStruct
{
AttribName = "YOUR_NAME",
AttribValues = new [] { "Value1", "Value2" }
},
// There can be n array items
}
Reference to MSDN arrays and MSDN structs

Related

Complex nested Array trying to find if an object is null c# asp.net

I am using an api for a shopping cart that has some complex json (very complicated to me) data structured like in my screenshot below. In this scenario in my code I am trying to fix an error which I am going to explain by illustrating the data and how its structured as I am very new to JSON and arrays.
This is from the Visual Studio json reader of the data that belongs to an order placed by a customer. This item at the index of [0] has a customFields which has a value.
When a customer completes a purchase, some items they bought can have custom fields, like the size of a shirt (Large) or (Medium) or (Small) etc... In the JSON these customFields have a value which in this case is the size of the shirt for me to display at the thank you page so the customer knows what size he bought. Essentially I am trying to have the data ready to pass to the thank you page view.
When I am calling for these items in my controller, the code only works if ALL the items that were purchased have a customFields. If the customer buys something like a coffee mug that has NO custom fields, then the application breaks because I guess my code is only accounting for items that actually have customFields.
This is the code that I have so far that only works when ALL items that were purchased have a custom field. This is inside my controller.
public ActionResult Thankyou(string token)
{
int itemsCountAddedToCart = (int)obj["items"].Count();
var items = obj["items"].Select(o =>
new Item
{
name = o["name"].ToString(),
quantity = int.Parse(o["quantity"].ToString()),
price = double.Parse(o["price"].ToString()),
image = o["image"].ToString(),
url = o["url"].ToString(),
//This customFields is what works, but only if all items had custom fields.
customFields = o["customFields"][0]["value"].ToString(),
});
thankYouViewModel.OrderItems = items;
}
//ThankYou View Model that loads hold the data to be able to show in the view.
public class ThankYouViewModel
{
public IEnumerable<Item> OrderItems { get; set; }
}
public class Item
{
public string name { get; set; }
public double price { get; set; }
public int quantity { get; set; }
public string image { get; set; }
public string url { get; set; }
//customFields
public string customFields { get; set; }
}
So that code above works, but breaks when I have items that do not have customFields. This is the error that I get:
System.ArgumentOutOfRangeException: 'Index was out of range. Must be non-negative and less than the size of the collection.
Parameter name: index'
So how should my code look where its currently breaking so that it can account for situations where one of the items from the JSON does not have a customFields attribute? I am very stuck and have tried to add some conditional statements but did not work because I am dealing with some complex json I do not understand very well yet.
If you want to forget the possibility of more than one element in the customFields array, and only cast the first element value to a string, then use this:
customFields = (o["customFields"] == null || o["customFields"].Count() == 0)?null:o["customFields"][0]["value"].ToString(),
With customFields = o["customFields"][0]["value"].ToString(), you directly receive the value from the customFields Array. If there is no Array in your case then there is nothing to get.
I would recommend you to check if your customFields exists:
var item = new Item ();
item.name = o["name"].ToString();
item.quantity = int.Parse(o["quantity"].ToString());
item.price = double.Parse(o["price"].ToString());
item.image = o["image"].ToString();
item.url = o["image"].ToString();
if(o["customFields"] != null)
{
item.customFields = o["customFields"][0]["value"].ToString();
}

Using Contains() in a Realm query

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.

Backendless c# desktop application saving data but by empty or null values

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.

ASP MVC 5 EF - Saving application settings -

I want to save my application settings like wordpress saves its app settings in wp_options table.
wp_options table schema is as follows:
option_id option_name option_value autoload
-------------------------------------------------
1 siteurl 'mywebsite.com' yes
2 blogname 'myblog' yes
If I save like this then I wont be able to directly access values like object['siteurl']. Do I need to make custom mappings?
I am using Entity Framework btw.
Here's a mapping sample just to give you an idea.
public class Option
{
public int Id { get; set; }
public string Name { get; set; }
public string Value { get; set; }
public bool IsAutoload { get; set; }
}
Create a dictionary..
Dictionary<string, Option> WP_Options = new Dictionary<string, Option>();
List<Option> options = context.Wp_Options.Select(r => new Option()
{
Id = r.option_id,
Name = r.option_name,
Value = r.option_value,
IsAutoload = r.option_autoload == "yes"
}; // store records into a list
foreach(Option option in options)
{
WP_Options.Add(option.Name, option); // Store to dictionary
}
You can now access your options like:
Option siteUrl = WP_Options["siteurl"];
var val = siteUrl.Value;
bool autoload = siteUrl.IsAutoload;
If you are familiar with singleton classes then I'd suggest creating one that exposes the dictionary WP_Options. With this, you can access the same instance of the WP_Options across your application.
You'd just have to handle the option saving to the database.
Here's a little sample:
foreach(KeyValuePair<string, Option> entry in WP_Options)
{
if(context.Wp_Options.FirstOrDefault(o => o.Name == entry.Value) != null)
{
// Entry exists do an update logic
}
else
{
// Entry does not exist do an insert logic
}
}
// save data context

Serializing "string list" to JSON in C#

(I'v restated my question here: Creating class instances based on dynamic item lists)
I'm currently working on a program in Visual Studio 2015 with C#.
I have 5 list strings that contain data that I wish to serialize to a json file.
public List<string> name { get; private set; }
public List<string> userImageURL { get; private set; }
public List<string> nickname { get; private set; }
public List<string> info { get; private set; }
public List<string> available { get; private set; }
An example of the desired json file format is the fallowing:
{
"users" :
[
{
"name" : "name1",
"userImageURL" : "userImageURL1",
"nickname" : "nickname1",
"info" : "info1",
"available" : false,
},
{
"name" : "name2",
"userImageURL" : "userImageURL2",
"nickname" : "nickname2",
"info" : "info2",
"available" : false,
},
{
"name" : "name3",
"userImageURL" : "userImageURL3",
"nickname" : "nickname3",
"info" : "info3",
"available" : false,
},
{
"name" : "name4",
"userImageURL" : "userImageURL4",
"nickname" : "nickname4",
"info" : "info4",
"available" : false,
}
]
}
Note that there might be errors in the json example above.
I've tried combining the 5 lists to create 1 list to serialize it using the following code:
users = new List<string>(name.Count + userImageURL.Count + nickname.Count + info.Count + available.Count);
allPlayers.AddRange(name);
allPlayers.AddRange(userImageURL);
allPlayers.AddRange(nickname);
allPlayers.AddRange(info);
allPlayers.AddRange(available);
Then I serialize the list with the fallowing code:
string data = JsonConvert.SerializeObject(users);
File.WriteAllText("data.json", data);
This just creates an array of unorganized objects. I wish to know how can I organize them as expressed in the format above.
PS: I'm pretty new to coding as you can tell. Sorry if I'm not expressing the question correctly or using the right terminology. Also, this is not the original code. The code creates this lists which I wish to serialize into a json file.
PSS: This data is collected using HtmlAgilityPack. I asked a question yesterday asking how could I parse an html file and serialize it's data to a json file. Using HtmlAgilityPack to get specific data in C# and serialize it to json . As nobody answered, I decided to try and do it myself. The method that I used may not be the best, but it is what I could do with the knowledge that I have.
I would suggest refactoring your code to start with - instead of having 5 "parallel collections", have a single collection of a new type, User:
public class User
{
public string Name { get; set; }
public string ImageUrl { get; set; }
public string NickName { get; set; }
public string Info { get; set; }
public bool Available { get; set; }
}
...
// In your containing type
public List<User> Users { get; set; }
This is likely to make life simpler not just for your JSON, but for the rest of the code too - because you no longer have the possibility of having more nicknames than image URLs, etc. In general, having multiple collections that must be kept in sync with each other is an antipattern. There are times where it's appropriate - typically providing different efficient ways of retrieving the same data - but for something like this it's best avoided.
It is actually what Jon says, but to move from your parallel lists to Jon's single list you need something like (assuming all lists have the same number of elements in the same order):
Users = new List<User>();
for (var i = 0; i < name.Count; i++)
{
Users.Add(new User
{
Available = available[i],
ImageUrl = userImageURL[i],
Info = info[i],
Name = name[i],
NickName = nickname[i]
});
}
And then serialise the Users list.

Categories