Trying to use the serializer of YAMLDOTNET, having some problems when I have and object which is not composed of just strings but also has an special object inside.
When serializing I will just get a {} string. If for example on the Serializing an object graph sample we define a structure Address. Then we create a new object of the class Address inside, which is eventually assign in the receipt, the results will be also a {} on the address field on the yaml file.
The sample code can be also seen here. This will create an output that looks like:
receipt: Oz-Ware Purchase Invoice
date: 2007-08-06T00:00:00.0000000
customer:
given: Dorothy
family: Gale
items:
- part_no: A4786
descrip: Water Bucket (Filled)
price: 1.47
quantity: 4
- part_no: E1628
descrip: High Heeled "Ruby" Slippers
price: 100.27
quantity: 1
bill_to: &o0 {}
ship_to: *o0
So the bill_to will appear as {}
YamlDotNet.Serialization.Serializer does not serialise fields into the YAML output. It works in the example, because that is using a dynamic object and street, city and state are properties of that object.
If you change the fields in your Address to properties they will be serialised e.g.
public struct Address
{
public string street { get; set; }
public string city { get; set; }
public string state { get; set; }
}
Using properties instead of public fields is also best practice.
Related
I have a Azure Table that is storing a Customer object with a nested address object as per following.
public class Customer {
public Guid Id { get; set; }
public string Name { get; set; }
public Address Address { get; set; }
}
public class Address {
public string AddressLine1 { get; set; }
public string AddressLine2 { get; set; }
public string City { get; set; }
public string Postcode { get; set; }
}
The Customer object gets stored in a Azure Table with columns like this:
Id
Name
Address_AddressLine1
Address_AddressLine2
Address_City
Address_Postcode
Child object gets flattened and gets columns at the same level as Table Storage doesn't support nested objects.
I want to migrate this to Cosmos DB SQL API. What's the best way to migrate this data so that I end up with a nested json document instead of a flat one with these underscore columns?
I want to migrate this data so that it looks something like this in Cosmos:
{
Id: 2fca57ec-8c13-4f2c-81c7-d6b649ca6296,
Name: "John Smith",
Address: {
AddressLine1: '123 Street',
AddressLine2: '',
City: 'City',
Postcode: '1234'
}
}
I have tried using Cosmos Data Migration tool (deprecated?) and Azure Data factory but couldn't figure out how to convert the Address_* columns to a nested Address object instead of ending up as flat attributes in the json document.
Is there a way to easily map it to a nested child object Or will I have to write custom code to do the migration?
Unfortunately, there is no out of the box solution for this kind of migration.
Easier option would be to write custom code to loop through the TableEntities, construct the document object and add the item to your Cosmos container.
There is no straightforward solution offered by Microsoft (Azure Storage Explorer) to overcome this challenge but leveraging a third-party tool like Cerebrata (https://cerebrata.com/) could help you migrate your data from Azure Table Storage to Cosmos DB SQL API in a simple copy/paste model.
This way you can also avoid spending a good amount of time on custom coding and also view your migrated data in a table format rather than a complicated JSON format.
Disclaimer: It’s purely based on my experience
I have a collection where I am storing the timestamp and its latest location with the following class:
public class TrackingInfo
{
[JsonProperty("id")]
public string Id { get; set; }
[JsonProperty("_partition_key")]
public string _PartitionKey { get; set; }
[JsonProperty("asset_id")]
public string AssetId { get; set; }
[JsonProperty("unix_timestamp")]
public double UnixTimestamp { get; set; }
[JsonProperty("timestamp")]
public string Timestamp { get; set; }
[JsonProperty("location")]
public Point Location { get; set; }
}
which is partitioned by _PartitionKey which contains a construct like this:
tracking._PartitionKey = $"tracking_{tracking.AssetId.ToLower()}_{DateTime.Today.ToString("D")}";
Looks like there is no way to do a Group by on the collection.
Can someone please help me create a SQL document query to find the latest entry for each AssetId and its Location and Timnestamp when the data was recorded.
Update 1:
what if I change the _PartitionKey to represent per day something like below:
tracking._PartitionKey = $"tracking_{DateTime.Today.ToString("D")}";
would it make it easier to get all assets and its latest tracking record?
As per my comment, my suggestion would be to solve your problem differently.
Assumption: You have a large number of assetIds and don't know the values beforehand:
Have one document that represents the latest state of your asset
Have another document that represents the location events of your asset
Update the first document whenever there is a new location event
You can put both types of documents in the same collection or separate them - both approaches have benefits. I would probably separate them.
Then do a query "what assets are within 1km of xxx" (Querying spatial types)
Sidenote: It might be a good idea to use the assetId as partitionKey instead of your combined key. Using such a key is very bad for queries
If you only have very few assetIds, you can use those to only find the latest updates by using and ordering by the timestamp field. This will only return the last item
Cosmos DB doesn't support group by feature,you could vote up this.
Provide a third-party package [documentdb-lumenize for your reference which supports group by feature,it has .net example:
string configString = #"{
cubeConfig: {
groupBy: 'state',
field: 'points',
f: 'sum'
},
filterQuery: 'SELECT * FROM c'
}";
Object config = JsonConvert.DeserializeObject<Object>(configString);
dynamic result = await client.ExecuteStoredProcedureAsync<dynamic>("dbs/db1/colls/coll1/sprocs/cube", config);
Console.WriteLine(result.Response);
You could group by assetId column and get the max timestamp.
I am evaluating Azure Search for a project. The MSDN articles are having only Flattened schema structure. Below is an example scenario i am looking at.
The below is "Project" class having reference to List of "Question" class. And "Question" has it's own set of fields
public class Project
{
public Guid Id
{
get;
set;
}
public string Owner
{
get;
set;
}
public string Title
{
get;
set;
}
public List<Question> QuestionList
{
get;
set;
}
public bool Disable
{
get;
set;
}
}
public class Question
{
public Guid Id { get; set; }
public string Title { get; set; }
public string Description { get; set; }
}
Below will be Index Schema for Project
Id - Edm.String
Owner - Edm.String
Title - Edm.String
QuestionList - Collection(Edm.String)
Questions
Is it possible to specify each item in QuestionList has hierarchical data?
Is it possible to Search only inside "Title" of "Question"?
The only possibility i see is to create Index for "Question" separately and use it
The only point i see in MSDN relevant is the below paragraph and i can't make much sense of it
Levels in faceted navigation
As noted, there is no direct support for nesting facets in a
hierarchy. Out of the box, faceted navigation only supports one level
of filters. However, workarounds do exist. You can encode a
hierarchical facet structure in a Collection(Edm.String) with one
entry point per hierarchy. Implementing this workaround is beyond the
scope of this article, but you can read about collections in OData by
Example.
For your first question, Azure Search does not allow for hierarchical datatypes, and to search you would need to flatten the data as you did for the QuestionList field which you created as a Collection. If you were asking how to also filter results based on items in this Collection, you can do that using OData Expressions such as $filter=QuestionList/any(t: t eq 'Question1') (https://msdn.microsoft.com/en-us/library/azure/dn798921.aspx)
I think for your second question, you were interested searching only in "Title" or "Question", correct? For this, you can use the SearchFields parameter (https://msdn.microsoft.com/en-us/library/azure/dn798927.aspx).
Liam
I have a DTO that goes something like this:
public class Request {
public id ASpecificIdentifier { get; set; }
public string PreciseDescription { get; set; }
public string FirstPartOfSomeonesName { get; set; }
}
Whilst I'm happy accepting this as the official 'Input', I would also like to be able to bind them to multiple keys. The reason for this will be serialized to a client in an encrypted JSON object, so I would like to keep the length down.
For example they should also be able to pass:
{
"Id":1,
"Desc":"My Issue",
"Name":"Bob"
}
How can I achieve this? I have looked around at the Attributes supplied but non seem to be able to allow this behaviour?
You could create multiple DTOs for each scenario you allow (set of named parameters). Then in your service you would have to handle each DTO and translate them a common DTO to take action.
Or Another way would be to have a DTO that takes a generic key/value parameter. This will make your DTOs very flexible but you will lose some of the advantages of strong typing.
For Example:
public class Request {
Dictionary<string,string> Properties { get; set; }
}
In an application we have modelled Company as an Entity and Address as a value object:
public class Company : Entity {
public Address PrimaryAddress { get; set; }
public Address SecondaryAddress { get; set; }
}
public class Address : ValueObject {
public string ZipCode { get; private set; } // etc.
public Address(string zipCode) {
ZipCode = zipCode;
}
}
Here, Address does not have an identity and is immutable. To update the PrimaryAddress of a company I replace it with a new Address object.
However, after further discovery into the domain we found that a company may have a variable number of addresses. Representing them as individual properties was no longer feasible. So we refactored Company:
public class Company : Entity {
public Address[] AddressBook { get; set; }
}
We now have a problem of how to update a company's Address. We now care about an address' identity, but only within the context of a Company.
After refactoring we end up with:
public class Company : Entity {
public Address[] AddressBook { get; set; }
public void UpdateAddress(Address newAddress) {
var oldAddress = AddressBook.FirstOrDefault(a => a.Id == newAddress.Id);
if (oldAddress != null)
oldAddress = newAddress;
}
}
public class Address : ValueObject {
public Guid Id { get; private set; }
public string ZipCode { get; private set; } // etc.
public Address(Guid id, string zipCode) {
ZipCode = zipCode;
Id = id;
}
}
// usage
var company = repo.Load<Company>(companyId);
var address = new Address(model.Id, "12345"); // uses id of address we are replacing
company.UpdateAddress(address);
repo.Save(company);
To update an address we locate it using its Id and replace it with a new address object with the same Id.
Therefore is Address still a value object?
Am I right to use the same Id for the updated Address, or by definition (as a value object) should we replace the Id.
If the Address only has identity within the context of the Company entity, should we make this explicit in our model, perhaps generating an Id that is only unique inside the entity (perhaps using the entity's id as part of the calculation)?
Therefore is Address still a value object?
I'd say no, as soon as you give the Address an identity, it ceases being a value object. It means that you stop caring only about its attributes and start giving importance to the Address life cycle in the application, tracking changes in its state, etc.
Am I right to use the same Id for the updated Address, or by
definition (as a value object) should we replace the Id.
I wouldn't reuse an Address Id. From a domain standpoint, when a company moves, it's not the address that changes per se. There will still be a valid street number and buildings at that address. The address defined by that identity still has a reality, and you could even imagine reusing it for some other company. Therefore, when a company moves, it's only the association between Company and Address that needs to change to point to an new Address with a new Id.
If the Address only has identity within the context of the Company
entity, should we make this explicit in our model, perhaps generating
an Id that is only unique inside the entity (perhaps using the
entity's id as part of the calculation)?
Just because you feel the need to single out different addresses inside a Company doesn't mean you have to give them an Id and make them Entities rather than value objects. For instance, if you want to rank addresses inside a Company, define a primary Address, etc. you can perfectly do that with value objects. The address value objects will remain the same, you can just have an indexed collection in the Company, a PrimaryAddress field, and so on.