Single resource file for DataAnnotations localization - asp.net-core

What I should try to achieve is to have a single resource (resx) file for each supported language in net-core. I explain the problem a bit.
I have DataAnnotations on each of my entities and I need to localize the messages returned in case of errors. It seems that the default convention required by net-core is to have a different resx file for each of our entities.
This file is named accordingly to the namespace of the entity with the culture indentifier and the resx extensions. So, if I have an entity named Customers within the namespace Data.Entities, I should add a file named Data.Entities.Customers.it.resx and put all the translations for the Italian language in it. So, if I have an attribute
StringLength(50, ErrorMessage="The {0} should not be longer than {1} characters")
public string Name {get;set;}
then I add the proper translation to the Data.Entities.Customers.it.resx file.
But, if I go on another entitity like Suppliers I am forced to write another resource file named Data.Entities.Suppliers.it.resx and, of course I have
StringLength(50, ErrorMessage="The {0} should not be longer than {1} characters")
public string SupplierName {get;set;}
Now I need to write again the same translation in the proper file for the Suppliers entity. This goes on as well for other common attributes like [Required].
So I hope to have explained well my problem and my queston is: There is a way to specify a single resource file for all my entities validation messages and then write a single time the messages for the common texts?

From the docs, you can tell the framework to use a shared resource for your data annotations localisation:
public void ConfigureServices(IServiceCollection services)
{
services.AddMvc()
.AddDataAnnotationsLocalization(options => {
options.DataAnnotationLocalizerProvider = (type, factory) =>
factory.Create(typeof(SharedResource));
});
}
In the preceeding code, SharedResource is the class corresponding to the resx where your validation messages are stored. With this approach, DataAnnotations will only use SharedResource, rather than the resource for each class.

Related

REST URL handling with DTOs

In my project I have a model with different DTOs:
public class Employee{
public Guid Id {get; set;}
public string Name {get; set;}
public IEnumerable<Cookie> Cookies {get; set;}
}
public class EmployeeIndex :Dto{
public Guid Key{get; set;}
public string Value {get; set;}
}
public class Cookie {
public Guid Id {get; set;}
public string Secret {get; set;}
}
public class CookiePublic :Dto{
public Guid Id{get; set;}
}
Now I would like to keep my API as clean as possible. So I have the following Routes:
POST /employees
GET /employees
GET /employees/index //--> Conflict (should return list of EmployeeIndex
GET /employees/:id
PUT /employees/:id
DELETE /employees/:id
GET /employees/:id/cookies // --> returns List of Cookies
GET /employees/:id/cookies/:cookieId
GET /employees/:id/coookies/public // --> Conflict (should return list of CookiePublic)
But now I need a route for returning also the CookiePublic-DTO. Where can I put that url respectively how does the routes should look like?
Or how would the route should look like to return the employee-index-Dto?
Because /employees/:id/cookies/public and employees/index results in a duplicate url conflict.
How can this get solved in REST?
REST doesn't care what spelling conventions you use for your resource identifiers.
It also doesn't really have any sense of "conflict" as you describe here. The fact that there is ambiguity about the identify of your conflicting resources is an artifact of your routing implementation, not REST.
In other words
/employees/index
/employees/12345
/employees/67890
as far as REST is concerned, these are three different resources. The fact that they want to be implemented via two routes is an implementation detail.
From a REST perspective, there's no particular reason that the hierarchy of path segments needs to match the domain hierarchy (the "resource model" is not the "domain model" is not the "data model").
So you could consider
/employees/12345
/index/employees
/employees/12345/cookies
/cookies/123
/public-cookies/123
The machines absolutely don't care whether the structure of the URI matches the domain semantics. They care a little bit about some general purpose things (the ability to use relative references to compute other resource identifiers, the ability to use general purpose URI templates to compute other resource identifiers, the ability to use web forms as URI templates, etc).
So we use the extra degrees of freedom allowed by the machines indifference to choose a design that makes life easier for the humans we care about (customers looking at a browser history, operators reviewing an access log, technical writers trying to document the API, etc).

How to get all FAL File Objects which are referenced?

I'm trying to make a extbase extension for TYPO3 to get alle file objects with mimetype image/... which referenced by any content, plugin or fluid in typo3.
But i don't know which is the best way to get these data. How should i create a model in my extension and how should i create the correct repository?
If i create a custom query i'm not sure how to return a complete FAL Object which contains any data (like metadata) etc.
hope someone could help me to find the right way, and maybe has a example or something.
thanks a lot
You could do it like this, details are at the bottom:
Get all file references.
Go through them, retrieve the referenced file for each of them and retain only the ones where the field mime_type starts with image/.
There are two things you probably need to watch out for:
The field mime_type needs to be up to date. Check the FAL scheduler indexing task for that.
Performance. Depending on the number of files you have, it could be much faster to do this with a custom SQL statement which makes use of a JOIN. But you should only do that if performance is a problem.
How to get all file references:
First, build your own empty file reference class:
namespace Vendor/Extkey/Domain/Model;
class FileReference extends \TYPO3\CMS\Extbase\Domain\Model\FileReference {}
Make sure to configure it in your TypoScript to be serialized to the table sys_file_reference:
config.tx_extbase.persistence {
classes {
Vendor\Extkey\Domain\Model\FileReference {
mapping {
tableName = sys_file_reference
}
}
}
}
Add a repository for the references:
namespace Vendor/Extkey/Domain/Repository;
class FileReferenceRepository extends \TYPO3\CMS\Extbase\Persistence\Repository {
public function initializeObject() {
/** #var \TYPO3\CMS\Extbase\Persistence\Generic\QuerySettingsInterface */
$defaultQuerySettings = $this->objectManager->get('TYPO3\\CMS\\Extbase\\Persistence\\Generic\\QuerySettingsInterface');
$defaultQuerySettings->setRespectStoragePage(FALSE);
$this->setDefaultQuerySettings($defaultQuerySettings);
}
}
The reference class can be empty, but the repository needs it to be there in order to work correctly. Make sure you add the default query settings to ignore the page id, so you get all non-hidden and non-deleted file references by calling $fileReferenceRepository->findAll().
How to check the MIME-type of each referenced file:
You can get the MIME-type of each reference by calling
$fileReference->getOriginalResource()->getMimeType()
This should automatically fetch the original file from storage and read its MIME-type.

findBy URI not working in Spring Data Rest

By default, in Spring Data Rest the #Id of the entity is not exposed. In line with the REST rules, we're supposed to use the URI of the resource to refer to it. Given this assumption, the findBy queries should work if you pass a URI to them, but they don't.
For example, say I have a one-to-many relationship between Teacher and Student. I want to find students by teacher.
List<Student> findByTeacher(Teacher teacher)
http://localhost:8080/repositories/students/search/findByTeacher?teacher=http://localhost:8080/repositories/teachers/1
This doesn't work because the framework is attempting to convert the teacher URI to a Long.
I get this error that says "Failed to convert from type java.lang.String to type java.lang.Long".
Am I missing something?
You could expose #Id s by configuring web intializer
//Web intializer
#Configuration
public static class RespositoryConfig extends
RepositoryRestMvcConfiguration {
#Override
protected void configureRepositoryRestConfiguration(
RepositoryRestConfiguration config) {
config.exposeIdsFor(Teacher.class);
}
}
Its good to change List to Page
List findByTeacher(Teacher teacher)
to
Page<Student> findByTeacher(#Param("teacher) Teacher teacher, Pageable pageable);
Also note #Param annotation is required along with Pageable. The latter is required because return type "Page"
3.Latest snapshots, not milestones work fine
See https://jira.spring.io/browse/DATAREST-502
Depending of your version of Spring Data, it would work as you want or not. If you are with Spring Data 2.4, you need to pass the URI. If you are with a previous version, you need to pass the id.

Customize table names ASP MVC 4

I have a few tables previously created in my database that I want to map to a few model classes:
"SISTEMA.Voyages" => public class Voyage
"SISTEMA.Puerto" => public class Port
I understand that in ASP.MVC 4 with Entity framework this can be done either of two ways. However for both of them I am getting errors which I do not know how to resolve.
The first in Voyage.cs:
[Table("SISTEMA.Voyages")]
public class Voyage
Or the second in Context.cs:
public class ApplicationEntities : DbContext
{
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<Voyage>().ToTable("SISTEMA.Voyages");
}
}
For the first version I get this error when I previously assumed this was something automatic:
The type or namespace name 'Table' could not be found (are you using directive or an assembly reference?)
The type or namespace name 'TableAttribute' could not be found (are you using directive or an assembly reference?)
Fore the second I get this error which I didn't expect because I assumed this was a configuration issue and leaves me really confused:
The model backing the 'ApplicationEntities' context has changed since the database was created. Consider using Code First Migrations to update the Database.
Where is this history even recorded?
For the record, I am used to dealing with this sort of issue in Rails by typing in:
class Voyage
self.table_name = "SISTEMA.Voyages"
end
And I am not very familiar with C# / ASP.NET. Just publishing what I am looking up for the next hour unless somebody tells me where I am going wrong first.
For the first way, you are missing a using directive for the Table attribute, add this to your class:
using System.ComponentModel.DataAnnotations;
For the second way, I'm guessing that something has changed in your model and now it recommends using Code First to update the database. When you run your application, and it first hits the database, it verifies that your models match the schema - if not it can give you this error.
I recommend checking out some of these links for help getting started with EF / Code First / Migrations - they are really handy
EF + Code First
Handling Many to Many with Payload
How to work with Migrations
MSDN EF Site
Please Add:
using System.ComponentModel.DataAnnotations.Schema;

Filter contents of lazy-loaded collection with NHibernate

I have a domain model that includes something like this:
public class Customer : EntityBase<Customer>, IAggregateRoot
{
public IList<Comment> Comments { get; set; }
}
public class Comment : EntityBase<Comment>
{
public User CreatedBy { get; set; }
public bool Private { get; set; }
}
I have a service layer through which I retrieve these entities, and among the arguments passed to that service layer is who the requesting user is.
What I'd like to do is be able to construct a DetachedCriteria in the service layer that would limit the Comment items returned for a given customer so the user isn't shown any comments that don't belong to them and are marked private.
I tried doing something like this:
criteria.CreateCriteria("Comments")
.Add(Restrictions.Or(Restrictions.Eq("Private", false),
Restrictions.And(Restrictions.Eq("Private", true),
Restrictions.Eq("CreatedBy.Id", requestingUser.Id))));
But this doesn't flow through to the lazy-loaded comments.
I'd prefer not to use a filter because that would require either interacting with the session (which isn't currently exposed to the service layer) or forcing my repository to know about user context (which seems like too much logic in what should be a dumb layer). The filter is a dirty solution for other reasons, too -- the logic that determines what is visible and what isn't is more detailed than just a private flag.
I don't want to use LINQ in the service layer to filter the collection because doing so would blow the whole lazy loading benefit in a really bad way. Lists of customers where the comments aren't relevant would cause a storm of database calls that would be very slow. I'd rather not use LINQ in my presentation layer (an MVC app) because it seems like the wrong place for it.
Any ideas whether this is possible using the DetachedCriteria? Any other ways to accomplish this?
Having the entity itself expose a different set of values for a collection property based on some external value does not seem correct to me.
This would be better handled, either as a call to your repository service directly, or via the entity itself, by creating a method to do this specifically.
To fit in best with your current model though, I would have the call that you currently make to get the the entities return a viewmodel rather than just the entities;
public class PostForUser
{
public Post Post {get; set;}
public User User {get; set;}
public IList<Comment> Comments}
}
And then in your service method (I am making some guesses here)
public PostForUser GetPost(int postId, User requestingUser){
...
}
You would then create and populate the PostForUser view model in the most efficient way, perhaps by the detached criteria, or by a single query and a DistinctRootEntity Transformer (you can leave the actual comments property to lazy load, as you probably won't use it)