Problems using sectioned Xamarin Forms ListView using ReactiveUI - xamarin.forms

I'm currently building a sectioned ListView in Xamarin forms where I want to dynamically update the list as the source observable changes.
The code below works until I emit a new set of deployments in the AllDeployments observable. Then I get an ArgumentOutOfBoundsException from the underlying list view trying to update itself.
I assume it's something about an unexpected change of rows within each section? Can anyone see what I've done wrong?
The same code works if I exchange SourceCache with SourceList and the appropriate Edit method.
class MockDeploymentService : IDeploymentsService
{
public IObservable<IEnumerable<Deployment>> AllDeployments {
get
{
DateTime date = DateTime.Now;
var deployments = new Deployment[]
{
new Deployment()
{
Id = "1",
Name = "One",
},
new Deployment()
{
Id = "2",
Name = "Two",
DeploymentDate = date
},
new Deployment()
{
Id = "3",
Name = "Three",
}
};
var deployments2 = new Deployment[]
{
new Deployment()
{
Id = "1",
Name = "One",
},
new Deployment()
{
Id = "2",
Name = "Two",
DeploymentDate = date
},
new Deployment()
{
Id = "3",
Name = "Three",
DeploymentDate = date
}
};
return Observable.Timer(DateTimeOffset.Now.AddSeconds(10))
.SelectMany(_ => Observable.Return(deployments2))
.StartWith(deployments);
}
}
}
public interface IDeploymentsService
{
IObservable<IEnumerable<Deployment>> AllDeployments { get; }
}
public class TableSection<T> : ObservableCollectionExtended<T>
{
public TableSection(string title, IEnumerable<T> items) : base(items)
{
Title = title;
}
public string Title { get; private set; }
}
public class DeploymentsPageViewModel : ViewModelBase
{
private readonly ISourceCache<DeploymentCellViewModel, string> _deploymentSourceCache;
public ReadOnlyObservableCollection<TableSection<DeploymentCellViewModel>> DeploymentViewModels { get; }
public DeploymentsPageViewModel(IDeploymentsService deploymentsService)
{
_deploymentSourceCache = new SourceCache<DeploymentCellViewModel, string>((x) => x.Deployment.Id);
deploymentsService.AllDeployments
.Do((IEnumerable<Deployment> deployments) =>
{
var oldItemIds = _deploymentSourceCache.Items.Select((x) => x.Deployment.Id);
var newItemIds = deployments.Select((x) => x.Id);
var deletedKeys = oldItemIds.Where((x) => !newItemIds.Contains(x));
_deploymentSourceCache.Edit((editableCache) =>
{
editableCache.RemoveKeys(deletedKeys);
editableCache.AddOrUpdate(TransformToViewModel(deployments));
});
})
.Subscribe()
.DisposeWith(subscriptionDisposables);
_deploymentSourceCache
.Connect()
// Hacky temporary way of grouping deployments
.GroupWithImmutableState((arg) => arg.Deployment.DeploymentDate == null ? "Not Deployed" : "Deployed")
.Transform((grouping) =>
{
return new TableSection<DeploymentCellViewModel>(grouping.Key, grouping.Items);
})
.Bind(out var deploymentViewModels)
.Subscribe()
.DisposeWith(subscriptionDisposables);
DeploymentViewModels = deploymentViewModels;
}
private IEnumerable<DeploymentCellViewModel> TransformToViewModel(IEnumerable<Deployment> deployments)
{
return deployments.Select((x) => new DeploymentCellViewModel(x));
}
}

Related

FindOneAndUpdateAsync Intermittently Returning Null

I am using MongoDB.Driver for .NET Core 3.1 and running into an issue were records are not being saved properly. They are intermittently coming back as null when calling FindOneAndUpdateAsync. I have a script that calls my below code 100 times. Out of those 100, 1-5 fail in the last method, SetChildFavoritesAsync. The results came back as null. Any suggestions on what I am doing wrong?
Example Calls
var id = 1;
var childName = "test";
var collectionEntry = await FindByIdOrCreateAsync(id);
collectionEntry.Children = new List<MyCollection.ChildClass>{
new MyCollection.ChildClass{
Name = childName,
Favorites = new List<MyCollection.ChildClass.Favorite>()
}
};
await FindByIdAndUpdateChildrenAsync(collectionEntry.Id, collectionEntry.Children);
var favorites = new List<MyCollection.ChildClass.Favorite>{
Name = "testFavorite"
};
var resultsOfSet = await SetChildFavoritesAsync(id, name, favorites)
//do stuff with resultsOfSet
Example Model
public class MyCollection
{
[MongoDB.Bson.Serialization.Attributes.BsonRepresentation(BsonType.ObjectId)]
[MongoDB.Bson.Serialization.Attributes.BsonId]
public string _Id { get; set; }
[MongoDB.Bson.Serialization.Attributes.BsonRequired]
public int Id { get; set; }
public List<ChildClass> Children { get; set; }
public class ChildClass
{
public string Name { get; set; }
public List<Favorite> Favorites { get; set; }
public class Favorite
{
public string Name { get; set; }
}
}
}
Example Methods
public async Task<MyCollection> FindByIdOrCreateAsync(int id)
{
var filter = Builders<MyCollection>.Filter.Eq(mc => mc.Id, id);
var update = Builders<MyCollection>.Update
.Set(mc => mc.Id, id)
.SetOnInsert(mc => mc.Children, new List<MyCollection.ChildClass>());
var options = new FindOneAndUpdateOptions<MyCollection> { ReturnDocument = ReturnDocument.After, IsUpsert = true };
return await _database.GetCollection<MyCollection>("MyCollectionName").FindOneAndUpdateAsync(filter, update, options);
}
public async Task<MyCollection> FindByIdAndUpdateChildrenAsync(int collectionId, List<MyCollection.ChildClass> children)
{
var filter = Builders<MyCollection>.Filter.Eq(mc => mc.Id, collectionId);
var update = Builders<MyCollection>.Update.Set(mc => mc.Children, children);
var options = new FindOneAndUpdateOptions<MyCollection> { ReturnDocument = ReturnDocument.After, IsUpsert = false };
return await _database.GetCollection<MyCollection>("MyCollectionName").FindOneAndUpdateAsync(filter, update, options);
}
public async Task<MyCollection> SetChildFavoritesAsync(int collectionId, string childName, List<MyCollection.ChildClass.Favorite> favorites)
{
var filter = Builders<MyCollection>.Filter.Eq(mc => mc.Id, collectionId);
filter &= Builders<MyCollection>.Filter.Eq("children.name", childName);
var update = Builders<MyCollection>.Update.Set("children.$.favorites", favorites);
var options = new FindOneAndUpdateOptions<MyCollection> { ReturnDocument = ReturnDocument.After };
var results = await _database.GetCollection<MyCollection>("MyCollectionName").FindOneAndUpdateAsync(filter, update, options);
if (results == null)
{
_log.Error($"Child Favorites didn't save: collectionId:{collectionId}, childName:{childName}");
}
else
{
_log.Debug($"Child Favorites: collectionId:{collectionId}, childName:{childName}, favorites:{Newtonsoft.Json.JsonConvert.SerializeObject(results)}");
}
return results;
}
Appears to be an issue with communication to the database. I added some retry logic, which solved the issue.

How to get a json data that model includes text json property in asp.net web api?

I have a database table named "application" in my postgresql database.
id name settings
----------------------------------------------------------------
1 x "{"color":"red", "left":"30px"}"
2 y "{"menuSize":"4", "menuOrientation":"horizontal"}"
my settings columnn has text type that includes json data as text format.
I am using this data in my asp.net web api application. The wab api can convert an object to a json data.
public class AppController : ApiController
{
App[] apps = new App[]
{
new App { Id = 1, Name = "x" },
new App { Id = 2, Name = "y" }
};
public IEnumerable<App> GetApps()
{
return apps;
}
}
But my model includes a string property that has a json formatted data.
public class AppController : ApiController
{
App[] apps = new App[]
{
new App { Id = 1, Name = "x", Settings = "{\"color\":\"red\", \"left\":\"30px\"}" }
};
public IEnumerable<App> GetApps()
{
return apps;
}
}
I want to get a json response like following:
[
{
id: 1,
name: "x",
color: "color",
left: "30px"
}
]
all columns are converted to a json format.
Use Newtonsoft library to parse json and then add new properties
public HttpResponseMessage GetApps()
JObject jsonObject = JObject.Parse("{\"color\":\"red\", \"left\":\"30px\"}");
jsonObject.Add("id", 1);
jsonObject.Add("name", x);
return new HttpResponseMessage {
Content = new StringContent(jsonObject.ToString(Formatting.None), Encoding.UTF8, "application/json"),
};
}
Try to use below code to return IEnumerable<JObject> since your keys in settings is dynamic.
public IEnumerable<JObject> GetApps()
{
var jsonList = new List<JObject>();
App[] apps = new App[]
{
new App { Id = 1, Name = "x", Settings = "{\"color\":\"red\", \"left\":\"30px\"}" },
new App { Id = 2, Name = "y", Settings = "{\"menuSize\":\"4\", \"menuOrientation\":\"horizontal\"}" }
};
foreach(var app in apps)
{
var obj = new JObject();
obj.Add("id", app.Id);
obj.Add("name", app.Name);
JObject settingsJsonObj = JObject.Parse(app.Settings);
foreach (var property in settingsJsonObj.Properties())
{
var name = property.Name;
obj.Add(name, settingsJsonObj.GetValue(name));
}
jsonList.Add(obj);
}
return jsonList;
}
Result:
[
{
"id": 1,
"name": "x",
"color": "red",
"left": "30px"
},
{
"id": 2,
"name": "y",
"menuSize": "4",
"menuOrientation": "horizontal"
}
]
If you use asp.net core 3.0, you need to add a package reference to Microsoft.AspNetCore.Mvc.NewtonsoftJson and update Startup.ConfigureServices to call AddNewtonsoftJson.
services.AddMvc().AddNewtonsoftJson();
Update:
Below is a demo to use custom json converter in asp.net core 3.0 with Newtonsoft.Json;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using System;
public class AppJsonConverter : JsonConverter
{
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
writer.WriteStartObject();
{
writer.WritePropertyName("Id");
writer.WriteValue(value.GetType().GetProperty("Id").GetValue(value).ToString());
writer.WritePropertyName("Name");
writer.WriteValue(value.GetType().GetProperty("Name").GetValue(value).ToString());
var settings = value.GetType().GetProperty("Settings").GetValue(value);
JObject settingsJsonObj = JObject.Parse(settings.ToString());
foreach (var property in settingsJsonObj.Properties())
{
var name = property.Name;
writer.WritePropertyName(name);
writer.WriteValue(settingsJsonObj.GetValue(name));
}
}
writer.WriteEndObject();
}
public override bool CanConvert(Type objectType)
{
throw new NotImplementedException();
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
throw new NotImplementedException();
}
}
Model:
[JsonConverter(typeof(AppJsonConverter))]
public class App
{
public int Id { get; set; }
public string Name { get; set; }
public string Settings { get; set; }
}
Controller:
//add `using Newtonsoft.Json;`
public IEnumerable<Object> GetApps()
{
App[] apps = new App[]
{
new App { Id = 1, Name = "x", Settings = "{\"color\":\"red\", \"left\":\"30px\"}" },
new App { Id = 2, Name = "y", Settings = "{\"menuSize\":\"4\", \"menuOrientation\":\"horizontal\"}" }
};
var jsonServices = JsonConvert.SerializeObject(apps);
var result = JsonConvert.DeserializeObject<List<Object>>(jsonServices);
return result;
}

Model returns null when the site first loads

So, I recently found quite an issue with my site: when it first loads, a section of the website is missing. After some tests, I found that this line was sometimes false: #if (Model != null && Model.Any()). After a test using a single Modal == null, I found that yes, the issue is that it's sometimes null. Also, I found that the best way for me to reproduce the issue (no error messages) is to restart visual studio. CTRL + F5 does not make it be null. Any ideas why is that ?
Here's the Model and the part of cshtml:
public class BlogModel
{
public int Id { get; set; }
public bool AfficheAuteur { get; set; }
public string Alias { get; set; }
public string Sujet { get; set; }
public string Auteur { get; set; }
public string Photo { get; set; }
public int? Ordre { get; set; }
public PostModel Post { get; set; }
}
public class PostModel
{
public int Id { get; set; }
public string Alias { get; set; }
public string Nom { get; set; }
}
//.cshtml:
#model IList<Project.Models.Shared.BlogModel>
//...
#if (Model != null && Model.Any())
//...
Note that I'm using asp.net Core MVC with razor.
Edit:
public static IList<BlogModel> GetBlogs()
{
var _lock = new object();
var strKey = string.Format("Home-Blogs-{0}", Site.Id);
var blogs = (IList<BlogModel>)CacheManager.Get(strKey);
if (blogs == null)
{
lock (_lock)
{
blogs = (IList<BlogModel>)CacheManager.Get(strKey);
if (blogs == null)
{
using (var context = new DB())
{
context.Configuration.LazyLoadingEnabled = false;
var nIdSite = Site.Id;
var bl = (from b in context.Blog
where b.Actif &&
(b.IdsSite.Contains("," + nIdSite + ",")) &&
b.Posts.Any(y => y.Publier)
orderby b.Ordre
select new BlogModel()
{
Id = b.Id,
AfficheAuteur = b.AfficherAuteur,
Alias = b.Alias,
Sujet = b.Sujet,
Photo = b.Image,
Auteur = b.User.Profile.FirstName + " " + b.User.Profile.LastName,
Ordre = b.Ordre,
Post = (from p in context.BlogPost
where p.Publier &&
p.IdBlog == b.Id &&
p.DateAffichage <= DateTime.Now
orderby p.DateAffichage descending
select new PostModel()
{
Id = p.Id,
Alias = p.Alias,
Nom = p.Nom
}).FirstOrDefault()
}).ToList();
CacheManager.Insert(strKey, bl, null, 10800, Cache.NoSlidingExpiration, CacheItemPriority.High, null);
return blogs;
}
}
}
}
return blogs;
}
public ActionResult Index(GridSettings settings, string strQuery)
{
var model = new IndexBlogViewModel(settings, blogService, strQuery);
ViewBag.FilAriane.Add(new KeyValuePair<string, string>(Url.Action("Index", "Home"), "Accueil"));
ViewBag.FilAriane.Add(new KeyValuePair<string, string>("", "Blogs"));
return View(model);
}
[HttpGet]
public ActionResult Create()
{
var model = new BlogFormViewModel { Blog = new Entitie.Blog { IdSite = IdSite } };
var lstUser = new List<User>();
var cfUserProvider = new CFUserProvider();
foreach (var mu in cfUserProvider.GetAllUsers().Cast<MembershipUser>())
{
var r = new CFRoleProvider();
if (r.IsUserInRole(mu.UserName, "Bloggeur"))
{
var u = new User { Username = mu.UserName, Id = Convert.ToInt32(mu.ProviderUserKey) };
lstUser.Add(u);
}
}
model.User = lstUser.Select(x => new SelectListItem
{
Text = x.Username,
Value = x.Id.ToString()
});
model.Sites = siteService.GetAll(x => x.IdEntreprise == IdEntreprise)
.Select(x => new CheckBoxListItem
{
Id = x.Id,
Display = x.Nom,
IsChecked = false
}).ToList();
ViewBag.FilAriane.Add(new KeyValuePair<string, string>(Url.Action("Index", "Home"), "Accueil"));
ViewBag.FilAriane.Add(new KeyValuePair<string, string>("", "Blog"));
return View(model);
}
Found it... It was checking for null and if it was, was adding it to cache but still returning the non-updated variable. Simply had to update it before returning...
Added:
blogs = (IList<BlogModel>)CacheManager.Get(strKey);
before returning.

Web API & entitiy framework how to work with posted json array

I try to write a post method for my asp.net web API.
The method shoulde be
1. receive Json formated datastring with have single objects and one sub array.
2. write it in my database in two tables with have a 1:n relation.
It would be wonderfull if anyone can help me.
I've no more idea how i can realise it
Example of the Json data:
[
{
"User":"testuser",
"CPGRP":21321321,
"Sex":"men",
"Name": "test",
"PointList":[
{
"Cost_I_Rea":"22202771.01",
"Cost_TV":"213213210.0" ,
"GRP":10,
"ID":0,
"ReichweiteID_F":1,
"RW_TV":"9.603",
"RW_Zuwgs":"9.603",
},
{
"Cost_I_Rea":"22202771.01",
"Cost_TV":"213213210.0" ,
"GRP":10,
"ID":0,
"ReichweiteID_F":1,
"RW_TV":"9.61103",
"RW_Zuwgs":"9.6043",
}
]
"Potenzial":213213,
"ReichweiteID":0,
"ZielGRP":21321321
}
]
This is my Post Method now, but it's generate a 500 Error:
// POST api/TVKurve
[ResponseType(typeof(Points))]
public async Task<IHttpActionResult> PostPoints(Points points)
{
//if (!ModelState.IsValid)
//{
// return BadRequest(ModelState);
//}
db.Points.Add(points);
await db.SaveChangesAsync();
db.Entry(points).Reference(x => x.Reichweite).Load();
var dto = new ReichweitePointsDto()
{
ReichweiteID = points.ReichweiteId,
Sex = points.Reichweite.Geschlecht,
Name = points.Reichweite.Name,
CPGRP = points.Reichweite.CpGRP,
Potenzial = points.Reichweite.Potenzial,
ZielGRP = points.Reichweite.ZielGRP,
User = points.Reichweite.Benutzer,
PointList = new List<PointListDto>(),
};
return CreatedAtRoute("DefaultApi", new { id = points.Id }, points);
}
and my data annotation Model:
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Linq;
using System.Web;
namespace xms_ef_vers1.Models
{
public class PointListDto
{
public PointListDto() { }
public PointListDto(Points item)
{
ID = item.Id;
GRP = item.GRP;
RW_TV = item.RW_TV;
Cost_TV = item.Cost_TV;
RW_Zuwgs = item.Rw_ZuWGS;
Cost_I_Rea = item.Cost_I_Rea;
ReichweiteID_F = item.ReichweiteId;
}
[Key]
public int ID { get; set;}
[Required]
public int GRP { get; set; }
public decimal RW_TV { get; set; }
public double Cost_TV { get; set; }
public decimal RW_Zuwgs { get; set; }
public decimal Cost_I_Rea { get; set; }
public int ReichweiteID_F { get; set; }
public Points ToEntity()
{
return new Points
{
Id = ID,
GRP = GRP,
RW_TV = RW_TV,
Cost_TV = Cost_TV,
Rw_ZuWGS = RW_Zuwgs,
Cost_I_Rea = Cost_I_Rea,
ReichweiteId = ReichweiteID_F,
};
}
}
}
Thank you for your answer.
I ve got it.
Look here:
public HttpResponseMessage PostReicheite(ReichweitePointsDto reichw)
{
if (ModelState.IsValid)
{
var reichwe = new Reichweite()
{
Geschlecht = reichw.Geschlecht,
Name = reichw.Name,
CpGRP = reichw.CPGRP,
Potenzial = reichw.Potenzial,
ZielGRP = reichw.ZielGRP,
Benutzer = reichw.Benutzer,
PointListe = (from item in reichw.PointListe
select new Points()
{
GRP = item.GRP,
RW_TV = item.RW_TV,
Cost_TV = item.Cost_TV,
Rw_ZuWGS = item.RW_Zuwgs,
Cost_I_Rea = item.Cost_I_Rea,
}).ToList()
};
db.Reichweites.Add(reichwe);
db.SaveChanges();
HttpResponseMessage response = Request.CreateResponse(HttpStatusCode.Created, reichwe);
response.Headers.Location = new Uri(Url.Link("DefaultAPI", new { id = reichwe.Id }));
return response;
}
else
{
return Request.CreateResponse(HttpStatusCode.BadRequest);
}
}
Now the Post Method do exactly what i need.
Only one bug, i've got an 500 status back can you say me why?
Best

How to use ASP.Net Web API to send a list of keys and return a list of key/values?

I'm new to REST services and have been working through the examples for ASP.Net Web API. What I would like to do is expand on this Get Method:
[ServiceBehavior(InstanceContextMode = InstanceContextMode.Single)]
public class ProductsController : ApiController
{
public IEnumerable<Product> GetAllProducts()
{
return new List<Product>
{
new Product() { Id = 1, Name = "Gizmo 1", Price = 1.99M },
new Product() { Id = 2, Name = "Gizmo 2", Price = 2.99M },
new Product() { Id = 3, Name = "Gizmo 3", Price = 3.99M }
};
} ...
To something where I send a list of products and all the prices are returned, in concept it would look like this:
public IEnumerable<Product> GetProducts(string[] ProductNames)
{
var pList = new List<Product>;
foreach (var s in ProductNames)
{
//Lookup price
var LookedupPrice = //get value from a data source
pList.Add(new Product() { Id = x, Name = s, Price = LookedUpPrice });
}
return pList;
}
Any ideas, and what would the REST call look like? I was thinking that I need to pass in a JSON object, but really am not sure.
With query string values, you can associate multiple values with a single field
public class ValuesController : ApiController
{
protected static IList<Product> productList;
static ValuesController()
{
productList = new List<Product>()
{
new Product() { Id = 1, Name = "Gizmo 1", Price = 1.99M },
new Product() { Id = 2, Name = "Gizmo 2", Price = 2.99M },
new Product() { Id = 3, Name = "Gizmo 3", Price = 3.99M }
};
}
public IEnumerable<Product> Get(IEnumerable<int> idList)
{
return productList;
}
}
with the default routes, you can now make a GET request to the following endpoint
/api/values/FilterList?idList=1&idList=2

Resources