Is there a way to add additional information for blob in Azure?
I want to store some relevant information, which connects the blob to other entity in in an a document database, for example a string which contains a JSON.
I know there is metadata for blob when I use Azure Storage explorer, but i want use it from code.
This a relevant question about this theme:
Adding Description/Metadata to Azure Blob
And how can retrieve the blobs based on this metadata?
Have you checked this link?
public static async Task AddContainerMetadataAsync(CloudBlobContainer container)
{
// Add some metadata to the container.
container.Metadata.Add("docType", "textDocuments");
container.Metadata["category"] = "guidance";
// Set the container's metadata.
await container.SetMetadataAsync();
}
Keep in mind that
The name of your metadata must conform to the naming conventions for C# identifiers.
The first part of the question is answered by Mihail Stancescu, thank you!
The second part is not answered correctly yet. The Azure Search is a solution for it, but it is a totally other service. I want to solve this problem in my repository class. And i solved it.
Maybe it is interesting for someone else that is why I share my solution:
Behind the solution
There is a metadata in AzureBlob, which has a string type. I serialized a object to String and store this string in metadata. When I need this information in any cases, I listing the with metadata in it. I reach this this functionality to passing the Microsoft.WindowsAzure.Storage.Blob.BlobListingDetails.Metadata value to the blobListingDetails parameter in ListBlobs function.
When The blobs are arrived, I inmediatly Deserialized back from JSON to object. This mechasim is visible in LINQ Select:
.Select<Microsoft.WindowsAzure.Storage.Blob.CloudBlockBlob, T>(blob = > JsonConvert.DeserializeObject<T>(blob.Metadata["data"]))
After this, the LINQ type is T, so I can Apply the Expression on it in LINQ Where.
The complete solution is:
GetMany function
public IEnumerable<T> GetMany( Expression<Func<T, bool>> filter )
{
return _AzureBlobCollection.BlobDirectory
.ListBlobs( useFlatBlobListing: false, blobListingDetails: Microsoft.WindowsAzure.Storage.Blob.BlobListingDetails.Metadata )
.OfType<Microsoft.WindowsAzure.Storage.Blob.CloudBlockBlob>()
.Select<Microsoft.WindowsAzure.Storage.Blob.CloudBlockBlob, T>( blob => JsonConvert.DeserializeObject<T>( blob.Metadata[ "data" ] ) )
.Where( filter.Compile() );
}
This function can call like this:
Repository repository = ..
IEnumerable files = repository.GetMany( f => f.Partner = "Microsoft" );
Base classes
where file class is:
public class ContractFile : File
{
public string Partner { get; set; }
public Date CreationDate { get; set; }
public string Remarks { get; set; }
public string Filename { get; set; }
}
...
public class File
{
public String File { get; set; }
public Stream Data { get; set; }
}
And the insert is following:
public void AddOne( T file )
{
file.id = Guid.NewGuid().ToString();
Microsoft.WindowsAzure.Storage.Blob.CloudBlockBlob blob = _AzureBlobCollection.BlobDirectory.GetBlockBlobReference( file.id );
blob.UploadFromStream( file.Data );
blob.Metadata.Add( "data", JsonConvert.SerializeObject( file ) );
blob.SetMetadata();
}
Related
How Can i OutPut the published Content of a Certain Document Type via Web Api?
Example:
I have a Document Type Called
Comment
its has three Properties "Name, Date, Text"
I Want To output the Values of those Properties to a UmbracoApiController So that I can Use it in other WebSites
any thoughts ? Thanks in Advance
public class publishedContentapiController : UmbracoApiController
{
//What Logic To Put Here In Order to get the Content OF published
// Document Types With the Alias "comment"
}
The below code outputs all documents of type "comment" through the webapi
public class publishedContentapiController : UmbracoApiController
{
public IHttpActionResult GetComments()
{
// Create an UmbracoHelper for retrieving published content
var umbracoHelper = new UmbracoHelper(UmbracoContext.Current);
// Get all comments from the Umbraco tree (this is not a optimized way of doing this, since it queries the complete Umbraco tree)
var comments = umbracoHelper.TypedContentAtRoot().DescendantsOrSelf("comment");
// Map the found nodes from IPublishedContent to a strongly typed object of type Comment (defined below)
var mappedComments = comments.Select(x => new Comment{
Name = x.Name, // Map name of the document
Date = x.CreatedTime, // Map createdtime
Text = x.GetPropertyValue<string>("text") // Map custom property "text"
});
return Ok(mappedComments);
}
private class Comment
{
public string Name { get; set; }
public DateTime Date { get; set; }
public string Text { get; set; }
}
}
Diclaimer: Code is untested and obviously needs refactoring
I am working on an Azure Mobile Apps project. Where I have to define a Table Controller with that can accept two parameters and give a list of values. I have a DataObject for ProductItem, which is
public class ProductItem : EntityData
{
public string Name { get; set; }
public string Details { get; set; }
public double Price { get; set; }
public string Image { get; set; }
public Merchant Merchant { get; set; }
}
I need to get a specific Product item, filter by its Price and Merchant. Already in the ProductItemContoller, I have scaffolded
// GET tables/ProductItem
public IQueryable<ProductItem> GetAllProductItems()
{
return Query();
}
// GET tables/ProductItem/48D68C86-6EA6-4C25-AA33-223FC9A27959
public SingleResult<ProductItem> GetProductItem(string id)
{
return Lookup(id);
}
by looking at existing examples. But in examples, we have not called any of the given methods from Client. Rather, IEnumerable<ProductItem> items = await productTable.ToEnumerableAsync(); was called.
My question is why can't we call GetAllProductItems() which was already defined in the controller to the client. If we can call, how to do it.
And also, I need to have a controller method, I need to have a GetAllProductByMerchat(string merchantId). How can I make this possible.
The Table controllers are called automatically by the client SDKs on your behalf, allowing you to work with LINQ queries on the client. You can use something like:
var items = productTable.Where(p => p.Price < 100).ToListAsync();
This gets translated into an OData query across the wire, then translated back into a LINQ query on the server, where it then gets translated into SQL and executed on the SQL Azure instance.
For more information, see chapter 3 of http://aka.ms/zumobook
Did you mean this?
// Server method:
[HttpGet]
[Route("GetAllProductItems")]
public IQueryable<ProductItem> GetAllProductItems()
{
return Query();
}
// Client call
var result = await MobileService.InvokeApiAsync<IQueryable<ProductItem>>("ProductItem/GetAllProductItems", HttpMethod.Get, null);
Remember to add these attribute before the ProductItemController:
[MobileAppController]
[RoutePrefix("api/ProductItem")]
You can do the same thing to your GetAllProductByMerchat(string merchantId) method.
I use Searchblox to index and search my files, which itself calls ES 2.x to do the job. Searchblox uses a "mapping.json" file to initialize a mapping upon the creation of an index. Here's the link to that file. As "#Russ Cam" suggested here, I created my own class content with the following code (just like he did with the "questions" index and "Question" class):
public class Content
{
public string type { get; set; }
public Fields fields { get; set; }
}
public class Fields
{
public Content1 content { get; set; }
public Autocomplete autocomplete { get; set; }
}
public class Content1
{
public string type { get; set; }
public string store { get; set; }
public string index { get; set; }
public string analyzer { get; set; }
public string include_in_all { get; set; }
public string boost { get; set; }
} //got this with paste special->json class
These fields from the content class (type,store etc.) come from the mapping.json file attached above. Now, when I (just like you showed me) execute the following code:
var searchResponse = highLevelclient.Search<Content>(s => s.Query(q => q
.Match(m => m.Field(f => f.fields.content)
.Query("service")
All I get as a response on the searchResponse variable is:
Valid NEST response built from a successful low level call on POST: /idx014/content/_search
Audit trail of this API call:
-HealthyResponse: Node: http://localhost:9200/ Took: 00:00:00.7180404
Request:
{"query":{"match":{"fields.content":{"query":"service"}}}}
Response:
{"took":1,"timed_out":false,"_shards":{"total":5,"successful":5,"failed":0},"hits":{"total":0,"max_score":null,"hits":[]}}
And no documents in searchResponse.Documents. Contradictorily, when I search for the "service" query on Searchblox or make an API call to localhost:9200 with the Sense extension of Google Chrome, I get 2 documents. (the documents that I was looking for)
In brief, all I want is to be able to :
get all the documents (no criteria)
get all the documents within a time range and based upon keywords.. such as "service"
What am I doing wrong? I can provide with more information if needed.. Thank you all for your detailed answers.
Your C# POCO is not correct in regards to your mapping; your document type is "sdoc" and each of the properties under the "properties" property is a field on that document type; These fields map to properties on your C# POCO.
As an example to get you started
public class Document
{
[String(Name = "uid")]
public string UId { get; set; }
public string Content { get; set; }
}
NEST by default will camel case POCO property names, so "content" will be case correctly according to your mapping, however, we use attribute mapping for the "uid" field in order to name it to match the mapping (we can go further here and set additional attribute property values to fully match the mapping; see the automapping documentation).
Now, to search with the document, let's create the connection settings and a client to use
void Main()
{
var pool = new SingleNodeConnectionPool(new Uri("http://localhost:9200"));
var connectionSettings = new ConnectionSettings(pool)
.InferMappingFor<Document>(t => t
// change the index name to the name of your index :)
.IndexName("index-name")
.TypeName("sdoc")
.IdProperty(p => p.UId)
);
var client = new ElasticClient(connectionSettings);
// do something with the response
var searchResponse = client.Search<Document>(s => s
.Query(q => q
.Match(m => m
.Field(f => f.Content)
.Query("service")
)
)
);
}
We set up the client with some inference rules for the Document type which will be used when interacting with Elasticsearch. The above query emits the following query json
{
"query": {
"match": {
"content": {
"query": "service"
}
}
}
}
As an aside, I noticed that the mapping contained a multi_field type; multi_field types were removed in Elasticsearch 1.0 (multi fields are still there, just the actual type is not), so be sure that you're actually running Elasticsearch 2.x on Searchblox, as NEST 2.x is only supported against Elasticsearch 2.x.
I am going crazy... I am missing something and I can't see what?!?!
I have created a property called "GAME_SETTINGS" inside the gameSparks admin area and have included this in it:
{
"AppVersionIOS": 1,
"AppVersionAndroid": 1
}
I am then trying to retrieve it inside Unity like this:
new GameSparks.Api.Requests.GetPropertyRequest().SetPropertyShortCode("GAME_SETTINGS").Send((response) => {
if (!response.HasErrors) {
Debug.Log("Setting Achieved: "+response.JSONString);
} else {
Debug.Log("Error Getting Settings");
}
});
I can see that I am getting the settings in my Debug.Log:
Setting Achieved: {"#class":".GetPropertyResponse","property":{"AppVersionIOS":1,"AppVersionAndroid":1},"requestId":"XXXXXXXXXXXXXXX","scriptData":null}
My question is though... How do I get the properties AppVersionIOS and AppVersionAndroid inside an Dictionary so I can call on them from other scripts...
Really hoping for help in this matter and thanks in advance :-)
I actually work for GameSparks and noticed your question so set up an account to answer you.
The property values returned in the JSON are of nullable type : https://msdn.microsoft.com/en-us/library/1t3y8s4s.aspx
Best practice is to parse the values before they are cached in a Dictionary or otherwise.
The following code should allow you to get those properties, then you may store them in a dictionary as you see fit.
public void GetProperties()
{
new GameSparks.Api.Requests.GetPropertyRequest()
.SetPropertyShortCode("GAME_SETTINGS")
.Send((response) =>
{
if (!response.HasErrors)
{
print(response.JSONString);
int androidProperty = (int)response.Property.GetInt("AppVersionAndroid");
int IOSProperty = (int)response.Property.GetInt("AppVersionIOS");
print("AndroidProperty:" + androidProperty);
print("IOSProperty:" + IOSProperty);
}
else
{
Debug.LogWarning(response.JSONString);
}
});
}
Hopefully this solves your problem. If you have any other questions please feel free to head to our website and log a ticket with us.
Regards, Patrick.
Notice: This answer assumes that the API doesn't have a way of converting this into a nice object which you can easily manipulate / parse, so it converts it itself using some class. It's however very likely that your API offers such a function somewhere, so you'd be better be looking in the documentation again. I guess it's somewhere near https://api.gamesparks.net/#getpropertyrequest .
You have the JSON document already, all you have to do is parse it. That'd be easier in a JavaScript file than in C#, but you can also use the JsonUtils class there, see http://docs.unity3d.com/Manual/JSONSerialization.html . Let http://json2csharp.com/ convert that JSON to a class layout for you and you get
public class Property
{
public int AppVersionIOS { get; set; }
public int AppVersionAndroid { get; set; }
}
public class RootObject
{
public string __invalid_name__#class { get; set; }
public Property property { get; set; }
public string requestId { get; set; }
public object scriptData { get; set; }
}
Now just take the string and serialize it into an RootObject.
new GameSparks.Api.Requests.GetPropertyRequest().SetPropertyShortCode("GAME_SETTINGS").Send((response) => {
if (!response.HasErrors) {
Debug.Log("Setting Achieved: "+response.JSONString);
//Serialization
var info = JsonUtility.FromJson<RootObject>(response.JSONString);
//Print the AppVersionIOS property
Debug.Log("App Version iOS: " + info.Property.AppVersionIOS);
} else {
Debug.Log("Error Getting Settings");
}
});
You might need some mofication in the data types of your class (e.g. make object scriptData to string scriptData if there can be an actual string in it), but that should be it. Have fun.
I'm working in a ASP MVC + Mongodb App, so I want to save files inside a specific document through a List. I'm using GridFs Upload Method which returns a MongoGridFSFileInfo (The type of the List in my Model) so I get the reference to the object returned and Add it to the List --so far everything seems fine-- but when I try to save the changes in the collection I get this exception "The specified method is not supported" and the File won't save into the document.
This is my Model
public class Document
{
[BsonRepresentation(BsonType.ObjectId)]
public string Id { get; set; }
public string Name { get; set; }
public string Description { get; set; }
public List<MongoGridFSFileInfo> FsFileInfos = new List<MongoGridFSFileInfo>();
public Document()
{
}
}
And Here is the Method where 1) Add a File to a List of Document Collection 2) Try to save the changes in the collection.
[HttpPost]
public ActionResult PrepareToAttach(string id, string description, HttpPostedFileBase raw_file)
{
Document doc = Context.FindById(id);
var options = new MongoGridFSCreateOptions
{
ContentType = raw_file.ContentType
};
var mongoFileInfo = Context._db.GridFS.Upload(raw_file.InputStream, raw_file.FileName,options);
doc.FsFileInfos.Add(mongoFileInfo);
Context.Documents.Save(doc);
return RedirectToAction("Index", "Document");
}
The exception is displayed in Context.Documents.Save(doc); --> The specified method is not supported
MongoGridFSFileInfo is not a POCO class. What I mean by that is that it has many properties on it that either aren't persistable or shouldn't be.
The only thing you actually need to persist to get back a document is the unique identifier for the file. You may want to persist some other meta-data as well, but only the identifier is required.