I want to serialize data on my .net Standard app into a local file and I would like to avoid sqlite if possible.
The standard recommendation for cross plattform app seems to have been PCL Storage, but according to this link, PCL Storage is not maintained anymore, offers no .net Standard support and the alternative PCLExt is not mature.
Can you tell me if it is possible to simply serialize my data e.g. with json?
Tank you very much!
You do not have complete access over the OS's filesystem and platform-specific features like Android's ContentResolver, but for basic file read/write within your app's sandbox (or external filesystem if your app has access to it) .NetStandard 2.0 works fine, and thus works for storing and retrieving text-based files for serializing/deserializing Json.
Example, if you have a Xamarin.Forms based solution and add a .NetStandard 2.0 library project to the solution and also add Newtonsoft.Json to it. You could create these functions in it:
.NetStandard library functions:
public static class Common
{
public static void WriteFile(string fileName, Tuple<string, string> obj)
{
var path = Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments);
File.WriteAllText(Path.Combine(path, fileName), JsonConvert.SerializeObject(obj));
}
public static Tuple<string, string> ReadFile(string fileName)
{
var path = Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments);
return JsonConvert.DeserializeObject<Tuple<string, string>>(File.ReadAllText(Path.Combine(path, fileName));
}
}
Now in your Xamarin.Forms project (.NetStandard * Shared project), reference the project/library you created you could do something like this:
ReadWriteCommand = new Command(() =>
{
var someObj = Tuple.Create("Stack", "Overflow");
Common.WriteFile("SushiHangover.txt", someObj);
var readSomeObj = Common.ReadFile("SushiHangover.txt");
if ((someObj.Item1 != readSomeObj.Item1) || (someObj.Item2 != readSomeObj.Item2))
throw new Exception();
});
Related
I have a Revit plug-in, I want it to do some operations on a workshared cloud model.
I can't figure out how to get the Revit model as a Document class (Autodesk.Revit.DB)
which is stored live on BIM360 cloud, not a local copy, nor downloaded copy.
Seems like I have to use different API's and there are multiple steps to this although
I was expecting something relatively simpler, I quickly realised this actually may have
multiple steps which I honestly can't figure out.
Is there a working relevant code example on git hub for this ?
Edit: I was able to find the below code but it doesn't compile
because ForgeClient and OSSObjectsApi doesn't exist in the latest
forge sdk package, how can I fix that ?
using System;
using System.IO;
using System.Threading.Tasks;
using Autodesk.Forge;
using Autodesk.Forge.Model;
using Autodesk.Forge.Client;
using Newtonsoft.Json.Linq;
namespace BIM360Downloader
{
class Program
{
static void Main(string[] args)
{
// These are the client ID and client secret that you obtained
// when you registered your application on the Forge developer portal.
string clientId = "YOUR_CLIENT_ID";
string clientSecret = "YOUR_CLIENT_SECRET";
// Replace these with the project ID and file ID of the model you want to download.
string projectId = "YOUR_PROJECT_ID";
string fileId = "YOUR_FILE_ID";
// Create a new Forge API client.
ForgeClient client = new ForgeClient(clientId, clientSecret);
// Get the access token for the client.
TwoLeggedApi oauth = new TwoLeggedApi();
dynamic token = oauth.Authenticate(clientId, clientSecret, "client_credentials", new Scope[] { Scope.DataRead });
string accessToken = token.access_token;
// Set the bearer token for the client.
client.Configuration.AccessToken = accessToken;
// Download the model from BIM 360.
MemoryStream modelStream = DownloadModelAsync(client, projectId, fileId).Result;
Console.WriteLine("Successfully downloaded model to memory stream.");
}
static async Task<MemoryStream> DownloadModelAsync(ForgeClient client, string projectId, string fileId)
{
// Set up the request to download the model.
OSSObjectsApi objectsApi = new OSSObjectsApi();
dynamic objectDetails = await objectsApi.GetObjectDetailsAsync(projectId, fileId);
string bucketKey = objectDetails.bucketKey;
// Download the model data.
dynamic data = await objectsApi.GetObjectAsync(bucketKey, fileId);
byte[] modelData = data.Body;
// Create a new MemoryStream object to store the model data.
MemoryStream modelStream = new MemoryStream(modelData);
return modelStream;
}
}
}
To open a live Revit Could Model (RCM), you can use ModelPathUtils.ConvertCloudGUIDsToCloudPath() to convert the project and model guid into a ModelPath. You can then use this ModelPath to open the document using Application.OpenDocumentFile() method.
Also read the sections Getting the CloudPath for a Model and SaveAsCloudModel Information from the Web Browser in this link on how to find the account, project, and model guids of the model you are interested in.
var cloudModelPath = ModelPathUtils.ConvertCloudGUIDsToCloudPath(region, projectGuid, modelGuid);
Document doc = app.OpenDocumentFile(cloudModelPath, new OpenOptions());
This code will work in a desktop addin as long as a valid user is logged in and that user has access to the input model. While you have not explicitly mentioned that you need this to work on Design Automation for Revit, you have added the tag #autodesk-designautomation to your question. The good news is the same code above should work for Design Automation addin (app bundle), but there is an extra step on how to provide user context to the design automation job. Please refer to this blog post and github sample for Design Automation for RCM.
I'm having a solution with an ASP.NET Core 6 MVC application project and a WebJob (console application)
Both applications are using a common library project where I have IRazorViewToStringRenderer service with views. I want to reuse this service in both applications, WebApp and WebJob. My solution is based on this sample https://github.com/aspnet/Entropy/blob/master/samples/Mvc.RenderViewToString/Program.cs
Here is how I use it:
var viewToStringEngine = ServiceProvider.GetService<IRazorViewToStringRenderer>();
string htmlContent = await viewToStringEngine.RenderToStringAsync<MyView>("~/Views/MyView.cshtml", new MyView());
The problem is RazorViewEngineOptions doesn't have anymore the option to specify the file provider ( in ASP.NET Core 6 )
services.Configure<RazorViewEngineOptions>(options =>
{
options.FileProviders.Clear();
options.FileProviders.Add(fileProvider);
});
IRazorViewToStringRenderer service is working fine when is called from the Web App, but is not working from the WebJob. It is only working if the WebJob services contains an IWebHostEnvironment with the ApplicationName as the name of the project where IRazorViewToStringRenderer is implemented, otherwise the views cannot be found.
How to specify file provider for the RazorViewEngine ? ( github sample )
WebJob service configuration:
private static ServiceCollection ConfigureServices()
{
var services = new ServiceCollection();
services.AddSingleton<IConfiguration>(Configuration);
var applicationEnvironment = PlatformServices.Default.Application;
services.AddSingleton(applicationEnvironment);
services.AddSingleton<Microsoft.AspNetCore.Hosting.IWebHostEnvironment>(new WebJobHostEnvironment
{
ApplicationName = Assembly.GetEntryAssembly().GetName().Name,
//ApplicationName = typeof(IRazorViewToStringRenderer).Assembly.GetName().Name,
});
var listener = new DiagnosticListener("Microsoft.AspNetCore");
services.AddSingleton<DiagnosticListener>(listener);
services.AddSingleton<DiagnosticSource>(listener);
services.AddSingleton<ObjectPoolProvider, DefaultObjectPoolProvider>();
services.AddSingleton<ILoggerFactory, LoggerFactory>(sp => new LoggerFactory());
services.AddMvcCore().AddRazorViewEngine();
services.AddCommonRazorEngine(Configuration);
return services;
}
RazorServiceCollectionExtension.cs
public static class RazorServiceCollectionExtension
{
public static void AddCommonRazorEngine(this IServiceCollection services, IConfiguration configuration)
{
//var fileProvider = new EmbeddedFileProvider(typeof(RazorViewToStringRenderer).Assembly);
// FileProviders property is not available anymore
services.Configure<RazorViewEngineOptions>(options =>
{
//options.FileProviders.Add(fileProvider);
});
services.AddScoped<IRazorViewToStringRenderer, RazorViewToStringRenderer>();
}
}
Edit
For others searching a similar solution, I updated my github sample
I encountered this problem myself, and it appears that the functionality has been moved to an external package. I was able to work around this by following the instructions located here and then amending them for my purposes:
https://learn.microsoft.com/en-us/aspnet/core/mvc/views/view-compilation?view=aspnetcore-6.0&tabs=visual-studio
I.e. install the package: Microsoft.AspNetCore.Mvc.Razor.RuntimeCompilation
Then you can change the appropriate file provider by using the code:
services.Configure<MvcRazorRuntimeCompilationOptions>(options =>
{
options.FileProviders.Clear();
options.FileProviders.Add(fileProvider);
});
I am looking for existing solutions to match dynamic parameters with HttpCore. What I have in mind is something similar to constraints in ruby on rails, or dynamic parameters with sails (see here for example).
My objective is to define a REST API where I could easily match requests like GET /objects/<object_id>.
To give a little bit of context, I have an application that creates an HttpServer using the following code
server = ServerBootstrap.bootstrap()
.setListenerPort(port)
.setServerInfo("MyAppServer/1.1")
.setSocketConfig(socketConfig)
.registerHandler("*", new HttpHandler(this))
.create();
And the HttpHandler class that matches the requested URI and dispatches it to the corresponding backend method:
public void handle(final HttpRequest request, final HttpResponse response, final HttpContext context) {
String method = request.getRequestLine().getMethod().toUpperCase(Locale.ROOT);
// Parameters are ignored for the example
String path = request.getRequestLine().getUri();
if(method.equals("POST") && path.equals("/object/add") {
if(request instanceof HttpEntityEnclosingRequest) {
addObject(((HttpEntityEnclosingRequest)request).getEntity())
}
[...]
For sure I can replace path.equals("/object/add") by something more sophisticated with RegEx to match these dynamic parameters, but before doing so I'd like to know if I am not reinventing the wheel, or if there is an existing lib/class I didn't see in the docs that could help me.
Using HttpCore is a requirement (it is already integrated in the application I am working on), I know some other libraries provide high-level routing mechanisms that support these dynamic parameters, but I can't really afford switching the entire server code to another library.
I am currently using httpcore 4.4.10, but I can upgrade to a newer version of this might help me.
At present HttpCore does not have a fully featured request routing layer. (The reasons for that are more political than technical).
Consider using a custom HttpRequestHandlerMapper to implement your application specific request routing logic.
final HttpServer server = ServerBootstrap.bootstrap()
.setListenerPort(port)
.setServerInfo("Test/1.1")
.setSocketConfig(socketConfig)
.setSslContext(sslContext)
.setHandlerMapper(new HttpRequestHandlerMapper() {
#Override
public HttpRequestHandler lookup(HttpRequest request) {
try {
URI uri = new URI(request.getRequestLine().getUri());
String path = uri.getPath();
// do request routing based on the request path
return new HttpFileHandler(docRoot);
} catch (URISyntaxException e) {
// Provide a more reasonable error handler here
return null;
}
}
})
.setExceptionLogger(new StdErrorExceptionLogger())
.create();
In a Windows Store app I can only store WinRT types in the ApplicationSettings, according to the documentation. For roamed settings that should be held together I can use ApplicationDataCompositeValue. Trying to store an instance of an own class or struct results in an Exception with the message " WinRT information: Error trying to serialize the value to be written to the application data store. Additional Information: Data of this type is not supported". The term "trying to serialize" indicates that there must be some way so serialize a type for the application data API.
Does anyone know how I could achieve that?
I tried DataContract serialization but it did not work.
I think custom/own types are not supported.
See http://msdn.microsoft.com/en-us/library/windows/apps/hh464917.aspx:
"The Windows Runtime data types are supported for app settings."
But you can serialize your objects to XML and save as string... (see code below)
public static string Serialize(object obj)
{
using (var sw = new StringWriter())
{
var serializer = new XmlSerializer(obj.GetType());
serializer.Serialize(sw, obj);
return sw.ToString();
}
}
public static T Deserialize<T>(string xml)
{
using (var sw = new StringReader(xml))
{
var serializer = new XmlSerializer(typeof(T));
return (T)serializer.Deserialize(sw);
}
}
https://github.com/MyToolkit/MyToolkit/blob/master/src/MyToolkit/Serialization/XmlSerialization.cs
Check out this class too:
https://github.com/MyToolkit/MyToolkit/wiki/XmlSerialization
Disclaimer: The above links are from my project
When you call RazorEngine.Razor.Compile(), where is the compiled template stored?
Is it available after the programs been restarted? If there is a memory shortage, will it be dumped?
I am using RazorEngine in an ASP.NET (MVC) project. Will the precompiled templates be available after the application restarts?
Would it make more sense for me to store them in the HttpContext.Cache?
If I did, then would it make more sense to use a different function (other than Compile) that bypasses the internal cache? Is there a way to execute an ITemplate and just pass it a model?
Does RazorEngine.Razor.Parse() do any caching? Or is the template recompiled each time?
Currently, after the RazorEngine compiles the templates, they are loaded into memory. These assemblies persist in memory only and do not continue beyond the lifetime of the application.
I am considering adding in support for compiling these assemblies to files, but that'll be a future version.
If you call Razor.Parse and pass in a name for the template, it will attempt to
Check the cache of in-memory assemblies for an assembly with the same name.
Invalid the cache of the content of the template has changed.
Cache the newly compiled template.
I've got this to work with RazorEngine 3.4.1.0, installed late Jan 2014.
The key is to call the expensive Razor.Compile(content, name) to put the template into cache, then call the cheap Razor.Run(name, model) to execute the template.
Remember that reading template content might be expensive -- say, involving a read from disk -- so my solution only gets template content once. This might be too much caching for you, so careful!
Here's the RenderPartial method I use inside a custom TemplateBase<T> subclass. It runs very quickly for multiple calls to the same template.
public abstract class SqlTemplate<T>: TemplateBase<T>
{
public string RenderPartial(string templateName, object model = null)
{
// loading a template might be expensive, so be careful to cache content
if (Razor.Resolve(templateName) == null)
{
// we've never seen this template before, so compile it and stick it in cache.
var templateContent = GetTemplateContent(templateName);
Razor.Compile(templateContent, templateName);
}
// by now, we know we've got a the template cached and ready to run; this is fast
var renderedContent = Razor.Run(templateName, model);
return renderedContent;
}
private string GetTemplateContent(string templateName)
{
... your implementation here
}
}
You also need to tell Razor to use this base class (SqlTempalte<T>) which you can do like this, by calling RazorEngineConfigurator.Configure();
public static class RazorEngineConfigurator
{
private static bool configured = false;
public static void Configure()
{
if (configured)
{
return;
}
var templateConfig = new TemplateServiceConfiguration
{
BaseTemplateType = typeof(SqlTemplate<>),
EncodedStringFactory = new RazorEngine.Text.RawStringFactory()
};
RazorEngine.Razor.SetTemplateService(new TemplateService(templateConfig));
configured = true;
}
}
Couldn't have done it without this SO answer -- why not give that one an up-vote, too? :)
Edit - If you need to perform caching in a more granular way, you'll need to use a different approach using RazorEngineTemplateService and ITemplateResolver.
Here's a piece of starter code;
public static RazorEngineTemplateService CreateService(ITemplateResolver resolver, ICollection<string> namespaces)
{
Check.IsNotNull(resolver, "resolver");
var config = new TemplateServiceConfiguration();
config.BaseTemplateType = typeof(PlainTextTemplate<>);
config.EncodedStringFactory = new RazorEngine.Text.RawStringFactory();
config.Resolver = resolver;
config.Namespaces = new HashSet<string>(namespaces);
var service = new RazorEngineTemplateService(config);
return service;
}
ITemplateResolver turns template names into template contents, so you can implement, eg, a CachedFileTemplateResolver which loads cached content from disk.