Xamarin Forms Plugin.CloudFirestore - How to Handle Firestore Map Fields and Firestore Map Arrays with Reactive - google-cloud-firestore

I am currently using the Plugin.CloudFirestore.Sample project (link: https://github.com/f-miyu/Plugin.CloudFirestore) as a reference for creating my own application and would like to know how I should code my project to upload documents containing Firestore Maps and Firestore Arrays that have nested Firestore Maps then listen for document changes and display these changes using Xamarin Forms, Plugin.CloudFirestore, and Reactive. I would also like to know the maximum amount of Map conversions that can be handled at a time while converting from a Firestore Document to a .Net Model Class. To clarify, if the ExampleItem Class mentioned below contains another Model Class as a property, how would the class and reactive class viewmodel code look to upload/listen for changes. If anyone knows the answer to my question, it would be awesome if you could download the Plugin.CloudFirestore.Sample project, make the changes to the file, share a response explaining what you added, and share a download link.
public class TodoItem
{
public static string CollectionPath = "todoItems";
//Should I use DocumentConverters for converting to the "DataMap" and "DataMapArray" properties? If so what would the code for them look like?
[Id]
public string? Id { get; set; }
public string? Name { get; set; }
public string? Notes { get; set; }
public ExampleItem1? DataMap { get; set; }
public ObservableCollection<ExampleItem1>? DataMapArray { get; set; }
[ServerTimestamp(CanReplace = false)]
public Timestamp CreatedAt { get; set; }
[ServerTimestamp]
public Timestamp UpdatedAt { get; set; }
}
public class ExampleItem1
{
// Should I use [Id] on the Id property or any other Plugin.CloudFirestore.Attributes?
public string? Id { get; set; }
public string? Name { get; set; }
public ExampleItem2? DataMap { get; set; }
public ObservableCollection<ExampleItem2>? DataMapArray { get; set; }
}
public class TodoItemViewModel : BindableBase
{
public string? Id { get; }
public ReactivePropertySlim<string?> Name { get; set; } = new ReactivePropertySlim<string?>();
public ReactivePropertySlim<string?> Notes { get; set; } = new ReactivePropertySlim<string?>();
public ReactivePropertySlim<ExampleItem?> DataMap { get; set; } = new ReactivePropertySlim<ExampleItem?>();
public ReactivePropertySlim<ObservableCollection<ExampleItem>?> DataMapArray { get; set; } = new ReactivePropertySlim<ObservableCollection<ExampleItem>?>();
public TodoItemViewModel(TodoItem item)
{
Id = item.Id;
Name.Value = item.Name;
Notes.Value = item.Notes;
DataMap.Value = item.DataMap;
DataMapArray.Value = item.DataMapArray;
}
public void Update(string? name, string? notes, ExampleItem1? dataMap, ObservableCollection<ExampleItem1>? dataMapArray)
{
Name.Value = name;
Notes.Value = notes;
DataMap.Value = dataMap;
DataMapArray.Value = dataMapArray;
}
}

Related

how to Pull data with paramter using Azure Offline Sync?

I have schema as below and I would like to know if I can get all the TaskCategoryMappings by CategoryId when I have to pulll all data. I went through the documentation here but I cant figure out how to do it ? All the examples are like this one based on UserId. but userid is used for also authentication and on the server side I already handle it fine to return only mappings belong to relevant user and i want in adition to filter by CategoryId?
another SO sample is here also using userId Parameter Passing with Azure Get Service
public class TaskCategoryMapping : TableData
{
public string TaskId { get; set; }
public string CategoryId { get; set; }
public string UserId { get; set; }
}
According to your description, I checked this issue on my side and found that it could work as expected, you could follow the details below to check your code:
Backend models:
public class Tag : EntityData
{
public string TagName { get; set; }
public bool Status { get; set; }
}
public class Message : EntityData
{
public string UserId { get; set; }
public string Text { get; set; }
public virtual Tag Tag { get; set; }
[ForeignKey("Tag")]
public string Tag_Id { get; set; }
}
GetAllMessage action:
// GET tables/Message
public IQueryable<Message> GetAllMessage()
{
return Query();
}
For the client, I just invoke the online table for retrieving the message entities as follows:
Model on client-side:
public class Message
{
public string Id { get; set; }
public string UserId { get; set; }
public string Text { get; set; }
public string Tag_Id { get; set; }
}
var result=await mobileServiceClient.GetTable<Message>().Where(msg => msg.Tag_Id == "c3cd4cf8-7af0-4267-817e-f84c6f0e1733").ToListAsync();
For offline table, the pull operation query would
await messageSyncTable.PullAsync($"messages_{userid}", messageSyncTable.Where(m => m.Tag_Id == "<Tag_Id>"));
Use fiddler, you could find that the request would look like this:
https://{your-app-name}.azurewebsites.net/tables/Message?$filter=Tag_Id eq 'c3cd4cf8-7af0-4267-817e-f84c6f0e1733'

Where should I do the mapping stuff? Repository or Service Layer?

Well, I have this DB Model "Book"
public class Book {
public int Id { get; set; }
public string Title { get; set; }
public string Author { get; set; }
public bool IsSubmitted { get; set; }
public bool IsCompleted { get; set; }
public bool IsDeleted { get; set; }
}
And I have implemented repository pattern where my GetBook(int id) method returns a Book which looks like this:
public Book GetBook(int id) {
return db.Books.Find(id);
}
However, my BookViewModel needs to query some other things as well. It looks like this:
public class BookViewModel
{
public int Id { get; set; }
public string Title { get; set; }
public string AuthorName { get; set; }
public int CommentsCount { get; set; }
public int FeedbacksCount { get; set; }
public int ViewsCount { get; set; }
}
Currently, my service layer is mapping binding models to DB models and passing them to repository.
Now my question is where should I query this additional (view-specific) data? Should I write separate repository methods for CommentsCount, FeedbacksCount, ViewsCount etc. and call them from my service layer to prepare my view model or should I write a new repository method with return type BookViewModel where I query all the required data in a single query?
Any help is highly appreciated.
The repository methods should map and return or recive DTO's, DAL layer should not know about MVC project, it should only know about DTO's.

If Exists Dont Add Data Entity Framework Many-To-Many

I have these two Models the logic is here One Post can have multiple Categories.
public class Post
{
public Post()
{
this.Categories = new HashSet<Category>();
}
public int Id { get; set; }
public string Title { get; set; }
public string Description { get; set; }
public string ShortDescription { get; set; }
public string PostImage { get; set; }
public string Thumbnail { get; set; }
public DateTime CreatedDate { get; set; }
public DateTime? PublishedDate { get; set; }
public string CreatedBy { get; set; }
public virtual ICollection<Category> Categories { get; set; }
}
public class Category
{
public int Id { get; set; }
public string Name { get; set; }
public virtual ICollection<Post> Posts { get; set; }
}
I have three static categories.
When I am trying to add new post its multiplexing CategoryTable creating new categories with same name ,And Mapping Them in to CategoryPostsTable.
The problem is here i want to map that data with existing categories. I dont want to add new category with same name.
I am using Repository Pattern how should i control that ? Is EF has some solution for that ?
I assume you have code like:
var post = new Post();
post.Categories.Add(cat);
context.Posts.Add(post);
...where cat is a Category object representing an existing category.
The latter Add method (DbSet.Add) doesn't only mark the received entity as Added but all entities in its object graph that are not attached to the context. So cat will also be marked as Added if it wasn't attached yet.
What you can do is
context.Entry(cat).State = System.Data.Entity.EntityState.Unchanged;
Now EF will only create the association to the category, but not insert a new category.

.Net MVC 4 REST Cannot send Object

I have build a .Net Mvc 4 application and now I want to extend it with REST.
I am using the Entity Framework and I have the following problem.
My goal is to have a system where categories have a number of products and where products can belong to multiple categories.
As follows:
public class Categorie
{
[Key]
public int Id { get; set; }
[Required]
public string Naam { get; set; }
[Required]
public string Omschrijving { get; set; }
public byte[] Plaatje { get; set; }
private List<Product> producten;
public virtual List<Product> Producten
{
get { return producten; }
set { producten = value; }
}
}
public class Product
{
[Key]
public int Id { get; set; }
[Required]
public string Naam { get; set; }
[Required]
public string Omschrijving { get; set; }
[Required]
public double Prijs { get; set; }
private List<Categorie> categorien = new List<Categorie>();
public virtual List<Categorie> Categorien
{
get { return categorien; }
set { categorien = value; }
}
[Required]
public byte[] Plaatje { get; set; }
}
NOTE: There are virtual properties in there so that my entity framework creates a merging table. Normally it links all the categorie's to the products and vice versa.
And my rest looks like:
// GET api/Rest/5
public Product GetProduct(int id)
{
Product product = db.Producten.Find(id);
Product newProduct = new Product();
if (product == null)
{
throw new HttpResponseException(Request.CreateResponse(HttpStatusCode.NotFound));
}
else
{
product.Categorien = null;
}
newProduct.Id = product.Id;
newProduct.Naam = product.Naam;
newProduct.Omschrijving = product.Omschrijving;
newProduct.Plaatje = product.Plaatje;
newProduct.Prijs = product.Prijs;
newProduct.Categorien = product.Categorien;
return newProduct;
}
First problem: I cannot send any product aslong as it has a categorie. I have to make it null.
Second problem: I cannot send the original product because of the first problem.
I am assuming your problem is with a circular reference during serialization, since categories reference multiple products and products reference multiple categories. One solution is to use Data Transfer Objects (DTO) instead of returning the straight entities you are using for EF. To make it easy to map your entities to the DTO's I would use AutoMapper. This is essentially what you are doing when you create an instance of newProduct in your REST API method, but AutoMapper takes the hard coding and drudgery out of mapping. Your DTO for a product would look very similar but they would not have the virtual navigation properties or the attributes needed by EF. A DTO for a product would look something like this.
public class Categorie
{
public int Id { get; set; }
public string Naam { get; set; }
public string Omschrijving { get; set; }
public byte[] Plaatje { get; set; }
}
public class Product
{
public int Id { get; set; }
public string Naam { get; set; }
public string Omschrijving { get; set; }
public double Prijs { get; set; }
public List<Categorie> categorien = new List<Categorie>();
public List<Categorie> Categorien
{
get { return categorien; }
set { categorien = value; }
}
public byte[] Plaatje { get; set; }
}
Notice that the DTO for Categorie does not contain a list of products, since in this case you want a listing of products. If you keep the field names the same for your DTO's as your entities AutoMapper will handle the mapping automatically. I usually keep the same class name for the DTO's and just distinguish them from the entities by having a different namespace. Your REST API method would look something like this.
// GET api/Rest/5
public Product GetProduct(int id)
{
Product product = db.Producten.Find(id);
return Mapper.Map<Product, Dto.Product>(product);
}

Eager loading including navigational property of derived class

Sample class structure
class Order
{
public int Id { get; set; }
public DateTime Date { get; set; }
public List<OrderDetail> Details { get; set; }
}
class OrderDetail
{
public int Id { get; set; }
public int Qty { get; set; }
public Item Item { get; set; }
}
class Item
{
public int Id { get; set; }
public string Name { get; set; }
}
class ElectronicItem : Item
{
public MoreDetail Detail { get; set; }
}
class MoreDetail
{
public int Id { get; set; }
public string SomeData { get; set; }
}
In order to populate order object with all navigational properties, I wrote
context.Orders.Include("Details").Include("Details.Item")
I also want to load MoreDetail object, hence I tried
context.Orders.Include("Details").Include("Details.Item.Detail")
It didn't work. How to load complete Order object?
It is currently not possible but it is feature requested by community on User DataVoice as you already found. There is also related bug on MS Connect.
You simply cannot eager load navigation properties of derived types but you can load them with separate query:
var moreDetails = context.MoreDetails;
EF should automatically fix your navigation properties. If you use filtering on orders in your original query you must apply that filter in more details query as well:
var moreDetails = cotnext.MoreDetials.Where(m => m.Item.Order ....);