Are there are any libraries that support user content localization in aspnetcore? - entity-framework

The are plenty of resources describing G11n an L10n in aspnetcore including the official docs.
But are there any libraries that simplify the implementation of a user content localization? An example could be a content of a blog post that may be translated into multiple languages. Such a library would use a specific table in SQL for storing/retrieving translation.
Here is the possible use case:
// this object contains content that user can add manually
public class BlogPost
{
// should be localised
public string Content { get; private set; }
}
It seems like we can add a collection of "string Content" in order to solve this issue:
public class LocalizableContent
{
public string CultureInfo { get; private set; }
public string Content { get; private set; }
}
public class BlogPost
{
public ICollection<LocalizableContent> Content { get; private set; }
}
Note: after making a bit of googling I found a related question (but it gives no answers):
Best Practices to localize entities with EF Code first | StackOverflow
Also, it doesn't seem like this library can help:
github.com/damienbod/AspNetCoreLocalization
Any suggestions?

I think you can implement your content localization library without the need to use third parties. Personaly I do something like below:
public class BlogPost {
public int Id { get; set; }
public ICollection<BloPostLocalized> Localizations { get; set; }
}
Create a localized class for BlogPost:
public class BlogPostLocalized {
public int Id { get; set; }
public BlogPostId { get; set; }
public BlogPost { get; set; }
public string Culture { get; set; }
public string Title { get; set; }
public string Content { get; set; }
}
Notice that the main BloPost has no Title nor Content fields, because we will have them defined in the BlogPostLocalized class for neutral and localized cultures.
So each blog post will have multiple localized versions that can be fetched simply from the db as a child of the main post.

Related

What Scaffolding Nuget package for child collections

I am using VS2015 and have MVC5 web app. I want to use scaffolding feature to generate CRUD for my child entities: currently when generating the scaffolding it is NOT creating the views/edit/create for the IEnumerable collection 'Cities'.
I have googled but not found anything. Is there a Nuget package that does what I want. It should allow to add/delete/edit the cities maybe using partial view but should be auto-generated.
code:
public partial class Country
{
public int ID { get; set; }
public string Name { get; set; }
public string Color { get; set; }
public string Location { get; set; }
public virtual IEnumerable<Cities> Cities { get; set; }
}
public partial class Cities
{
public int ID { get; set; }
public string Name { get; set; }
public string TreLocation { get; set; }
public string Geo { get; set; }
}
Do you want an editable list of cities under each country?
Don't use mvc! In the time you spend figuring it out, you can probably learn angular. Which is far better for tis sort of thing.
However, back in the mists of time wise sages wrestled with this problem, for example....
Collection of complex child objects in Asp.Net MVC 3 application?
https://www.donnfelker.com/editable-grid-list-binding-in-mvc2/&ved=2ahUKEwiltefd9cngAhWjQxUIHe45AM4QFjACegQIARAB&usg=AOvVaw2z8iH5hZnObcnIMl3d4cyI

Do you need to define DbSet's for all entitiies

I am learning EF and unclear when to create a DbSet for an entity. In the standard Blog example that the Microsoft Docs have, they define DbSets for both Blog and Post. Why would one need to create a DbSet for Posts? Isn't the Posts accessible thru Blogs and therefore I can get the Posts using a LINQ query?
public DbSet<Blog> Blogs { get; set; }
public DbSet<Post> Posts { get; set; }
public class Blog
{
public int BlogId { get; set; }
public string Url { get; set; }
public int Rating { get; set; }
public List<Post> Posts { get; set; }
}
public class Post
{
public int PostId { get; set; }
public string Title { get; set; }
public string Content { get; set; }
public int BlogId { get; set; }
public Blog Blog { get; set; }
}
Why would one need to create a DbSet for Posts?
It's not strictly necessary. The rules in EF Core are:
By convention, types that are exposed in DbSet properties on your
context are included in your model. In addition, types that are
mentioned in the OnModelCreating method are also included. Finally,
any types that are found by recursively exploring the navigation
properties of discovered types are also included in the model.
Including & Excluding Types
But common practice is to declare a DbSet for each Entity because you might want to write a query that starts with Posts, or retrieve a single Post without its associated Blog.

Marten JasperFx - How to ignore property of class when document is generated

Its possible ignore class property when Marten store document on database?
Eg.:
public class Test
{
public int Id { get; set; }
public string Name { get; set; }
[Ignore this when create a document on DB]
public Date DateOfBirth { get; set; }
}
SOLVED
public class Test
{
public int Id { get; set; }
public string Name { get; set; }
[IgnoreDataMember]
public Date DateOfBirth { get; set; }
}
TL:DR
Marten just uses Newtonsoft.Json under the hood, so to ignore property, use Newtonsoft JsonIgnoreAttribute:
public class Account
{
public string FullName { get; set; }
public string EmailAddress { get; set; }
[JsonIgnore]
public string PasswordHash { get; set; }
}
Explanation
This seems to be question visible on top of google search so I'd like to add my 50 cents.
Since MartenDB use internally Newtonsoft.Json then all attributes from this lib should work fine. I don't know about Igor answer, couldn't confirm this anywhere in library so it seems to be outdated at the moment.
From docs:
An absolutely essential ingredient in Marten's persistence strategy is JSON serialization of the document objects. Marten aims to make the JSON serialization extensible and configurable through the native mechanisms in each JSON serialization library. For the purposes of having a smooth "getting started" story, Marten comes out of the box with support for a very basic usage of Newtonsoft.Json as the main JSON serializer.
About Newtonsoft.json in MartenDB
MartenDB isn't tied to Newtonsoft.json, you could always write own adapter for other library.
All you have to do is implement ISerializer interface:
public interface ISerializer
{
void ToJson(object document, TextWriter writer);
string ToJson(object document);
T FromJson<T>(TextReader reader);
object FromJson(Type type, TextReader reader);
string ToCleanJson(object document);
EnumStorage EnumStorage { get; }
Casing Casing { get; }
CollectionStorage CollectionStorage { get; }
NonPublicMembersStorage NonPublicMembersStorage { get; }
}
More detailed example: docs

Circular Reference error when serializing objects in ASP.NET Web API

I'm writing a Web API project in C# that uses Entity Framework to pull data from a DB, serialize it and send it to a client.
My project has 2 classes, Post and Comment (foreign key from Post).
These are my classes.
Post class:
public partial class Post
{
public Post()
{
this.Attachment = new HashSet<Attachment>();
this.Comment = new HashSet<Comment>();
}
public int PostId { get; set; }
public string Title { get; set; }
public string Content { get; set; }
public System.DateTime Created { get; set; }
public Nullable<System.DateTime> Modified { get; set; }
public virtual ICollection<Attachment> Attachment { get; set; }
public virtual ICollection<Comment> Comment { get; set; }
}
Comment class:
public partial class Comment
{
public int CommentId { get; set; }
public string Content { get; set; }
public System.DateTime Posted { get; set; }
public bool Approved { get; set; }
public int AnswersTo { get; set; }
public int PostId { get; set; }
public virtual Post Post { get; set; }
}
My problem is that when I try to get via Web API a Post, it spits me the following error:
Object graph for type 'APIServer.Models.Comment' contains cycles and cannot be serialized if reference tracking is disabled.
And when I try to get a Comment via Web API, the error is as follows:
Object graph for type 'System.Collections.Generic.HashSet`1[[APIServer.Models.Comment, APIServer, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null]]' contains cycles and cannot be serialized if reference tracking is disabled.
If I annotate the Comment class with
[DataContract(IsReference = true)]
the errors disappear, but the serialization only returns the ID of the comment and ignores the other fields.
Any suggestions on how to solve this?
Thanks in advance,
Léster
Here are 2 solutions
Solution #1:
I had this same problem and so I decorated my class with DataContract and the members with DataMember like you mention. HOWEVER, I don't like editing auto-generated code directly because I have to redo it every time I regenerate the file. In order to get around this, I used the MetadataType attribute. In your case, it would look like this...
First, you will keep the auto generated entity as is:
public partial class Comment
{
public int CommentId { get; set; }
public string Content { get; set; }
public System.DateTime Posted { get; set; }
public bool Approved { get; set; }
public int AnswersTo { get; set; }
public int PostId { get; set; }
public virtual Post Post { get; set; }
}
Next, in another file, you will create another partial class and decorate it like this:
[MetadataType(typeof(Metadata))]
[DataContract(IsReference = true)]
public partial class Comment
{
private class Metadata
{
[DataMember]
public int CommentId { get; set; }
[DataMember]
public string Content { get; set; }
[DataMember]
public System.DateTime Posted { get; set; }
[DataMember]
public bool Approved { get; set; }
[DataMember]
public int AnswersTo { get; set; }
[DataMember]
public int PostId { get; set; }
[DataMember]
public virtual Post Post { get; set; } // you can remove "virtual" if you wish
}
}
MetadataType will essentially add the attributes from the Metadata buddy class to the ones with the same name in Comment (not directly, but for our purposes, it's close enough... that's a topic for a different post). Of course, if your Comment entity changes, you'll need to update this accordingly.
Solution #2:
Having to edit your second file every time you make a change is only a slight improvement from directly editing auto-generated files. Fortunately, there is another approach that is much easier to maintain. Details can be found here but as a summary, all you need to do is decorate your OperationContract that is consuming Comment with an additional attribute, ReferencePreservingDataContractFormat. Note that there is a slight error in the code provided on that page that would cause infinite recursion. As noted in this post, the fix is quite simple: instead of recursing at all, just create a new DataContractSerializer
The advantage to this approach is that no matter how much you change Comment, you still don't need update anything.
As an example for your code, let's say you are using Comment as follows:
[OperationContract]
Comment FindComment(string criteria);
All you need to do is add
[OperationContract]
[ReferencePreservingDataContractFormat]
Comment FindComment(string criteria);
And then somewhere else you need to define ReferencePreservingDataContractFormat which will look like this:
//From http://blogs.msdn.com/b/sowmy/archive/2006/03/26/561188.aspx and https://stackoverflow.com/questions/4266008/endless-loop-in-a-code-sample-on-serialization
public class ReferencePreservingDataContractFormatAttribute : Attribute, IOperationBehavior
{
public void AddBindingParameters(OperationDescription description, BindingParameterCollection parameters)
{
}
public void ApplyClientBehavior(OperationDescription description, System.ServiceModel.Dispatcher.ClientOperation proxy)
{
IOperationBehavior innerBehavior = new ReferencePreservingDataContractSerializerOperationBehavior(description);
innerBehavior.ApplyClientBehavior(description, proxy);
}
public void ApplyDispatchBehavior(OperationDescription description, System.ServiceModel.Dispatcher.DispatchOperation dispatch)
{
IOperationBehavior innerBehavior = new ReferencePreservingDataContractSerializerOperationBehavior(description);
innerBehavior.ApplyDispatchBehavior(description, dispatch);
}
public void Validate(OperationDescription description)
{
}
}
class ReferencePreservingDataContractSerializerOperationBehavior : DataContractSerializerOperationBehavior
{
public ReferencePreservingDataContractSerializerOperationBehavior(OperationDescription operationDescription) : base(operationDescription) { }
public override XmlObjectSerializer CreateSerializer(Type type, string name, string ns, IList<Type> knownTypes)
{
return new DataContractSerializer(type, name, ns, knownTypes,
0x7FFF, //maxItemsInObjectGraph
false, //ignoreExtensionDataObject
true, //preserveObjectReferences
null //dataContractSurrogate
);
}
public override XmlObjectSerializer CreateSerializer(Type type, XmlDictionaryString name, XmlDictionaryString ns, IList<Type> knownTypes)
{
return new DataContractSerializer(type, name, ns, knownTypes,
0x7FFF, //maxItemsInObjectGraph
false, //ignoreExtensionDataObject
true, //preserveObjectReferences
null //dataContractSurrogate
);
}
}
And that's it!
Either method will work just fine--pick the one that works for you.
You can disable Lazy Loading on your Comment class by removing virtual from the Post property definition...
public partial class Comment
{
public int CommentId { get; set; }
public string Content { get; set; }
public System.DateTime Posted { get; set; }
public bool Approved { get; set; }
public int AnswersTo { get; set; }
public int PostId { get; set; }
public Post Post { get; set; }
}
This should sort out the circular reference exception.

How to create a LINQ to Entities query including both tag class name and text string

My ASP.NET MVC project has some
chapter class
and related
tag class
They have many to many relationship though one tag has multiple chapter and also one chapter has multiple tags on it.
My question is: I want to make a query by using tag.title within this structure but LINQ doesn't allow objects; it only gets variables like string, int ...etc.
I am feeling a little bit stupid but I can't figured it out this basic thing :( (May be I have to give a break to let it be clear in my mind)
My models are:
public partial class Chapter
{
public string Id { get; set; }
public string Subject { get; set; }
public string Content { get; set; }
public virtual ICollection<Tag> Tags { get; set; }
}
public partial class Tag
{
public int? TagId { get; set; }
public string Title { get; set; }
public int CallCount { get; set; }
public virtual ICollection<Chapter> Chapters { get; set; }
}
and also for db:
public class DataContext : DbContext
{
public DbSet<Chapter> Chapters { get; set; }
public DbSet<Tag> Tags { get; set; }
}
so here we go:
var tag = (from t in db.Tags where t.Title.Contains(search_word)).FirstOrDefault();
var chapters = (from c in db.Chapters where (m.Title.Contains(search_word) || m.Tags.Contains(tag)) select c).ToList();
As I explained above; it gives
"you should use string, guid, int... on queries"
error for this query. I think there is a logical failure I made but my brain stopped responding...
Briefly; I want to search by tag names and if you have another approach I will be pleased to listen it..
Any help will be highly appreciated.
Thanks!
You want:
var tag = (from t in db.Tags
where t.Title.Contains(search_word)
select t).FirstOrDefault();
var chapters = (from c in db.Chapters
where m.Title.Contains(search_word)
|| m.Tags.Any(t => t.TagId == tag.TagId)
select c).ToList();