
One of the things I tend to do as a an automatic step is call HtmlEncode on all string data going to the DB and then HtmlDecode when it comes back out.
With my DB access going through OA's persistent classes now I only have to write this once ...
[FieldAlias(
"description"
)]
public
string
Description
{
get
{
return
HttpContext.Current.Server.HtmlDecode(description);
}
set
{
description = HttpContext.Current.Server.HtmlEncode(value);
}
}
but I wondered if it wouldn't be a nice idea for OA's designer to spot string values and provide an option to automatically add the code to encode/decode these values.
I guess there are going to be times when people don't want OA messing with their data, so it should be a optional step, a checkbox on the form, for example leading to a change in the class like this...
[FieldAlias(
"description"
)]
[AutoEncode]
public
string
Description
{
get
{
return
description;
}
set
{
description = value;
}
}
What do you think?
--
Stuart
13 Answers, 1 is accepted
The good thing about the code generation is that you can easily modify it to suite your needs. In this particular example I will show you how to modify the way OpenAccess generates string properties. You can do this in a couple of steps.
First we need to locate the T4 templates that OpenAccess uses. The C# templates are usually located under
C:\Program Files (x86)\Telerik\OpenAccess ORM\dsl\CodeGenerationTemplates\CSharp. What we need to modify is the Specific.ttinclude file. Note that it would be better to copy this folder and not modify the default templates. You can specify which template the code generation is using through the model settings dialog.
First you should find the InitializeDefaultUsings() method and add the System.Web using to the default usings. Your should look like this :
private
void
InitializeDefaultUsings()
{
if
(Usings.Count < 1)
{
Usings.Add(
"System"
);
Usings.Add(
"System.Data"
);
Usings.Add(
"System.Linq"
);
Usings.Add(
"System.Linq.Expressions"
);
Usings.Add(
"System.Data.Common"
);
Usings.Add(
"System.Collections.Generic"
);
Usings.Add(
"System.Web"
);
// the added using
Usings.Add(
"Telerik.OpenAccess"
);
}
}
Then you have to find the code for adding a property. It should look like this before any changes.
private
<#=
this
.GetTypeStringPresentation(property) #> <#= property.FieldName #><#= initialValue #>;
<#+ GenerateCustomAttributes(property.Attributes); #>
public
virtual
<#=
this
.GetTypeStringPresentation(property) #> <#= property.Name #>
{
<#+
if
(property.HasGetter)
{
#>
get
{
return
this
.<#= property.FieldName #>;
}
<#+
}
if
(property.HasSetter)
{
#>
set
{
this
.<#= property.FieldName #> = value;
}
<#+
}
#>
}
In order to make it generate code that encodes and decodes these properties you should change it to look like this.
private
<#=
this
.GetTypeStringPresentation(property) #> <#= property.FieldName #><#= initialValue #>;
<#+ GenerateCustomAttributes(property.Attributes); #>
public
virtual
<#=
this
.GetTypeStringPresentation(property) #> <#= property.Name #>
{
<#+
if
(property.HasGetter)
{
#>
get
{
<#+
string
type =
this
.GetTypeStringPresentation(property);
if
(type ==
"String"
|| type ==
"string"
)
{
#>
return
HttpContext.Current.Server.HtmlDecode(
this
.<#= property.FieldName #>);
<#+
}
else
{
#>
return
this
.<#= property.FieldName #>;
<#+
}
#>
}
<#+
}
if
(property.HasSetter)
{
#>
set
{
<#+
string
type =
this
.GetTypeStringPresentation(property);
if
(type ==
"String"
|| type ==
"string"
)
{
#>
this
.<#= property.FieldName #> = HttpContext.Current.Server.HtmlEncode(value);
<#+
}
else
{
#>
this
.<#= property.FieldName #> = value;
<#+
}
#>
}
<#+
}
#>
}
I have attached a zip file containing this modified template for you to use. Using this technique I have successfully generated properties that look like this for example :
private
string
firstName;
public
virtual
string
FirstName
{
get
{
return
HttpContext.Current.Server.HtmlDecode(
this
.firstName);
}
set
{
this
.firstName = HttpContext.Current.Server.HtmlEncode(value);
}
}
Please share your thoughts on this approach.
Best wishes,
Serge
the Telerik team

Neat!
Like I said, this would be a nice addition to the product as a whole, so I'd like to suggest that your template is added to the official product.
A secondary question: is the nature of the templates documented anywhere?
--
Stuart

I have a sneaking suspicion I know the answer to this question...
I'm not using the Designer. I started this project before the designer could work with forward mapping which is what I'm using. I've looked for a "Model Settings" option, or anything that suggests I can change the path of any templates but can't find anything. Does this mean I can't do this?
--
Stuart
Your sneaking suspicion is right.
You mentioned the designer so I assumed even though the examples were from the Wizards you were in fact looking for domain model features. Unfortunately we do not plan on investing more time into the old approach. As to adding this in the product, at the time we think it's best to only ship the default templates and give the chance to the community to fill this gap.
As to the nature of the T4 templates this blog post by Scott Hanselman rounds up most of the good articles about code generation.
Serge
the Telerik team

I hate it when that happens! :-)
> Unfortunately we do not plan on investing more time into the old approach.
I had guessed that too. I've been told by one of your colleagues that I should hold my water and that a conversion tool for the old forward mapping projects to the new Designer model will turn up "at some point in the future". I'll just have to carry on being the poor relation for now I guess.
> As to the nature of the T4 templates this blog post by Scott Hanselman rounds up most of the good articles about code generation.
I've added that to my "to read" list.
Thanks again Serge.
--
Stuart
Actually the convert tool is already here, but I suspect it will not be of any help to you as it is only working with models created trough the Reverse Mapping Wizard.
However you can check this blog post out.
Be that as it may, it shouldn't be too hard to manually convert your project and I will be glad to help you with this task, if you have the time of course.
Serge
the Telerik team

That's very kind.
> if you have the time of course.
Indeed I do.
I have to say that the Model isn't very big at all ATM, just a dozen or so classes.
So, where do I start? Do we do it here in the forum?
--
Stuart
I do not see a reason why not. However a few points I would like to make before we start. We can convert your solution to use the new domain model but any changes you would want to your model will have to go through the designer. This means that you will not exactly be using forward mapping but modifying your model trough the designer.
So first you should know that the old approach and the designer cannot co-exist in a single project. They use different enhancers and which exclude each other. The easiest way for you will be to just create a new DAL project and add a new domain model. Configure it to connect to the same database and choose all object needed. Then reverse map your objects.
I think this would be the place to check if the class and property names are the same as before. If you have only been using the forward mapping this would likely not be the case. However renaming properties shouldn't pose as a problem.
At this point you will have two models that should look exactly the same. However as you might have noticed the entry points to OpenAccess are now different. The scopes previously used are nowhere to be found in the new model. You have a choice here, if you have a lot of code that uses scopes that you do not want to change you can implement a ObjectScopeProvider that would be exactly the same as the old one with the exception of the static Database() method. The new implementation of this method you can find in the blog post about the Upgrade Wizard. This approach is left there for backwards compatibility so that you are able to still run your existing code.
However it shouldn't be to hard to convert code using scopes to using the new context. Essentially what you need to change is the initialization of the scope to initialize a context, remove the Transaction.Begin statements, change scope.Extent<Product>() to context.Products for the type named Product, change scope.Add to context.Add. What is left is to change scope.Transaction.Commit() to context.SaveChanges() and if you have used it the scope.Transaction.Rollback() to context.ClearChanges().
It would be best in this situation to remove any references to the project containing the Wizards generated model to make sure everything works with the new context. Also in order to get things working with the connection strings you have to copy the connection string from app.config in the project containing the .rlinq and paste into the config file of the executing project.
This should be enough to get you started.
Serge
the Telerik team

> would want to your model will have to go through the designer. This means that
> you will not exactly be using forward mapping but modifying your model trough
> the designer.
I see no problem with that.
> The easiest way for you will be to just create a new DAL project and add a new
> domain model. Configure it to connect to the same database and choose all
> object needed. Then reverse map your objects.
I've done that and it's made a dreadful job of it.
Let's look at some of the issues:
I have a class "Contact". There are 2 additional classes, "Tutor" and "Student" which inherit from this. There are just a couple of properties in each of the 2 inherited classes so, when I created them using the Forward-Mapping wizard, I opted for flat mapping,
When the I created by Domain Model by pointing it at my existing DB, it created 3 classes, but all three had properties in from all of the others.
In my original model I have a class "School". A School can have many "Locations" and each Location has an "Address". None these relationships (or any of the others that exist in the original model) made it in to the design of the new Domain Model.
Maybe my expectations were too high. I expected to have to do something to the generated Domain Model to get it working, but this leaves me thinking that I would be better off starting from fresh.
--
Stuart
Regarding the lost relationships that you mention:
In a database relationships are usually represented by foreign key constraints.
And by default, the forward mapping wizard does not always create these constraints in the database.
And if there are no foreign key constraints in the database there is no enough information for the "add new domain model" wizard to create the associations between classes.
You can make the forward mapping wizard to always create foreign key constraints using the dbNullForeignKey option in your app.config file. You can find more information in this forum thread:
http://www.telerik.com/community/forums/orm/development/foreign-key-constraint-on-null-column-in-mssql-2005.aspx
Regarding your inheritance issues:
It is normal that the "add new domain model" wizard did not guess the inheritance from the database and thus did not create inherited classes. This functionality is not implemented currently.
We are planning to do some improvements in that direction for the Q3 2010 release of OpenAccess.
Sincerely yours,
Jordan
the Telerik team

Thanks again for the comments. I have to say that I've abandoned just about any hope of using the designer for building a model. I just can't bring myself to believe that it's fit for the purpose in its current incarnation.
I'll just go on doing things with the forward mapping wizard at least until Q3 gets here.
--
Stuart
I have to admit that the forward mapping functionality of the visual designer is lacking a couple of features that will make it more usable. We are currently working on improving the experience there.
I hope that after we get out the Q3 2010 release of OpenAccess you will find the visual designer fit for your needs.
Sincerely yours,
Jordan
the Telerik team

--
Stuart