We are trying to design a site that will support multilingual data and we use asp.net and nhibernate for O/R mapping. After some googling we decided that for us its best to create the entity classes containing all fields we need for supporting the default (english) language and for each multilingual class we would create a new class containing only the multilingual fields plus the id of the main (english) class and the language Id. For example a simple "product" class we could have the fields :
product
int ID
string DescriptionInEnglish
string FullDescriptionInEnglish
decimal price
and a second class "product_Lang" containing
product_Lang
int Product_ID
int Language_ID
string Description
string FullDescription
then for being able to load a product in any language we could add a product_Lang property called lang on the products and for easy binding we could have two read only properties like that :
string DescriptionToBind
{
get
{
if (lang != null)
return this.lang.Description;
else
return this.DescriptionInEnglish;
}
}
string FullDescriptionToBind
{
get
{
if (lang != null)
return this.lang.FullDescription;
else
return this.FullDescriptionInEnglish;
}
}
for loading some products we can use some methods on the ProductRpository like that :
Product GetProductByID(int ID);
Product GetProductbyID_ML(int ID, int Language_ID);
and for loading some collections
IList<Product> GetAllProducts();
IList<Product> GetAllProducts_ML(Language_ID);
The problem is how to map the Lang property inside the product class in nhibernate.
It may be easy but I cannot figure out. Its not one-to-one because in english lang is optional. I thought about one-to-many so i load a list of all but i think its not fair to load all languages because i need only one.
Any help ? or any other suggestions will still give fair performance ?
I have written a detailed article about multi language implementation with ASP.NET and NHibernate.
Check Create a multi languaged domain model with NHibernate and C#
Related
I'm using Entity Framework (DB First) on a new project and wanted to add some customisation to the classes generated. However, my changes are obviously lost every time that the edmx is refreshed. I was just wondering if there is a design pattern for handling this sort of thing?
As an example, suppose I have a class with a integer property; StatusID - and I'd like to extend the entity class so that the status value can also be accessed/set via the related enum and finally a property that gets a text representation of that Enum from the description attribute. This all works, but those customisations are lost when the model is refreshed. I appreciate that the property can be converted to an enum, so the latter property that gets the description of the enum is perhaps a better example for this question.
I think I know the answer but I just wanted to put this out there in case there were some magic tricks that would allow this to work and prevent those customisations from being lost.
public int StatusID { get; set; }
public Enumerations.ValidationStatus StatusEnum
{
get
{
return (Enumerations.ValidationStatus)StatusID;
}
set
{
StatusID = (int)value;
}
}
public string StatusText
{
get
{
return MyMethodThatGetsTheEnumDescription(StatusEnum);
}
}
Two Solutions to work around the problem:
User Data Transfer Object(DTO) nd put the enum there. then use Automapper or manually map between the DB Model and the DTO Model (best practice)
Instead of enum you can use extension functions on the model and define your getter, setters and any extra properties you want as extension functions to the class
(will add some complexity to your models)
Just started trying out Breeze today, long time EF user - think I found a bug in Breeze, but I may be doing something wrong - want to know which it is:
I have a simple hierarchy in EF Code First:
// For testimonials about the product line in general
public class Testimonial
{
public int Id { get; set; }
public string Text { get; set; }
}
// For testimonials specific to a single product
[Table("ProductTestimonial")]
public class ProductTestimonial : Testimonial
{
public Product Product { get; set; }
}
So just to be clear there's 2 tables here, Testimonial and ProductTestimonial, both have a PK of Id, and a Text field, and one of them also has an FK off to Product. This is just a simple way of implementing Table Per Type.
So I setup the BreezeController via WebApi:
[BreezeController]
public class EFController : ApiController
{
private readonly EFContextProvider<EfDb> _db = new EFContextProvider<EfDb>();
[HttpGet]
public string Metadata()
{
return _db.Metadata();
}
And I go to load it in breeze.js:
var manager = new breeze.EntityManager('/api/ef');
manager.executeQuery(breeze.EntityQuery.from('Product');
Kablamo. Exception. It says:
Unable to get value of the property 'propertyRef': object is null or undefined
At:
function convertFromODataEntityType(... {
. . .
var keyNamesOnServer = toArray(odataEntityType.key.propertyRef)...
Where odataEntityType.name == 'ProductTestimonial', and .key sure enough is undefined.
Which is true. Picking things apart, when I call executeQuery(), Breeze hits the Metadata call on the WebApi, which I verified calls and returns successfully. The massive JSON string returned from there includes:
{
"name": "ProductTestimonial",
"baseType": "Self.Testimonial",
"navigationProperty": {
"name": "Product",
"relationship": "Self.ProductTestimonial_Product",
"fromRole": "ProductTestimonial_Product_Source",
"toRole": "ProductTestimonial_Product_Target"
}
},
{
"name": "Testimonial",
"key": {
"propertyRef": {
"name": "Id"
}
},
So it would appear the basic issue is that the Metadata is accurately portraying ProductTestimonial as an inherited class, whose key is defined elsewhere, but Breeze.js - if I'm understanding it correctly - is naively just checking the .key property without considering superclasses. But, I could be wrong since I'm so new to Breeze.
Addendum:
I don't think it's relevant here but in case it comes up, yes I do have an IQueryable as well on the WebApi Controller:
[HttpGet]
public IQueryable<Product> Products()
{
return _db.Context.Products;
}
Also, I recognize a potential workaround here is probably to discard TPT and make full classes for every Entity with no inheritance, but I'm really slimming my example down here - there's a lot of inheritance throughout the EF model that has to stay. If it's inheritance or Breeze, Breeze is getting the axe.
Edit: As of v 1.3.1 Breeze now DOES support inheritance.
Inheritance is coming but we don't have a fixed date just yet. Please vote for the feature on the Breeze User Voice. We take these suggestions very seriously.
I don't think manager.fetchEntityByKey works with inheritance - can any of the Breeze guys confirm whether this is correct? It seems to be picking up the inherited fields from my base class, but not the fields from my derived class. I can get the full object by using entityQuery but then I have to name every field. When I do that I'm still getting parse errors on ko bindings.
I have a website that reads some of its content from a database, I need this website in both languages, English and Arabic.
the needed content is duplicated in the database in both languages. lets say I have a En_Name and Ar_Name columns in my database.
and for example for the Arabic version of the website a link will display a text from Ar_Name , and with the English one it should display the text from the En_Name.
for the static content in my website I think it is a good idea to use the ASP.NET default localization using (.resx files). but what I don't know is how to do the localization for the dynamic section of the website.
So, how can I make the same hyperlink read once from the Ar_Name field, and then from the En_Name based on the users choice (Localization)?
There are many ways to accomplish this. You've not mentioned which database technology you are using, so my example is with Entity Framework. You may need to customise this to your own situation.
Something similar may be possible with LinqToSql or other ORMs. If you are using something else entirely, then the key is to have a central class that you pass something consistent to (hence the interface) that does the translation.
For example, if I was using Entity Framework, every table in the database that had these two fields I'd add an interface that exposes those fields. Then I'd have a helper class with a method that took any entity with that interface and checked the current localisation and return the correct version of the text.
public interface IBilingualEntity
{
// Defines a consistent interface that indicates which language version
// each thing is in.
string Ar_Name { get; }
string En_Name { get; }
}
public partial MyEntity : IBilingualEntity
{
// This is a class generate by Entity Framework. But it could
// be anything really if you are using some other framework.
//
// Nothing to do here as the other side of the partial
// should already implement the interface with the code
// generated from Entity Framework. If not, implement the
// interface and return the correct language version in
// the property.
}
// This is the helper that works out which language version to use.
public class BilingualHelper
{
public string GetName(IBilingualEntity entity)
{
// NOTE: You may have to strip away the region part of the name
// but off the top of my head I can't remember the format.
// If you are using something else to store the culture you'll
// have to reference that instead.
var cultureName = Thread.CurrentThread.CurrentUICulture.Name;
if (cultureName == "ar")
return entity.Ar_Name;
return entity.En_Name;
}
}
It seems weird that I couldn't find an explanation of the difference between those two helpers, so I would assume that is something obvious but I missed.
Basically I am trying to decide which one I should use for my case, with the following simple Model:
public class Booking
{
public int ID { get; set; }
public Room Room { get; set; }
public DateTime StartTime { get; set; }
public DateTime EndTime { get; set; }
public ICollection<Equipment> Equipments { get; set; }
public string Who { get; set; }
}
and I want display a simple Room DropDownlist for Adding and Editing Booking record.
After doing a lots of Google around, it seems that I probably need a DropDopwListFor, but not sure why and how?
Take the following two examples:
#Html.DropDownListFor(
x => x.EquipmentId,
new SelectList(Model.Equipments, "Id", "Text")
)
and:
#Html.DropDownList(
"EquipmentId",
new SelectList(Model.Equipments, "Id", "Text")
)
It is obvious that with the second example the name of the property you are binding the dropdown to is hardcoded as a magic string. This means that if you decide to refactor your model and rename this property Tooling support that you might be using has no way of detecting this change and automatically modifying the magic string you hardcoded in potentially many views. So you will have to manually search & replace everywhere this weakly typed helper is used.
With the first example on the other hand we are using a strongly typed lambda expression tied to the given model property so tools are able to automatically rename it everywhere it is used if you decide to refactor your code. Also if you decide to precompile your views you will get a compiler time error immediately pointing to the view that needs to be fixed. With the second example you (ideally) or users of your site (worst case scenario) will get a runtime error when they visit this particular view.
Strongly typed helpers were first introduced in ASP.NET MVC 2 and the last time I used a weakly typed helper was in an ASP.NET MVC 1 application long time ago.
DropDownListFor will automatically select the selected value by using the specified property:
// Will select the item in model.Equipments that matches Model.EquipmentId
#Html.DropdownListFor(m => m.EquipmentId, Model.Equipments);
Another comment:
Don't have ICollection<Equipment> Equipments in your view model. You should have a property that returns an IEnumerable<SelectListItem>.
When you want to add a view (aspx file) where this DropDownList or DropDownListFor will be inside, rightclick->add view then select "Create a strongly typed view" then in list select Booking class. After that add this page.
You can write in it as follows:
#Html.DropdownListFor(m => m.Equipments , Model.Equipments);
because we add strongly typed view as Booking, you can have:
m => m.ID, m => m.Room, m => m.StartTime
... etc.
In your services you can have methods to take data from database, then use this service's method in your controller to pass data from database to view. You can use ViewData in your controller:
ViewData["Equipments"] = new servicename().getdatalistfromdatabase().AsEnumarable();
Putting AsEnumarable() at the end of your list taken from database makes it IEnumarable.
Then in your view, you can also have :
#Html.DropdownList("MyEquipments" , ViewData["Equipments"]);
A link on ViewData usage:
http://msdn.microsoft.com/en-us/library/dd410596.aspx
I hope that helps you.
DropdownListFor is support strongly type and it name assign by lambda Expression so it shows compile time error if have any error.
DropdownList not support this.
ASP.net scaffolding creates administrative page names by adding a 'S' to the name of the table. Thus the editing page for User table is named Users, and so on.
Is there a simple way of changing that name without creating custom pages?
For instance for the table "Business" I'd like ASP.net DynamicData to create an administrative link named "Businesses" appearing at the front page instead of the very ugly "Businesss" I'm getting now.
Of course I don't care about the name that will appear in the routed URL, just want the front page to have the name appearing correctly.
Yes, use the DisplayName attribute. It can be used on both Classes and Properties.
http://msdn.microsoft.com/en-us/library/system.componentmodel.displaynameattribute.aspx
Example:
[MetadataType(typeof(OrderDetailsMetadata))]
public partial class OrderDetails
{
public const string DisplayName_UnitPrice = "Unit Price";
}
[DisplayName("Order Details")]
public partial class OrderDetailsMetadata
{
[DisplayName(OrderDetails.DisplayName_UnitPrice)]
public Decimal UnitPrice { get; set; }
}