Akavache not storing/returning a NodaTime LocalDateTime - serialization

I need to store a NodaTime LocalDateTime in an Akavache cache.
I've created a simple app which takes the following class and stores/retrieves it in/from an Akavache cache:
public class TestModel
{
public string Name { get; set; }
public LocalDateTime StartDateTimeLocal {get; set;}
public DateTime StartDateTimeUtc {get;set;}
}
When this is stored in and retrieved from the cache, the StartDateTimeLocal property hasn't been populated.
It seems that Akavache isn't aware of how to serialise/deserialize a LocalDateTime.
Is it possible to register types with Akavache or supply a custom serialisation for unknown types?
Console application to demonstrate it:
using Akavache;
using NodaTime;
using System;
using System.Reactive.Linq;
namespace AkavacheNodaTimeCore
{
class Program
{
static TestModel BeforeModel;
static TestModel AfterModel;
static void Main(string[] args)
{
// Note that we're using Akavache 6.0.27, to match the version we're using in our live system.
BlobCache.ApplicationName = "AkavacheNodaTimeCore";
BlobCache.EnsureInitialized();
BeforeModel = new TestModel()
{
StartLocalDateTime = LocalDateTime.FromDateTime(DateTime.Now),
StartDateTime = DateTime.UtcNow,
};
Console.WriteLine($"Before:LocalDateTime='{BeforeModel.StartLocalDateTime}' DateTime='{BeforeModel.StartDateTime}'");
CycleTheModels();
Console.WriteLine($"After: LocalDateTime='{AfterModel.StartLocalDateTime}' DateTime='{AfterModel.StartDateTime}'");
Console.WriteLine("Note that Akavache retrieves DateTimes as DateTimeKind.Local, so DateTime before and after above will differ.");
Console.WriteLine("Press any key to continue.");
var y = Console.ReadKey();
}
/// <summary>
/// Puts a model into Akavache and retrieves a new one so we can compare.
/// </summary>
static async void CycleTheModels()
{
await BlobCache.InMemory.Invalidate("model");
await BlobCache.InMemory.InsertObject("model", BeforeModel);
AfterModel = await BlobCache.InMemory.GetObject<TestModel>("model");
}
}
}
TestModel class:
using NodaTime;
using System;
namespace AkavacheNodaTimeCore
{
public class TestModel
{
public string Name { get; set; }
public LocalDateTime StartLocalDateTime { get; set; }
public DateTime StartDateTime {get;set;}
}
}
I have added a Git repo with the above in a console application which demonstrates the problem.

You need to configure the JsonSerializerSettings that Akavache uses with Json.NET. You'll need a reference to NodaTime.Serialization.JsonNet, at which point you can create a serializer settings instance, configure it for Noda Time, then add that as a dependency in Splat (which Akavache uses). I haven't used Splat before, so it's possible that this isn't the right way of doing it, but it works with your example:
using Newtonsoft.Json;
using NodaTime.Serialization.JsonNet;
using Splat;
...
// This should be before any of your other code.
var settings = new JsonSerializerSettings();
settings.ConfigureForNodaTime(DateTimeZoneProviders.Tzdb);
Locator.CurrentMutable.RegisterConstant(settings, typeof(JsonSerializerSettings));
It may be worth filing in issue in the Akavache repo to request more documentation for customization of serialization settings - the above works, but was guesswork and a little bit of source code investigation.

Related

How to add new language to ABP template?

I'm using free boilerplate (ASP.NET Core MVC & jQuery) from this site https://aspnetboilerplate.com/Templates
Is it possible to add new language support?
I already add localized .xml file, update 'abplanguages' table in database but it is not working. I'm changing language but text is still in english. The same situation with predefined languages already shipped with boilerplate like 'espanol-mexico' is not working but when I pick 'french' the page is translated.
This is weird because in documentation said it can be done.
https://aspnetboilerplate.com/Pages/Documents/Localization#extending-localization-sources
I wonder is it free template restriction?
inject IApplicationLanguageManager interface and use AddAsync() method to add a new language.
private readonly IApplicationLanguageManager _applicationLanguageManager;
public LanguageAppService(
IApplicationLanguageManager applicationLanguageManager,
IApplicationLanguageTextManager applicationLanguageTextManager,
IRepository<ApplicationLanguage> languageRepository)
{
_applicationLanguageManager = applicationLanguageManager;
_languageRepository = languageRepository;
_applicationLanguageTextManager = applicationLanguageTextManager;
}
protected virtual async Task CreateLanguageAsync(ApplicationLanguageEditDto input)
{
if (AbpSession.MultiTenancySide != MultiTenancySides.Host)
{
throw new UserFriendlyException(L("TenantsCannotCreateLanguage"));
}
var culture = CultureHelper.GetCultureInfoByChecking(input.Name);
await _applicationLanguageManager.AddAsync(
new ApplicationLanguage(
AbpSession.TenantId,
culture.Name,
culture.DisplayName,
input.Icon
)
{
IsDisabled = !input.IsEnabled
}
);
}
public static class CultureHelper
{
public static CultureInfo[] AllCultures = CultureInfo.GetCultures(CultureTypes.AllCultures);
public static bool IsRtl => CultureInfo.CurrentUICulture.TextInfo.IsRightToLeft;
public static bool UsingLunarCalendar = CultureInfo.CurrentUICulture.DateTimeFormat.Calendar.AlgorithmType == CalendarAlgorithmType.LunarCalendar;
public static CultureInfo GetCultureInfoByChecking(string name)
{
try
{
return CultureInfo.GetCultureInfo(name);
}
catch (CultureNotFoundException)
{
return CultureInfo.CurrentCulture;
}
}
}
public class ApplicationLanguageEditDto
{
public virtual int? Id { get; set; }
[Required]
[StringLength(ApplicationLanguage.MaxNameLength)]
public virtual string Name { get; set; }
[StringLength(ApplicationLanguage.MaxIconLength)]
public virtual string Icon { get; set; }
/// <summary>
/// Mapped from Language.IsDisabled with using manual mapping in CustomDtoMapper.cs
/// </summary>
public bool IsEnabled { get; set; }
}
I figure it out. In my case it was incorrect build action property. In VS right click on localization source file: *.xml file -> Advanced -> Build action: Embedded resource.

Cannot create a DbSet for 'Model' because this type is not included in the model for the context

I do a Generic and using DI
so I create a empty class
public class DBRepo
{
}
and my model class to inheriting class DBRepo
public partial class UserAccount : DBRepo
{
public int Id { get; set; }
public string Account { get; set; }
public string Pwd { get; set; }
}
then this is a Interface to do CRUD
public interface IDBAction<TEntity> where TEntity : class,new()
{
void UpdateData(TEntity _entity);
void GetAllData(TEntity _entity);
}
public class DBService<TEntity> : IDBAction<TEntity> where TEntity : class,new()
{
private readonly CoreContext _db;
public DBService(CoreContext _db)
{
this._db = _db;
}
public void UpdateData(TEntity _entity)
{
this._db.Set<TEntity>().UpdateRange(_entity);
this._db.SaveChanges();
}
public void GetAllData(TEntity _entity)
{
var x = this._db.Set<TEntity>().Select(o => o).ToList();
}
}
And I Dependency Injection Service Provider in constructor
this.DBProvider = new ServiceCollection()
.AddScoped<IDBAction<DBRepo>, DBService<DBRepo>>()
.AddScoped<DBContext>()
.AddDbContext<CoreContext>(options => options.UseSqlServer(ConnectionString))
.BuildServiceProvider();
last step I Get Services
DBProvider.GetService<IDBAction<DBRepo>>().GetAllData(new UserAccount());
I will get a error message same with title
or I change to
DBProvider.GetService<IDBAction<UserAccount>>().GetAllData(new UserAccount());
I'll get other message
Object reference not set to an instance of an object.'
but the void UpdateData() is can work,
so how to fix GetAllData() problem?
The error simply is because the class you're using here UserAccount has apparently not been added to your context, CoreContext. There should be a property there like:
public DbSet<UserAccount> UserAccounts { get; set; }
Regardless of whether you end up using the generic Set<T> accessor, you still must defined a DbSet for the entity on your context.
That said, you should absolutely not be creating your own service collection inside your repo. Register your context and your repo with the main service collection in Startup.cs and then simply inject your repo where you need it. The DI framework will take care of instantiating it with your context, as long as you have a constructor that takes your context (which you seem to).
And that said, you should ditch the repo entirely. It still requires a dependency on Entity Framework and doesn't do anything but proxy to Entity Framework methods. This is just an extra thing you have to maintain and test with no added benefit.

Protobuf-net / NetCore2: Deserialization ignores annotated private fields

Edit: The problem was with Nancy. Protobuf-net (de)serializes marked private fields just fine.
I am running a NetCore 2.0 unit test project. Protobuf-net appears to be ignored private fields even though the have the [ProtoMember] attribute.
[ProtoContract]
internal class Model
{
[ProtoMember(1)]
public int Example { get; private set; } // Works
[ProtoMember(2)]
private List<int> _a; // Not deserialized unless made public
public IEnumerable<int> A => this._a;
public Model(int example, IEnumerable<int> a)
{
this.Example = example;
this._a = a.ToList(); // Copy prevents mutation
}
private Model() // For deserialization
{
}
}
I have used a public IEnumerable<int> to avoid mutability and hide implementation details. It is backed by a private List<int> to allow serialization. However, protobuf-net will only deserialize the field if I make it public. The serialization, on the other hand, will actually include the data even if the field is private.
Is this intended behavior? Is there are a clean way to make protobuf-net honor the marked private field when deserializing?
P.S. The same behavior is seen for non-collection members, but I have demonstrated with IEnumerable/List because it shows the reason for this approach.
The following works identically (apart from the first line of the output) when targetting netcoreapp2.0 or net45. I'd be happy to help, but I'd need to see an example that fails. I'm using:
<PackageReference Include="protobuf-net" Version="2.3.6" />
Code:
using System;
using System.Collections.Generic;
using System.Linq;
using ProtoBuf;
[ProtoContract]
internal class Model
{
[ProtoMember(1)]
public int Example { get; private set; } // Works
[ProtoMember(2)]
private List<int> _a; // Not deserialized unless made public
public IEnumerable<int> A => this._a;
public Model(int example, IEnumerable<int> a)
{
this.Example = example;
this._a = a.ToList(); // Copy prevents mutation
}
private Model() // For deserialization
{
}
}
static class Program
{
static void Main()
{
#if NETCOREAPP2_0
Console.WriteLine(".NET Core 2.0");
#elif NET45
Console.WriteLine(".NET 4.5");
#endif
var obj = new Model(123, new int[] { 4, 5, 6 });
var clone = Serializer.DeepClone(obj);
Console.WriteLine(clone.Example);
foreach (var val in clone.A)
{
Console.WriteLine(val);
}
}
}

How to write data to the Orchard CMS repository from a non HTTP thread

I have an Orchard CMS module that loads up some code which provides service functions. The service code is written to be host agnostic and has been used with ASP.NET and WCF previously. The service code uses MEF to load plugins. One such plugin is for audit.
In an attempt to allow access to the Orchard database for audit I have modified the service code to also allow the host to pass in an audit implementation instance. Thus my Orchard module can pass in an instance when the service starts with the intention that this instance writes audit data as records in the Orchard DB.
I have created a migration for my database:
public int UpdateFrom5()
{
SchemaBuilder.CreateTable("AuditRecord",
table => table
.Column<int>("Id", c => c.PrimaryKey().Identity())
.Column<int>("AuditPoint")
.Column<DateTime>("EventTime")
.Column("CampaignId", DbType.Guid)
.Column("CallId", DbType.Guid)
.Column<String>("Data")
);
return 6;
}
I have created my AuditRecord model in Models:
namespace MyModule.Models
{
public class AuditRecord
{
public virtual int Id { get; set; }
public virtual int AuditPoint { get; set; }
public virtual DateTime EventTime { get; set; }
public virtual Guid CampaignId { get; set; }
public virtual Guid CallId { get; set; }
public virtual String Data { get; set; }
}
}
I have added an IAuditWriter interface that derives from IDependency so that I can inject a new instance when my module starts.
public interface IAuditWriter : IDependency
{
void WriteAuditRecord(AuditRecord data);
}
For my audit writer instance to work with the existing service code it must be derived from an abstract class FlowSinkAudit defined in the service library. The abstract class defines the Audit method. When the service needs to write audit it calls the audit method on all instances derived from the FlowAuditSink abstract class that have been instantiated either through MEF or by passing in an instance at startup.
public class AuditWriter : FlowAuditSink, IAuditWriter
{
private readonly IComponentContext ctx;
private readonly IRepository<AuditRecord> repo;
public AuditWriter(IComponentContext ctx, IRepository<AuditRecord> repo)
{
this.ctx = ctx;
this.repo = repo;
}
public void WriteAuditRecord(AuditRecord data)
{
// Get an audit repo
//IRepository<AuditRecord> repo = (IRepository<AuditRecord>)ctx.Resolve(typeof(IRepository<AuditRecord>));
using (System.Transactions.TransactionScope t = new System.Transactions.TransactionScope(System.Transactions.TransactionScopeOption.Suppress))
{
this.repo.Create(data);
}
}
public override void Audit(DateTime eventTime, AuditPoint auditPoint, Guid campaignId, Guid callId, IDictionary<String, Object> auditPointData)
{
// Add code here to write audit into the Orchard DB.
AuditRecord ar = new AuditRecord();
ar.AuditPoint = (int)auditPoint;
ar.EventTime = eventTime;
ar.CampaignId = campaignId;
ar.CallId = callId;
ar.Data = auditPointData.AsString();
WriteAuditRecord(ar);
}
}
My service code is started from a module level class that implements IOrchardShellEvents
public class Module : IOrchardShellEvents
{
private readonly IAuditWriter audit;
private readonly IRepository<ServiceSettingsPartRecord> settingsRepository;
private readonly IScheduledTaskManager taskManager;
private static readonly Object syncObject = new object();
public ILogger logger { get; set; }
public Module(IScheduledTaskManager taskManager, IRepository<ServiceSettingsPartRecord> settingsRepository, IAuditWriter audit)
{
this.audit = audit;
this.settingsRepository = settingsRepository;
this.taskManager = taskManager;
logger = NullLogger.Instance;
}
...
When the service is started during the "Activated" event, I pass this.Audit to the service instance.
public void Activated()
{
lock (syncObject)
{
var settings = settingsRepository.Fetch(f => f.StorageProvider != null).FirstOrDefault();
InitialiseServer();
// Auto start the server
if (!StartServer(settings))
{
// Auto start failed, setup a scheduled task to retry
var tasks = taskManager.GetTasks(ServerAutostartTask.TaskType);
if (tasks == null || tasks.Count() == 0)
taskManager.CreateTask(ServerAutostartTask.TaskType, DateTime.Now + TimeSpan.FromSeconds(60), null);
}
}
}
...
private void InitialiseServer()
{
if (!Server.IsInitialized)
{
var systemFolder = #"C:\Scratch\Plugins";
if (!Directory.Exists(systemFolder))
Directory.CreateDirectory(systemFolder);
var cacheFolder = System.Web.Hosting.HostingEnvironment.MapPath("~/App_Data/MyModule/Cache");
if (!Directory.Exists(cacheFolder))
Directory.CreateDirectory(cacheFolder);
Server.Initialise(systemFolder, cacheFolder, null, (FlowAuditSink)audit);
}
}
All of this works as expected and my service code calls the audit sink.
My problem is that when the audit sink is called and I try to write the audit to the database using this.repo.Create(data) nothing is written.
I have also attempted to create a new repository object by using the IComponentContext interface but this errors with object already disposed. I assume this is because the audit sink is a long lived object instance.
I have attempted both with and without the current transaction suspended which doesn't affect the result. I assume this is because the call is not coming through ASP.NET MVC but from a thread created by the service code.
Can anyone tell my how I can get my audit data to appear in the Orchard database?
Thanks
Chris.
Well, I have a solution, but as I'm not very familiar with Orchards architecture it may not be the best way.
After a good deal of delving into the Orchard sources it struck me that the crux of this issue can be summarised as
"how do I access the Orchard autofac injection mechanism from a thread that does not use the Http request pipeline".
I figured that this is what a scheduled task must do so I created a scheduled task and set a breakpoint in IScheduledTaskHandler.Process to discover how the task was executed. Looking at Orchard\Tasks\SweepGenerator.cs showed me the way.
I modified my AuditWriter thusly:
public interface IAuditWriter : ISingletonDependency
{
}
public class AuditWriter : FlowAuditSink, IAuditWriter
{
private readonly IWorkContextAccessor _workContextAccessor;
public AuditWriter(IWorkContextAccessor workContextAccessor)
{
_workContextAccessor = workContextAccessor;
}
public override void Audit(DateTime eventTime, AuditPoint auditPoint, Guid campaignId, Guid callId, IDictionary<String, Object> auditPointData)
{
// Add code here to write audit into the Orchard DB.
AuditRecord ar = new AuditRecord();
ar.AuditPoint = (int)auditPoint;
ar.EventTime = eventTime;
ar.CampaignId = campaignId;
ar.CallId = callId;
ar.Data = auditPointData.AsString();
using (var scope = _workContextAccessor.CreateWorkContextScope())
{
// resolve the manager and invoke it
var repo = scope.Resolve<IRepository<AuditRecord>>();
repo.Create(ar);
repo.Flush();
}
}
}
scope.Resolve works and my data is successfully written to the Orchard DB.
At the moment, I don't think my use of ISingletonDependency is working correctly as my constructor is only called when my module injects an AuditWriter instance in its constructor and it happens more than once.
Anyway it seems that to gain access to the Orchard autofac resolution mechanism from a non Http thread we use IWorkContextAccessor
Please let me know if this is not correct.

Return Entity Framework objects over WCF

We have a problem concerning Entity Framework objects and sending them through WCF.
We have a database, and Entity Framework created classes from that database, a 'Wallet' class in this particular situation.
We try to transfer a Wallet using this code:
public Wallet getWallet()
{
Wallet w = new Wallet();
w.name = "myname";
w.walletID = 123;
return w;
}
We need to transfer that Wallet class, but it won't work, we always encounter the same exception:
"An error occurred while receiving the HTTP response to localhost:8860/ComplementaryCoins.svc. This could be due to the service endpoint binding not using the HTTP protocol. This could also be due to an HTTP request context being aborted by the server (possibly due to the service shutting down). See server logs for more details."
We searched on the internet, and there is a possibility that the error is due to the need of serialization of Entity Framework-objects.
We have absolutely no idea if this could be the case, and if this is the case, how to solve it.
Our DataContract looks like this (very simple):
[DataContract]
public partial class Wallet
{
[DataMember]
public int getwalletID { get { return walletID; } }
[DataMember]
public string getname { get { return name; } }
}
Does anyone ever encountered this problem?
EDIT: Our Entity Framework created class looks like this:
namespace ComplementaryCoins
{
using System;
using System.Collections.Generic;
public partial class Wallet
{
public Wallet()
{
this.Transaction = new HashSet<Transaction>();
this.Transaction1 = new HashSet<Transaction>();
this.User_Wallet = new HashSet<User_Wallet>();
this.Wallet_Item = new HashSet<Wallet_Item>();
}
public int walletID { get; set; }
public string name { get; set; }
public virtual ICollection<Transaction> Transaction { get; set; }
public virtual ICollection<Transaction> Transaction1 { get; set; }
public virtual ICollection<User_Wallet> User_Wallet { get; set; }
public virtual ICollection<Wallet_Item> Wallet_Item { get; set; }
}
}
Thanks for helping us.
I had the same problem some time ago and the solution for this was:
The entity framework was returning a serialized class instead of normal class.
eg. Wallet_asfawfklnaewfklawlfkawlfjlwfejlkef instead of Wallet
To solve that you can add this code:
base.Configuration.ProxyCreationEnabled = false;
in your Context file.
Since the context file is auto generated you can add it in the Context.tt
In the Context.tt file it can be added around lines 55-65:
<#=Accessibility.ForType(container)#> partial class <#=code.Escape(container)#> : DbContext
{
public <#=code.Escape(container)#>()
: base("name=<#=container.Name#>")
{
base.Configuration.ProxyCreationEnabled = false;
<#
if (!loader.IsLazyLoadingEnabled(container))
{
#>
this.Configuration.LazyLoadingEnabled = false;
<#
Try specifying a setter for the properties, something like this :
[DataContract]
public partial class Wallet
{
[DataMember]
public int getwalletID { get { return walletID; } set { } }
[DataMember]
public string getname { get { return name; } set { } }
}
If it still doesn't work, you may consider creating an intermediate POCO class for this purpose, and use mapper library like AutoMapper or ValueInjecter to transfer the data from the EF objects.
The POCO class should have same properties as your EF class :
[DataContract]
public class WalletDTO
{
[DataMember]
public int walletID { get; set; }
[DataMember]
public string name { get; set; }
}
And modify your method to return this class instead :
public WalletDTO getWallet()
{
Wallet w = new Wallet(); // or get it from db using EF
var dto = new WalletDTO();
//assuming we are using ValueInjecter, this code below will transfer all matched properties from w to dto
dto.InjectFrom(w);
return dto;
}
Are you trying to recieve a IEnumerable<Wallets>? If - yes, please modify your server class that returns the IEnumerable by adding .ToArray() method