tag:blogger.com,1999:blog-31319373276545760992024-03-18T11:47:50.000+02:00.net wispInteresting for me and maybe for somebody else thingsAnonymoushttp://www.blogger.com/profile/10759235722359109807noreply@blogger.comBlogger59125tag:blogger.com,1999:blog-3131937327654576099.post-87283681107455555532016-02-24T21:28:00.000+02:002016-02-24T21:32:30.349+02:00Changing default IIS 7.5 culture after installation<p>Too many nerves spent on this. So here are the steps:<br>1. In the Region settings select culture you want to set.<br>2. Go to the Administrative tab of the Region settings:<br><a href="https://lh3.googleusercontent.com/-9J_2RpwujzA/Vs4FNvdNddI/AAAAAAAABRo/JCyeG5itQZQ/s1600-h/rundll32_2016-02-24_21-19-31%25255B8%25255D.png" target="_blank"><img title="Region settings" style="border-top: 0px; border-right: 0px; background-image: none; border-bottom: 0px; padding-top: 0px; padding-left: 0px; border-left: 0px; display: inline; padding-right: 0px" border="0" alt="Region settings" src="https://lh3.googleusercontent.com/-slz4U8bFUNg/Vs4EdI-NZ_I/AAAAAAAABRs/7OUU9PnjtF0/rundll32_2016-02-24_21-19-31_thumb%25255B6%25255D.png?imgmax=800" width="603" height="772"></a></p> <p>3. Then select Copy settings and check both “Welcome screen and system accounts” and “New user accounts”<br>4. Create new application pools for each existing app pool and assign web site to the new one. For some reason old application pools won’t receive new settings as everyone’d expect.</p> <p>This was checked on windows 10 pro version.</p>Anonymoushttp://www.blogger.com/profile/10759235722359109807noreply@blogger.com0tag:blogger.com,1999:blog-3131937327654576099.post-22361639560699704582014-03-25T19:03:00.001+02:002014-03-25T19:03:42.355+02:00Preventing 302 redirect for ASP.NET WebApi 2 on 401 response<p>If you are adding asp.net WebApi inside asp.net MVC web site you probably want to respond unauthorized to some requests. But then ASP.NET infrastructure come into play and when you try to set response status code to <em>HttpStatusCode.Unauthorized </em>you will get 302 redirect to login page.</p> <p>If you are using <a href="http://www.asp.net/identity">asp.net identity</a> and owin based authentication here a code that can help to solve that issue:</p><pre class="brush: csharp;">public void ConfigureAuth(IAppBuilder app)
{
app.UseCookieAuthentication(new CookieAuthenticationOptions
{
AuthenticationType = DefaultAuthenticationTypes.ApplicationCookie,
LoginPath = new PathString("/Account/Login"),
Provider = new CookieAuthenticationProvider()
{
OnApplyRedirect = ctx =>
{
if (!IsApiRequest(ctx.Request))
{
ctx.Response.Redirect(ctx.RedirectUri);
}
}
}
});
app.UseExternalSignInCookie(DefaultAuthenticationTypes.ExternalCookie);
}
private static bool IsApiRequest(IOwinRequest request)
{
string apiPath = VirtualPathUtility.ToAbsolute("~/api/");
return request.Uri.LocalPath.StartsWith(apiPath);
}
</pre> Anonymoushttp://www.blogger.com/profile/10759235722359109807noreply@blogger.com11tag:blogger.com,1999:blog-3131937327654576099.post-88673993425435847502013-10-27T10:44:00.001+02:002013-10-27T10:44:14.285+02:00How to build xnb files with visual studio 2012 for monogame 3<p>While doing <a href="http://rbwhitaker.wikidot.com/monogame-tutorials">monogame tutorials</a>, I’ve spent a lot of time on a simple thing that could be useful for someone else. The problem is the following – monogame framework can render xnb assets (images, sounds, fonts, etc), but it has no built in way to compile them yet. It looks like monogame team is working on its own implementation of content pipeline, but its not ready yet. </p> <p>There is another open source gaming platform – cocos-2dx. In order to get his content builder you need a visual studio extension. So start visual studio 2012 extensions and updates, and search for cocos:</p> <p><a href="http://lh6.ggpht.com/-V0W3s3s9RLY/UmzSU5Re2TI/AAAAAAAABM8/y-oWqyKBJA8/s1600-h/Extensions_and_Updates_2013-10-27_10-33-01%25255B17%25255D.png"><img title="Content builder plugin" style="border-top: 0px; border-right: 0px; border-bottom: 0px; float: none; margin-left: auto; border-left: 0px; display: block; margin-right: auto" border="0" alt="Content builder plugin" src="http://lh3.ggpht.com/-CXyF9i8wmQ0/UmzSVbD9AaI/AAAAAAAABNE/79R3f6wbIOU/Extensions_and_Updates_2013-10-27_10-33-01_thumb%25255B5%25255D.png?imgmax=800" width="638" height="480"></a> After installing it, you will have a new project type – Monogame content builder</p> <p><a href="http://lh6.ggpht.com/-hzLyftFa7JE/UmzSWLD1yNI/AAAAAAAABNI/Htm5O_mhYpw/s1600-h/New_Project_2013-10-27_10-36-32%25255B7%25255D.png"><img title="Content project type" style="border-top: 0px; border-right: 0px; border-bottom: 0px; float: none; margin-left: auto; border-left: 0px; display: block; margin-right: auto" border="0" alt="Content project type" src="http://lh6.ggpht.com/-wBWS2zeTSqI/UmzSWjE3MgI/AAAAAAAABNY/ZERCczugbPc/New_Project_2013-10-27_10-36-32_thumb%25255B5%25255D.png?imgmax=800" width="640" height="442"></a></p> <p>Now after you build project, it will generate xnb file per each asset added in the project. You can add those as linked files for other projects:</p> <p><img title="2013-10-27_10-41-29" style="border-top: 0px; border-right: 0px; border-bottom: 0px; float: none; margin-left: auto; border-left: 0px; display: block; margin-right: auto" border="0" alt="2013-10-27_10-41-29" src="http://lh4.ggpht.com/-GaaPpgBnBsI/UmzSXFtjeLI/AAAAAAAABNg/sVss-6_UPZA/2013-10-27_10-41-29%25255B6%25255D.png?imgmax=800" width="357" height="315"></p> Anonymoushttp://www.blogger.com/profile/10759235722359109807noreply@blogger.com13tag:blogger.com,1999:blog-3131937327654576099.post-52415491189915262852013-05-30T14:49:00.001+03:002013-05-30T14:49:42.440+03:00Migrating from Castle ActiveRecord<p>Unfortunately <a href="http://www.castleproject.org/projects/activerecord/">Castle ActiveRecord</a> project is abandoned and <a href="http://groups.google.com/group/castle-project-devel/browse_thread/thread/5bb84ad56941a539">not developed</a> anymore. Latest release uses NHibernate 3.0 and is not compatible with last NHibernate due to the changes in API of dynamic proxy.</p> <p>So we have decided to remove dead assembly and move on. In order to do that there some major steps that needed to be done:</p> <ol> <li>Rewrite all dependent code that used <a href="http://docs.castleproject.org/Active%20Record.Using%20the%20ActiveRecordMediator%20(avoiding%20a%20base%20class).ashx">ActiveRecordMediator</a> class <li>Implement ISession, ISessionFactory lifetime management <li>Reconfigure NHibernate without using <a href="http://docs.castleproject.org/Active%20Record.Configuration%20and%20initialization.ashx">AR wrappers over configuration</a> <li>Export existing AR mappings based on attributes to XML <li>Migrate XML mappings to <a href="http://stackoverflow.com/questions/5777898/docs-examples-for-nhibernate-3-2-mapping-by-code">mapping by code</a></li></ol> <p>First two steps are tightly related. Methods in <a href="http://docs.castleproject.org/Active%20Record.Using%20the%20ActiveRecordMediator%20(avoiding%20a%20base%20class).ashx">ActiveRecordMediator</a> class are easily translated into appropriate analogs in ISession object. We already had a Repositories layer that was used to encapsulate all NHibernate related code, so it was not a really big problem to inject session to them.</p> <p>For lifetime management we implemented same solution that <a href="http://slynetblog.blogspot.com/2012/03/using-aspnet-mvc-4-webapi-with.html">was described</a> in my blog earlier – let the <a href="http://www.martinfowler.com/articles/injection.html">IoC container</a> resolve it. </p> <p>Now about third step. NHibernate has not a lot of configuration in fact. All the new API descriptions can be found <a href="http://fabiomaulo.blogspot.com/2009/07/nhibernate-fluent-configuration.html">here</a>. AR doesn’t add a lot, so nothing fancy here.</p> <h2>Export ActiveRecord mappings to XML</h2> <p>With exporting mappings things are starting to get interesting. I couldn’t find an out of the box way in AR to do that. So in fact I had to copy paste methods and to make something from AR public to support this. So <a href="https://gist.github.com/SlyNet/5670026">here</a> is a mapper class that allows exporting (btw <a href="https://github.com/SlyNet/ActiveRecord">AR sources</a> that we have used before removing it). Line 21 is a place where you have map as a string and do anything you want with it.</p> <h2>Migration from hbm.xml to code</h2> <p>Sure you can stop migration here and just keep XML mappings. But that is just not good enough. Now there is a task of converting XML mappings to code. What is the best tool to covert XML to anything else?.. Well its XSLT. Sorry for that, but it is as it is. So I’ve created a transformation that is capable to do what is needed. Source is too long to put here, so I’m putting a link to <a href="https://gist.github.com/SlyNet/5676242">appropriate gist</a>. There are some preparations need for mappings – remove all nhibernate namespaces and wrappers like </p><pre class="brush: xml;"><hibernate-mapping auto-import="true" default-lazy="false" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="urn:nhibernate-mapping-2.2">
<!-- mapping here should be kept -->
</hibernate-mapping>
</pre>
<p>It uses XSLT 2.0 that is not supported by default .net tools. To run it you can use <a href="http://saxonica.com/welcome/welcome.xml">saxonica tool</a>. In order to run it you can use following command line:</p>
<blockquote>
<p>c:\Program Files\Saxonica\SaxonPE9.4N\bin\Transform.exe -s:<em>mappings.xml</em> -xsl:<em>nbmToCs.xslt</em> -o:<em>mappings.cs</em></p></blockquote>
<p>So after doing that you will have new nice mappings. Not everything that is supported by NHibernate is supported by XSLT, I’ve added only things that we needed. If you need to add something, feel free to contact me.</p> Anonymoushttp://www.blogger.com/profile/10759235722359109807noreply@blogger.com0tag:blogger.com,1999:blog-3131937327654576099.post-34847375137972708492013-04-05T15:53:00.001+03:002013-04-05T15:53:52.342+03:00Downloading multiple files on Windows Phone 8<p>What I think is missing from the internet is a posts with examples of solving simple tasks that usually takes longer then they should. So I decided to share some. </p> <p>What I wanted to do is to download a number of images from the internet, combine them and use it as a background for my app. The simple way to do that is with the help of build in <em>BitmapImage</em> class:</p><pre class="brush: csharp;">BitmapImage image = new BitmapImage(new Uri("http://test.jpg"));
image.ImageOpened += (sender, args) => {
Process(); // here we have image downloaded
};
</pre>
<p>Each time I need to create new Uri I ask myself why there is no constructor that accepts string…</p>
<p>So what went wrong with the code above? The problem is that I needed all images content at the same time to combine them and put results to cache or something. First solution I came up with was using Interlocked.Increment() method. It solved problem in some way. But code became complicated and I didn’t like that. So here a better way to do that. In order to have determined point in time where all images are downloaded I created my file downloader helper:</p><pre class="brush: csharp;">public class ImageDownloader
{
public static async Task<MemoryStream> Download(string url)
{
var request = (HttpWebRequest)HttpWebRequest.Create(url);
var response = await Task<WebResponse>.Factory.FromAsync(request.BeginGetResponse, request.EndGetResponse, null);
var result = new MemoryStream();
using (var responseStream = response.GetResponseStream())
{
responseStream.CopyTo(result);
}
result.Position = 0;
return result;
}
}
</pre>
<p>Maybe returning Stream is not the best way to do it, but I think its ok for this example post. So code is simple and self-explanatory. It just downloads any file by provided URL. </p>
<p>Having that helper, we can compose list of tasks and execute processing just after receiving all images:</p><pre class="brush: csharp;">var imageUrls = new[] {
string.Format("http://placekitten.com/{0}/{1}", imageWidth, imageHeight),
string.Format("http://placekitten.com/g/{0}/{1}", imageWidth, imageHeight)
};
var downloadTasks = new List<Task<MemoryStream>>();
foreach (var image in imageUrls)
{
downloadTasks.Add(ImageDownloader.Download(image));
}
Task.Factory.ContinueWhenAll(downloadTasks.ToArray(), tasks => {
WriteableBitmap image1 = new WriteableBitmap(imageWidth, imageHeight);
image1.LoadJpeg(tasks[0].Result);
WriteableBitmap image2 = new WriteableBitmap(imageWidth, imageHeight);
image2.LoadJpeg(tasks[1].Result);
Image1.Source = image1;
Image2.Source = image2;
},
CancellationToken.None,
TaskContinuationOptions.None,
TaskScheduler.FromCurrentSynchronizationContext() // this will make sure we are changing UI in the thread that is allowed to do that.
);
</pre>
<p align="left">This code uses <a href="http://msdn.microsoft.com/en-us/library/dd460717.aspx" target="_blank">Task Parallel Library</a>’s method ContinueWhenAll. It allows you to execute task after all other tasks are finished. </p>
<p align="left">So that all what is needed. It is really simple, but took me a while to find the right classes and syntax to solve the problem.</p>
<p align="left">You can download a working example <a href="http://sdrv.ms/16BnsGb" target="_blank">here</a>.</p> Anonymoushttp://www.blogger.com/profile/10759235722359109807noreply@blogger.com0tag:blogger.com,1999:blog-3131937327654576099.post-3170693709871482402012-07-31T13:13:00.001+03:002012-07-31T13:13:20.186+03:00Setup kdiff3 with TortoiseGIT for 3 way merge<p>Its always an issue because you need to know what values to pass for merge tool so just as a reminder:</p> <blockquote> <p>C:\Program Files (x86)\KDiff3\kdiff3.exe %base %mine %theirs -o %merged</p></blockquote> <p><a href="http://lh5.ggpht.com/-7N9RsYaStxY/UBevt2nKj2I/AAAAAAAAA1I/jbj_WE_LlvA/s1600-h/20120731130426%25255B1%25255D%25255B5%25255D.png"><img style="background-image: none; border-right-width: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px; padding-top: 0px" title="kdiff3 as merge tool" border="0" alt="kdiff3 as merge tool" src="http://lh5.ggpht.com/-BL2MWXTgW2k/UBevvKIZrRI/AAAAAAAAA1Q/et-A8XSCno0/20120731130426%25255B1%25255D_thumb%25255B8%25255D.png?imgmax=800" width="640" height="433"></a></p> <p>The only problem with it is that when you exit kdiff3, it remains to be conflicted and you need manually mark as resolved. </p> Anonymoushttp://www.blogger.com/profile/10759235722359109807noreply@blogger.com2tag:blogger.com,1999:blog-3131937327654576099.post-16196140301841294422012-03-28T10:52:00.001+03:002012-03-28T10:52:34.245+03:00ASP.NET MVC extension points in action<p align="left">Recently I made a tech talk about my alt.net <a href="http://www.youtube.com/watch?v=KVbsNgN4wJg" target="_blank">web stack of love</a>. It has lots of things there. Validation, NHibernate, Routing etc. So here are slides:</p> <div style="width: 425px" id="__ss_12187007"><strong style="margin: 12px 0px 4px; display: block"><a title="ASP.NET MVC extension points in action" href="http://www.slideshare.net/ssuser18ec3a/mvc-extension-points-in-action" target="_blank">ASP.NET MVC extension points in action</a></strong> <iframe height="355" marginheight="0" src="http://www.slideshare.net/slideshow/embed_code/12187007" frameborder="0" width="425" marginwidth="0" scrolling="no"></iframe></div> <p align="left"><a href="https://bitbucket.org/SlyNet/mvcextensions" target="_blank">Source code</a> for it is on bitbucket. Its purpose is just see it all in action. If you want, you should move code your new/old projects. Don’t try to make some type of project template out of it.</p> <h4>To try things out</h4> <p>Once again link to <a href="https://bitbucket.org/SlyNet/mvcextensions" target="_blank">sources</a>.</p> <p>I was asked to give some kind of practice task to try all stack together. So here it is:</p> <p>Implement blog details page to show posts inside it. So when I open localhost/blogs/3 I should see something like:</p> <p align="left"><img style="background-image: none; border-bottom: 0px; border-left: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top: 0px; border-right: 0px; padding-top: 0px" title="untitled_page" border="0" alt="untitled_page" src="http://lh6.ggpht.com/-hwrq9wMZZqs/T3LDNZ-2ePI/AAAAAAAAArk/J5P-ZBS1gys/untitled_page%25255B7%25255D.png?imgmax=800" width="608" height="433" /></p> <p align="left">When I navigate to post details page I should be able to see post content and leave comments. Just like on this blog <img style="border-bottom-style: none; border-left-style: none; border-top-style: none; border-right-style: none" class="wlEmoticon wlEmoticon-smile" alt="Smile" src="http://lh4.ggpht.com/-w5Jw6byd9nY/T3LDOeYkk9I/AAAAAAAAArs/M_FXvFMdBDs/wlEmoticon-smile%25255B2%25255D.png?imgmax=800" />.</p> Anonymoushttp://www.blogger.com/profile/10759235722359109807noreply@blogger.com0tag:blogger.com,1999:blog-3131937327654576099.post-37994332871585517362012-03-05T13:16:00.001+02:002012-03-05T13:16:03.567+02:00Using ASP.NET MVC 4 WebAPI with NHibernate and Autofac<p align="left">Wanted to try how they play together. So it will yet another tutorial with sample application built from scratch. First of all I don’t want to manage ISession and ISessionFactory lifetime manually, so I’ll use <a href="http://code.google.com/p/autofac/" target="_blank">Autofac</a> to do the job. So after creating new Web API project execute following commands in <a href="http://nuget.org/" target="_blank">nuget</a> console:</p> <blockquote>Uninstall-Package EntityFramework <br />Install-Package NHibernate <br />Install-Package Autofac.Mvc3 </blockquote> <p align="left">First remove EntityFramework, then install <a href="http://nhforge.org/" target="_blank">NHibernate</a> and <a href="http://code.google.com/p/autofac/wiki/MvcIntegration" target="_blank">Autofac.Mvc3</a> package. The last package has some really useful extensions like implementation of dependency resolver for MVC and instance per web request life style. Now setup autofac:</p> <pre class="brush: csharp;">var builder = new ContainerBuilder();
// Register ISessionFactory as Singleton
builder.Register(x => NHibernateConfigurator.BuildSessionFactory())
.SingleInstance();
// Register ISession as instance per web request
builder.Register(x => x.Resolve<ISessionFactory>().OpenSession())
.InstancePerHttpRequest();
// Register all controllers
builder.RegisterAssemblyTypes(typeof(ProductsController).Assembly)
.InNamespaceOf<ProductsController>()
.AsSelf();
// override default dependency resolver to use Autofac
DependencyResolver.SetResolver(new AutofacDependencyResolver(builder.Build()));
// this override is needed because WebAPI is not using DependencyResolver to build controllers
GlobalConfiguration.Configuration.ServiceResolver.SetResolver(
DependencyResolver.Current.GetService,
DependencyResolver.Current.GetServices);</pre>
<p>This code is executed one time on Application start. I won’t put code for domain (its simple one <a href="https://github.com/SlyNet/BlogSamples/blob/master/WebApiSample/webapi/Models/Product.cs" target="_blank">entity</a>) and <a href="https://github.com/SlyNet/BlogSamples/blob/master/WebApiSample/webapi/Infrastructure/NHibernateConfigurator.cs" target="_blank">NHibernateConfigurator</a> class you can find them on github.</p>
<p>Now we are ready to add our first controller that is going to expose web api:</p>
<pre class="brush: csharp;">public class ProductsController : ApiController
{
private readonly ISession nhSession;
public ProductsController(ISession nhSession)
{
if (nhSession == null) throw new ArgumentNullException("nhSession");
this.nhSession = nhSession;
}
public IQueryable<Product> Get()
{
return nhSession.Query<Product>();
}
}</pre>
<p>Notice that I don’t need to do anything to get the ISession instance. Autofac will find that in order to get it it needs ISessionFactory and will configure factory first to give ISession for controller. Now we can visit url http://localhost:54270/api/products and see our list of products in XML format. Notice that because returned type is IQueryable request to http://localhost:54270/api/products?$top=1&$skip=0 will return only first product from the list.</p>
<p>PUT, POST and DELETE methods are pretty straight forward and won’t be different from the same in entity framework. So I won’t cover it. The last thing I want to try is transaction management. In mvc projects I <a href="http://slynetblog.blogspot.com/2011/04/lightweight-nhibernate-and-aspnet-mvc.html" target="_blank">used to do it with action filter</a>. Here is slight catch involved. There are two ActionFilterAttribute classes. One in <a href="http://msdn.microsoft.com/en-us/library/system.web.http.filters.actionfilterattribute(v=vs.108).aspx" target="_blank">System.Web.Http.Filters</a> and other is in <a href="http://msdn.microsoft.com/ru-ru/library/system.web.mvc.actionfilterattribute.aspx" target="_blank">System.Web.Mvc</a>. In order to get working in webapi we need to implement the one in System.Web.Http.Filters namespace. So the implementation is the following:</p>
<pre class="brush: csharp;">using System.Data;
using System.Web.Mvc;
using NHibernate;
using ActionFilterAttribute = System.Web.Http.Filters.ActionFilterAttribute;
namespace webapi.Infrastructure
{
public class TransactionAttribute : ActionFilterAttribute
{
public override void OnActionExecuting(System.Web.Http.Controllers.HttpActionContext actionContext)
{
base.OnActionExecuting(actionContext);
DependencyResolver.Current.GetService<ISession>().BeginTransaction(IsolationLevel.ReadCommitted);
}
public override void OnActionExecuted(System.Web.Http.Filters.HttpActionExecutedContext actionExecutedContext)
{
base.OnActionExecuted(actionExecutedContext);
ITransaction currentTransaction = DependencyResolver.Current.GetService<ISession>().Transaction;
try
{
if (currentTransaction.IsActive)
if (actionExecutedContext.Exception != null)
currentTransaction.Rollback();
else
currentTransaction.Commit();
}
finally
{
currentTransaction.Dispose();
}
}
}
}</pre>
<p>I’ve put all the code there just be sure that you can figure out all required namespaces. The last thing I want to notice here is that if you implement System.Web.Mvc version of action filter, you won’t see any error messages or exceptions. Your filter just won’t work.</p>
<p>All code you can find <a href="https://github.com/SlyNet/BlogSamples/tree/master/WebApiSample/webapi" target="_blank">here</a>. </p> Anonymoushttp://www.blogger.com/profile/10759235722359109807noreply@blogger.com8tag:blogger.com,1999:blog-3131937327654576099.post-30914024567636493582012-03-01T09:33:00.001+02:002012-03-01T09:33:26.354+02:00Convert tfs repository to mercurial<p>The easiest way I’ve found to do it is the following:</p> <ol> <li> <div align="left">With the help of <a href="https://github.com/git-tfs/git-tfs" target="_blank">git-tfs</a> tool convert tfs repository to git one with the following command: <br /> <blockquote>git tfs clone %FullUrlToYourTfsServer% $/%PathToProject% </blockquote> <br />I suggest you to verify that your network connection to TFS is stable because this operation will take a lot of time and if it fail you will need to start from the beginning. </div> </li> <li> <div>Have hg installed (I’m using tortoisehg) and configure its <a href="http://mercurial.selenic.com/wiki/ConvertExtension" target="_blank">ConvertExtension</a>. <br />In order to do that navigate to C:\Users\%UserName% and open mercurial.ini file, and add this at the end: <br /> <blockquote>[extensions] hgext.convert= </blockquote> </div> </li> <li>Execute convert command on git repository with following command: <br /> <blockquote>hg convert -s git -d hg %PathToGitRepository% </blockquote> </li> <li>Have fun with hg</li> </ol> Anonymoushttp://www.blogger.com/profile/10759235722359109807noreply@blogger.com2tag:blogger.com,1999:blog-3131937327654576099.post-1514169333866226462011-11-22T14:42:00.001+02:002012-03-13T17:07:36.668+02:00Implementing Repository with NHibernate<p align="left">In spite of common now approach of using in memory data base for unit testing NHibernate related code I still do like to have repository. The reason for that is simplicity. In most applications transactions are managed separately, either via action filters or HTTP modules. So in unit test you need to repeat logic not for just creating of object graph, but for saving it also. </p> <p align="left">What I always wanted is ability to write code like this in tests:</p> <pre class="brush: csharp;">var product = new Product {
Price = 100,
Name = "Test"
};
product.Category = new Category {
Name = "Food"
};
IRepository<Product> products = new List<Product>();</pre>
<p align="left">And all the logic for testing queries can be done with LINQ to objects (you <strong>will </strong>need integration tests to verify real query generated by the ORM). No need for huge test setup and so on. </p>
<p align="left">With release of on NH 3 LINQ provider was greatly improved (but still has a lot of troubles). In this post I’m going to show implementation of Repository described in <a href="http://fabiomaulo.blogspot.com/2009/09/repository-or-dao-repository.html" target="_blank">Fabio’s post</a>. The main idea described there is that IRepository interface should just look like this:</p>
<pre class="brush: csharp;">/// <summary>
/// Repository for basic entities persistence actions
/// </summary>
/// <typeparam name="T"></typeparam>
public interface IRepository<T> : ICollection<T>, IQueryable<T>
where T : Entity
{
T Get(long id);
}</pre>
<p>The only additional method is Get. Its just useful in a lot of cases. Everything else is provided with mentioned interfaces. Implementation from NHibernate point of view is pretty straight forward and I won’t describe it, you can see in the project sample. The interesting part is mocking one. Here is code for one of the methods:</p>
<pre class="brush: csharp;">public class FakeRepository<T> : List<T>, IRepository<T>
where T : Entity
{
public FakeRepository(IEnumerable<T> products) : base(products)
{
}
public Expression Expression
{
get
{
return ((IEnumerable<T>)this).Select(x => x).AsQueryable().Expression;
}
}
}</pre>
<p>So now we can implement test like this:</p>
<pre class="brush: csharp;">[Test]
public void Repository_can_be_created_from_simple_list()
{
Product product = new Product();
List<Product> products = new List<Product>();
products.Add(product);
IRepository<Product> repository = new FakeRepository<Product>(products);
Assert.That(repository, Is.Not.Empty);
}</pre>
<p>And it will pass. Also all production code can do any sort of LINQ queries and they will succeed.</p>
<p>The last and probably the most scary thing - <a href="http://mikehadlow.blogspot.com/2010/08/nhibernate-linq-eager-fetching.html" target="_blank">fetching</a>. LINQ to NHibernate has Fetch and FetchMany extension methods. But when you use them on regular list exception is thrown:</p>
<blockquote>
<p>System.InvalidOperationException : There is no method 'Fetch' on type 'NHibernate.Linq.EagerFetchingExtensionMethods' that matches the specified arguments</p>
</blockquote>
<p>So we need to abstract fetching away. In order to do that, we will need our own Fetch and FetchMany methods and IFetchRequest interface with the following signature:</p>
<pre class="brush: csharp;">public interface IFetchRequest<TQueried, TFetch> : IOrderedQueryable<TQueried>
{
}</pre>
<p>Instance of this interface will be returned as a result of calls to our new extension methods, that look like this:</p>
<pre class="brush: csharp;">public static class EagerFetch
{
public static IFetchRequest<TOriginating, TRelated> Fetch<TOriginating, TRelated>(this IQueryable<TOriginating> query, Expression<Func<TOriginating, TRelated>> relatedObjectSelector)
{
return FetchingProvider().Fetch(query, relatedObjectSelector);
}
// ... other methods
public static Func<IFetchingProvider> FetchingProvider = () => new NhFetchingProvider();
}</pre>
<p>I’m showing only method here, others are implemented in the same way (and yes, it is ugly). But the good news is that you write it once, and forget. Interesting part is FetchingProvider that performs fetching itself. The instance of provider is provided by Func, that means that in tests you can easily change provider instance. With such code somewhere in test fixture setup:</p>
<pre class="brush: csharp;">EagerFetch.FetchingProvider = () => new FakeFetchingProvider();</pre>
<p>Implementation of NHibernate provider is on the <a href="https://github.com/SlyNet/BlogSamples/blob/master/RepositorySample/RepositorySample/Implementations/Nh/NhFetchingProvider.cs" target="_blank">github</a> (together with <a href="https://github.com/SlyNet/BlogSamples/blob/master/RepositorySample/RepositorySample.Tests/FakeFetchingProvider.cs" target="_blank">fake provider</a>). FakeProvider in its turn just doing nothing. But in theory we can mock it and set some verifications, but I don’t think it’s a good idea.</p>
<p>Full source code with working solution you can find on the <a href="https://github.com/SlyNet/BlogSamples" target="_blank">github</a>.</p> Anonymoushttp://www.blogger.com/profile/10759235722359109807noreply@blogger.com3tag:blogger.com,1999:blog-3131937327654576099.post-34096210938246284192011-10-21T10:41:00.001+03:002011-10-21T10:45:11.877+03:00Setup SQL Server Compact 4 to unit test NHibernate related code<p>Most of the time to fake data base SQL lite data base is used. But it has certain differences from SQL server. With release of SQL Compact 4 it becomes really good choice to mock data base calls. So in this post I will describe a way of setting up NHibernate to work with SQL CE local data base. So here are our goals:</p> <ol> <li>Each <strong>test fixture</strong> has its own fresh DB instance </li> <li>NHibernate SessionFactory is same for all the tests (performance is still important in tests)</li> <li>Caching of NHibernate doesn’t stands on isolation way (each test will have clean factory, without any cached entities) </li> <li>Works just after getting from source control with no additional configuration </li> </ol> <p>First of all we need to install <a href="http://go.microsoft.com/fwlink/?LinkId=212219" target="_blank">sql ce tools for visual studio</a>. After done that we can add an empty data base file to our tests project:</p> <p><img style="display: block; float: none; margin-left: auto; margin-right: auto" title="Sql server compact 4.0 local data base " alt="Sql server compact 4.0 local data base " src="http://lh5.ggpht.com/-b_WMws5-LWw/TqEiQhbNkKI/AAAAAAAAAjA/2WshDd7fL4Y/Screenshot-2011-10-20_17.48.125.png?imgmax=800" width="491" height="66" /></p> <p align="left">lets call it db. This file is going to be the one that is going to be copied for each test and where NHibernate will create its tables. </p> <p align="left">Now lets create a base class for tests that are going to use NHibernate:</p> <pre class="brush: csharp;">public class DbTests
{
protected static ISessionFactory factory;
static Configuration nhConfig;
static DbTests()
{
File.Copy("db.sdf", "Temp.sdf", true);
nhConfig = NhConfigure();
factory = nhConfig.BuildSessionFactory();
}
[TestFixtureSetUp]
public void Setup()
{
File.Copy("db.sdf", "Temp.sdf", true);
new SchemaExport(nhConfig).Execute(true, true, false);
}
[TestFixtureTearDown]
public void TearDown()
{
File.Delete("Temp.sdf");
}
static Configuration NhConfigure()
{
DomainMapper mapper = new DomainMapper();
HbmMapping mappings = mapper.CompileMappingFor(new[] { typeof(TestEntity) });
var configuration = new Configuration();
configuration.SessionFactory()
.Integrate.Using<MsSqlCe40Dialect>()
.Connected.By<SqlServerCeDriver>()
.Using("Data Source=Temp.sdf");
configuration.AddDeserializedMapping(mappings, "domain");
return configuration;
}
}</pre>
<p>What is done here is pretty straight forward. Each <strong><a href="http://www.nunit.org/index.php?p=testFixture&r=2.2.10" target="_blank">test fixture</a> </strong>will get its own empty data base with new schema installed. </p>
<p>Also you will need to install <a href="http://nuget.org/List/Packages/SqlServerCompact" target="_blank">SqlServerCompact</a> package from nuget in order to get SqlServerCeDriver support.</p>
<p><em>There is an interesting bug when using identity columns with SQL CE. You can get NHibernate.AssertionFailure : null identifier exception. </em><a href="http://stackoverflow.com/questions/2361730/assertionfailure-null-identifier-fluentnh-sqlserverce" target="_blank"><em>Here</em></a><em> is how you can solve it.</em></p>
<p>The last thing we want to take care about is NHibernate cache. Each test probably will have its own ISession instance, so only second level caching should be handled. Here is how we can clean up it:</p>
<pre class="brush: csharp;">static void ClearCache()
{
factory.EvictQueries();
foreach (var collectionMetadata in factory.GetAllCollectionMetadata())
factory.EvictCollection(collectionMetadata.Key);
foreach (var classMetadata in factory.GetAllClassMetadata())
factory.EvictEntity(classMetadata.Key);
}</pre>
<p>Just add this method call to Setup and that it. As always working sample attached:</p>
<iframe style="padding-bottom: 0px; background-color: #fcfcfc; padding-left: 0px; width: 98px; padding-right: 0px; height: 115px; padding-top: 0px" title="Preview" marginheight="0" src="https://skydrive.live.com/embedicon.aspx/.Public/NHTests.zip?cid=44848200dcefa4cd&sc=documents" frameborder="0" marginwidth="0" scrolling="no"></iframe>
<p>Source code sample doesn’t contain nuget packages and uses <a href="http://docs.nuget.org/docs/workflows/using-nuget-without-committing-packages" target="_blank">this way</a> of working. So don’t be scared of everything red in ReSharper after open solution. Just build it.</p> Anonymoushttp://www.blogger.com/profile/10759235722359109807noreply@blogger.com0tag:blogger.com,1999:blog-3131937327654576099.post-19062042928016242522011-10-07T17:42:00.001+03:002011-10-07T17:42:44.088+03:00Getting started with knockout.js<p>Recently I’ve had some time to learn <a href="http://knockoutjs.com/" target="_blank">knockout.js</a>. It’s a javascript library for building rich internet applications. In spite of wonderful tutorials section on main site learning wasn’t as smooth as I would like it to be. Mainly because of some changes in 1.3 version and version 1.2 (that is currently used for tutorials). </p> <p align="left">Before reading further I would suggest you to watch a great <a href="http://channel9.msdn.com/Events/MIX/MIX11/FRM08" target="_blank">video about knockout.js</a>. After seeing it I’m not sure that you need to read further <img style="border-bottom-style: none; border-left-style: none; border-top-style: none; border-right-style: none" class="wlEmoticon wlEmoticon-smile" alt="Smile" src="http://lh5.ggpht.com/-Dl5zodzbPwc/To8P4AzlTDI/AAAAAAAAAik/9IQiLclB2eA/wlEmoticon-smile2.png?imgmax=800" />.</p> <p>Now we are going to build an editing form for <a href="http://www.microsoft.com/download/en/details.aspx?id=23654" target="_blank">Northwind data base</a> products. The desired result is the following form:</p> <p><img style="display: inline" title="Products editing form" alt="Products editing form" src="http://lh4.ggpht.com/-W7kzdoQLybs/To8P4nICMuI/AAAAAAAAAio/Jo9eg95eIeQ/Screenshot-2011-10-07_11.27.232.png?imgmax=800" width="695" height="380" /></p> <p>So when page is loaded only categories list is visible at the left. When user selects category, list of products is shown where user is able to select concrete product to edit and save.</p> <p>So lets create a new ASP.NET MVC 3 internet web application. As data access layer we’ll use Entity Framework. So add new ADO.NET Entity Data Model, name it Northwind and point to your DB instance. </p> <p>To get started we need a list of categories, so navigate to HomeController and add the following code to Index action:</p> <pre class="brush: csharp;">public ActionResult Index()
{
using (Northwind context = new Northwind())
{
var categories = context.Categories.Select(c => new {c.CategoryID , c.CategoryName}).ToList();
ViewBag.Categories = categories;
return View();
}
}</pre>
<p>That’s it for now on server side. Now navigate to Index.cshtml view where everything interesting is going to happen. In order to get knockout working execute next nuget command: <em>Install-Package Knockoutjs. </em>It will download latest version of knockout library and place it under Scripts folder. Include it in your view.</p>
<p>First of all we need to render a list of categories. Knockout 1.3 has build in ability to generate html based on template. So to render list of categories add the following html with javascript on view:</p>
<pre class="brush: xml;"><ul data-bind="foreach: categories">
<li>
<a data-bind="text: $data.CategoryName"
href="javascript:void(0);">
</a>
</li>
</ul>
<script type="text/javascript">
var viewModel = {
categories: @Html.Raw(Json.Encode(ViewBag.Categories))
};
ko.applyBindings(viewModel);
</script></pre>
<p>It will render li with anchor for each category that came from server. Notice that in order to get JSON representation of categories list Json.Encode method is used. </p>
<p>On this small example we already can see the MVVM pattern in action. View is bounded to model that is stored in js objects. So how we have strong separation of data from its representation even on client side. We can apply unit testing of javascript without messing with UI and have all the procs of concerns separation (e.g. build other UI for mobile devices).</p>
<p>Now lets work with some events. We want to show products when some category is selected. Lets create a server side logic for retrieving products in category. Add a new controller like this:</p>
<pre class="brush: csharp;">public class ProductsController : Controller
{
public ActionResult InCategory(int id)
{
using (Northwind context = new Northwind())
{
var result = context.Products.Where(x => x.CategoryID == id)
.Select(p => new {p.ProductID, p.ProductName})
.ToList();
return Json(result, JsonRequestBehavior.AllowGet);
}
}
}</pre>
<p>So when category is clicked we should somehow call this method and show returned results. Lets add a method for our view model that should be called when category is selected and bind click event to it. Here what we should get:</p>
<pre class="brush: xml;"><a data-bind="text: $data.CategoryName,
click: function(){ viewModel.selectCategory($data.CategoryID); }"
href="javascript:void(0);">
</a>
<script type="text/javascript">
var viewModel = {
categories: @Html.Raw(Json.Encode(ViewBag.Categories)),
selectCategory: function(categoryId) {
console.log(categoryId);
}
};
ko.applyBindings(viewModel);
</script></pre>
<p align="left">Now if you open <a href="http://getfirebug.com/" target="_blank">FireBug</a> and refresh a page when you click on category you will see its id being printed in console.  Now lets store selected category id (we will need it further in tutorial). In order to do it, lets add a new property for viewModel and set in selectCategory method:</p>
<pre class="brush: js;">selectedCategory: ko.observable(),
selectCategory: function(categoryId) {
this.selectedCategory(categoryId);
}</pre>
<p>Couple of things needs to be noticed here: initial value of the selectedCategory is ko.observable – it will create an empty value, but when this value is changed all interested in it parts of application will be notified. Second thing is that assigning value is done not via =, but with calling that property and passing value. Now we are ready to display list of products. In order to do it lets add a table template: </p>
<pre class="brush: xml;"><table>
<thead>
<th>
ProductName
</th>
</thead>
<tbody data-bind="foreach: products">
<tr>
<td>
<a data-bind="text: $data.ProductName"
href="javascript:void(0);">
</a>
</td>
</tr>
</tbody>
</table></pre>
<p>So we have a table that is bound to the products field of view model. Now we need fill this collection:</p>
<pre class="brush: js;">viewModel.products = ko.observableArray([]);
ko.dependentObservable(function() {
if(this.selectedCategory()) {
$.get('@Url.Action("InCategory", "Products")/' + this.selectedCategory(), this.products);
}
}, viewModel);</pre>
<p>With the help of dependentObservable method we can create a property that is going to change when another property changes. Knockout will figure out by himself that this method should be called when selectedCategory method is called. So now you should have working list of categories with ability to view products in it.</p>
<p>Next step is displaying and edit form. Steps should be already familiar. Lets add a server side method for retrieving order by its id:</p>
<pre class="brush: csharp;">public ActionResult Get(int id)
{
using (Northwind context = new Northwind())
{
var result = from p in context.Products
where p.ProductID == id
select new { p.ProductID, p.UnitPrice, p.ProductName, p.UnitsInStock, p.UnitsOnOrder };
return Json(result.FirstOrDefault(), JsonRequestBehavior.AllowGet);
}
}</pre>
<p>And on the client side:</p>
<pre class="brush: js;">viewModel.selectedProductId = ko.observable();
viewModel.selectedProduct = ko.observable(‘’);
viewModel.selectProduct = function(productId) {
viewModel.selectedProductId(productId);
};
ko.dependentObservable(function() {
if(this.selectedProductId()) {
$.get('@Url.Action("Get", "Products")/' + this.selectedProductId(), this.selectedProduct);
}
}, viewModel);</pre>
<p>After binding a click event of product anchor to selectProduct method we need last thing to do – implement template for editing:</p>
<pre class="brush: xml;"><fieldset>
<legend data-bind="text: selectedProduct().ProductName">
</legend>
<dl>
<dt>
Product name
</dt>
<dt>
<input type="text" name="ProductName" data-bind="value: selectedProduct().ProductName" />
</dt>
</dl>
</fieldset></pre>
<p>So now user is able to select category and product. I won’t cover saving data to db in this tutorial. Lets add some nice features. For example we want to highlight selected category and selected product in order to show user where he is now. Lets add a new style called current:</p>
<pre class="brush: css;">.selected
{
background-color: Aqua;
}</pre>
<p>And we want apply it to current category and product. Now we can do it with just only bindings:</p>
<pre class="brush: js;">css: {selected: $data.CategoryID === viewModel.selectedCategory()}
css: {selected: $data.ProductID === viewModel.selectedProductId()}</pre>
<p>First one goes for category anchor template, second one is for product item template in list. </p>
<p>Full source code for this example:</p>
<iframe style="padding-bottom: 0px; background-color: #fcfcfc; padding-left: 0px; width: 98px; padding-right: 0px; height: 115px; padding-top: 0px" title="Preview" marginheight="0" src="https://skydrive.live.com/embedicon.aspx/.Public/Knockout.zip?cid=44848200dcefa4cd&sc=documents" frameborder="0" marginwidth="0" scrolling="no"></iframe> Anonymoushttp://www.blogger.com/profile/10759235722359109807noreply@blogger.com6tag:blogger.com,1999:blog-3131937327654576099.post-8898330908515092702011-09-30T15:57:00.001+03:002011-09-30T15:57:09.858+03:00Yet another scripts and styles combining and minification in ASP.NET<p>In recent project we had to optimize application for mobile devices with slow internet connection. Obviously among other we need to combine, minify and gzip scripts/styles to reduce their size. And that’s how project I’m going to describe here has started. Its free, open source and <a href="https://bitbucket.org/devkit/assets-optimizer/" target="_blank">hosted on bitbucket</a>.</p> <p>Examples on how to use it you can find on the <a href="https://bitbucket.org/devkit/assets-optimizer/wiki/Home" target="_blank">wiki page</a>. Here I want describe a little about how it it done and how it works.</p> <h3>nuget</h3> <p>Of course I’ve created a <a href="http://nuget.org/List/Packages/AssetsOptimizer" target="_blank">nuget package</a> <img style="border-bottom-style: none; border-left-style: none; border-top-style: none; border-right-style: none" class="wlEmoticon wlEmoticon-smile" alt="Smile" src="http://lh4.ggpht.com/-xoGkqhL0iQo/ToW8o_MDAEI/AAAAAAAAAig/hRc6oQqFK5k/wlEmoticon-smile%25255B2%25255D.png?imgmax=800" />. During installation it registers required http handler and creates required configuration section. The only issue I can see here is that when you are going to update on new versions it may reset your adjustments in configuration. But currently its not a lot of them so you can restore it. </p> <h3>Rendering</h3> <p>During debug mode all the scripts/styles references will be outputted separately and non minified. Its useful for debugging and development.</p> <p>In release mode all the references are combined in single <em>style/script</em> reference with query string looking something like:</p> <pre class="brush: xml;">/Scripts/assets.axd?js=jquery-1.4.1,main&cache=akqipjXPAq2UFLgARybjJ9QwjiA%3d</pre>
<p>In this case client browser will do less web requests for the page and will receive all the static content in one go.</p>
<h3>Client side cache</h3>
<p align="left">URL for assets has two parameters. File names and cache. Meaning of first one is obvious, but the second one is more interesting. It is <a href="http://en.wikipedia.org/wiki/SHA-1" target="_blank">SHA1</a> code of combined file contents of all assets that are included in this reference. So if you change any of that scripts this parameter will change. It gives us the ability to easily allow public caching of results for the reference meaning that even proxy servers will cache it. And the uniqueness of this URL will allow you to update files without asking users to press ctrl + f5 in order to get new version of content.</p>
<h3>Server side cache</h3>
<p>On server side two things are cached. </p>
<p>Minified js and css files. Its obvious that we shouldn’t minify files on each request, so its done once and placed in cache. Also each minified cache entry has a <a href="http://msdn.microsoft.com/en-us/library/system.web.caching.cachedependency.aspx" target="_blank">cache dependency</a> on file system meaning that if you change file it will expire the cache. So you can modify static content just in production without any restrictions – new file will be delivered on next request.</p>
<p>Digital signature. signature is calculated once and is places in cache. It also has a dependency on files its signing. So if anything changes, signature will also change.</p>
<h3>Unit tests</h3>
<p>I’ve tried <a href="https://github.com/machine/machine.specifications" target="_blank">MSpec</a> on this project as testing framework. And I really liked it. For me the main problem in tests was the test fixture setup. It was always bigger than action, code under test and results verification part.  But look at this test:</p>
<pre class="brush: csharp;">[Subject(typeof(AssetsHandler))]
public class When_compress_is_true_and_client_accepts_gzip : compression_spec
{
Establish context = () => {
Compress = true;
AcceptEncoding = "gzip";
};
Because of = () => handler.ProcessRequest(httpContext.Object);
It should_set_gzip_response_filter = () =>
response.Filter.ShouldBeOfType<GZipStream>();
It should_set_gzip_response_header = () =>
responseMock.Verify(x => x.AppendHeader("Content-Encoding", "gzip"));
}</pre>
<p>Its just great! All unnecessary code ceremony is moved to compression_spec class. In the test itself you see only required part. </p>
<h3>Where to store script references before rendering?</h3>
<p>In order to output all references at one go assets optimizer needs a place where to accumulate all required references. In ASP.NET its easy to implement because we have an <em>AssetsManager</em> control. During Init event it places <em>this </em>pointer into <a href="http://msdn.microsoft.com/en-us/library/system.web.ui.page.items.aspx" target="_blank">Page.Items</a> state (before writing this manager, I didn’t know this state even exists <img style="border-bottom-style: none; border-left-style: none; border-top-style: none; border-right-style: none" class="wlEmoticon wlEmoticon-smile" alt="Smile" src="http://lh4.ggpht.com/-xoGkqhL0iQo/ToW8o_MDAEI/AAAAAAAAAig/hRc6oQqFK5k/wlEmoticon-smile%25255B2%25255D.png?imgmax=800" />) and verifies that no other assets managers has placed themselves to this storage. Each proxy object in own event just finds that single manager in page state and registers all the styles and scripts there. </p>
<p>In ASP.NET MVC it’s a little bit more interesting. We don’t have manager there and need to find another state where to store references. The best place for the references I’ve found is <a href="http://msdn.microsoft.com/en-us/library/system.web.httpcontext.items.aspx" target="_blank">HttpContext.Items</a>. So each call to <em>Assets.Script</em> or <em>Assets.Style</em> just places a reference and renders nothing. And when Assets.Render() is called all the collected references are picked up from HttpContext. So if some of your scripts are not rendered, make sure that you register it before <em>Render</em>  method is called.</p>
<h3>What next</h3>
<p>What next I think would be useful:</p>
<ol>
<li>Components – ability to output assets in groups, not in one go </li>
<li>Control over order – maybe automatic, maybe by providing some king of sort order property that developer could set </li>
<li>Routing – nice url for handler </li>
</ol> Anonymoushttp://www.blogger.com/profile/10759235722359109807noreply@blogger.com1tag:blogger.com,1999:blog-3131937327654576099.post-40304637114476377962011-07-22T17:45:00.001+03:002011-07-22T17:45:27.918+03:00Slides and code<p>I promised to publish slides and code from my talk about unit testing. Here they are:</p> <div style="width: 425px" id="__ss_8664254"><strong style="margin: 12px 0px 4px; display: block"><a title="Unit tests best practices" href="http://www.slideshare.net/ssuser18ec3a/unit-tests-best-practices">Unit tests best practices</a></strong><object id="__sse8664254" width="425" height="355"><param name="movie" value="http://static.slidesharecdn.com/swf/ssplayer2.swf?doc=unittestsbestpractices-110722092859-phpapp02&stripped_title=unit-tests-best-practices&userName=ssuser18ec3a" /><param name="allowFullScreen" value="true" /><param name="allowScriptAccess" value="always" /><embed name="__sse8664254" src="http://static.slidesharecdn.com/swf/ssplayer2.swf?doc=unittestsbestpractices-110722092859-phpapp02&stripped_title=unit-tests-best-practices&userName=ssuser18ec3a" type="application/x-shockwave-flash" allowscriptaccess="always" allowfullscreen="true" width="425" height="355"></embed></object></div> <p>And a source code:</p> <iframe style="padding-bottom: 0px; background-color: #fcfcfc; padding-left: 0px; width: 98px; padding-right: 0px; height: 115px; padding-top: 0px" title="Preview" marginheight="0" src="https://skydrive.live.com/embedicon.aspx/.Public/Examples.zip?cid=44848200dcefa4cd&sc=documents" frameborder="0" marginwidth="0" scrolling="no"></iframe> Anonymoushttp://www.blogger.com/profile/10759235722359109807noreply@blogger.com0tag:blogger.com,1999:blog-3131937327654576099.post-57528122344849495322011-06-09T12:06:00.001+03:002011-06-14T10:52:03.351+03:00Getting started with NHibernate and ConfOrm (revamp)<p align="left">I’m trying to post different things about NHibernate, but still post NHibernate for beginners is most popular. I wrote them in 2009 (Wow, how old I am! :D). First of all its in Russian and I think things have changed a bit since that time, mainly because of awesomeness of <a href="http://nuget.codeplex.com/" target="_blank">NuGet</a>. This getting started will contain not only recommended mapping technique, but also references to some other useful things like GetHashCode, Equals implementations, transactions management, etc.</p> <p>So lets get started. First of all <a href="http://visualstudiogallery.msdn.microsoft.com/27077b70-9dad-4c64-adcf-c7cf6bc9970c" target="_blank">install NuGet</a>. Create a new empty <a href="http://www.asp.net/mvc/mvc3" target="_blank">MVC 3</a> project and add two class library projects name them %WhatYouLike%.Core and %WhatYouLike%.Data. First assembly will contain all business entities that NHibernate will save and load from data base. Data assembly is going to be used for NHibernate configuration, repositories, etc. </p> <p>So when projects are created lets install NHibernate. In visual studio navigate to Tools –> Library Package Manager –> Package Manager Console. In the opened command prompt select your MVC project as default project and execute command: “<em>Install-Package ConfOrm</em>”. Do the same for Data project.</p> <p align="left"><a href="http://code.google.com/p/codeconform/" target="_blank">ConfOrm</a> is an open source project that allows you to map all your domain model without any XML. I do use ConfOrm instead of <a href="http://fluentnhibernate.org/" target="_blank">Fluent NHibernate</a> in this example just because first one is going to be added in core of NHibernate, so it is preferable (but of course not necessary) to get familiar with it. </p> <blockquote> <p align="left">Just can’t allow myself to forget about some political issues with ConfOrm and Fluent NHibernate :). ConfOrm project was started by <a href="http://fabiomaulo.blogspot.com/2010/02/conform-nhibernate-un-mapping.html" target="_blank">Fabio Maulo</a> who is lead of NHibernate project (correct me here if I’m wrong). And 3.2 version of NHibernate was released with mapping in code part that basically was very similar to ConfOrm way of doing things. </p> <p align="left">This fact has raised a little storm in twitter and James Gregory (author of Fluent NHibernate) wrote a <a href="http://lostechies.com/jamesgregory/2011/04/13/me-on-nhibernate-3-2/" target="_blank">blog post</a> to explain what he thinks about it. </p> <p align="left">Some time later Fabio wrote a <a href="http://fabiomaulo.blogspot.com/2011/04/me-on-fluent-nhibernate.html" target="_blank">response post</a> where he has shown how you can reuse your Fluent mappings with new API :). </p> <p align="left">So, don’t where it will end, but politics in open source space also takes place :). I recommend you to read mentioned two posts, just to be informed. </p> </blockquote> <p>Now lets get back to our project. First thing we are going to do is add some entities to work with. NHibernate uses POCO objects. It means that you don’t need to inherit from base classes or implement some interfaces in order to create an entity that is going to be managed by NHibernate. Two things required:</p> <ol> <li>Entity should have public/protected default constructor (constructor with no parameters) </li> <li>All properties and methods should be virtual </li> </ol> <p>So lets create some entities with associations and properties. Here is example domain model I’ve created:</p> <p><img style="display: block; float: none; margin-left: auto; margin-right: auto" title="domain model" alt="domain model" src="http://lh4.ggpht.com/-qZz8f7fOJj0/TfCNBy3YTJI/AAAAAAAAAeI/DYqrorHAb20/2011-06-08_1829%25255B5%25255D.png?imgmax=800" width="407" height="387" /></p> <p>Here is source code for one of the entities:</p> <pre class="brush: csharp;">using System.Collections.Generic;
namespace nhrevamp.Core
{
public class Post : Entity
{
public Post()
{
this.Comments = new HashSet<Comment>();
this.Tags = new HashSet<Tag>();
}
public virtual string Title { get; set; }
public virtual string Content { get; set; }
public virtual ICollection<Comment> Comments { get; protected set; }
public virtual ICollection<Tag> Tags { get; protected set; }
}
}</pre>
<p>All entities inherit from base class Entity with single property Id:</p>
<pre class="brush: csharp;">public abstract class Entity
{
public virtual int Id { get; protected set; }
}</pre>
<p>Notice that we are using ICollection from System.Collections.Generic not from Iesi.Collections. The reason for that is to keep our domain model as clean as possible. Also we initialize collections in constructor in order to avoid nasty NullReferenceException’s. And also we mark setters for collections as protected, just to avoid possibility to replace all collection with new one accidently. </p>
<p>So we have:</p>
<ol>
<li>One to many association (Post has many Comments) </li>
<li>Many to one association (each Comment belongs to one Post) </li>
<li>Many to many association (Post has many Tags and Tag has many Posts) </li>
</ol>
<p align="left">NHibernate has two main interfaces that you are going to work with. First is ISessionFactory. Instance of it should be used as a <a href="http://en.wikipedia.org/wiki/Singleton_pattern" target="_blank">singleton</a> and configured only once. This operation will take time, especially for complex domain models. The good news is that ISessionFactory is serializable and are <a href="http://vimeo.com/16225792" target="_blank">able to cache it</a> in order to not recreate it each time. Second interface is ISession. This one is used to query entities, update and delete them. Creation of ISession object is <a href="http://ayende.com/blog/4123/what-is-the-cost-of-opening-a-session" target="_blank">small</a>, but you should consider some lifetime management issues with ISession. <a href="http://slynetblog.blogspot.com/2011/04/lightweight-nhibernate-and-aspnet-mvc.html">Here</a> I’ve described how you can easily integrate it with MVC.  </p>
<p align="left">In order to create ISessionFactory we need to configure it and provide mappings for our domain model. Lets start with configuration (you can read full list of available options <a href="http://fabiomaulo.blogspot.com/2009/07/nhibernate-fluent-configuration.html" target="_blank">here</a>). Add new class to your .Data project, lets call it NHibernateConfigurator and create a single method there -BuildSessionFactory. Code there should be the following:</p>
<pre class="brush: csharp;">public class NHibernateConfigurator
{
public ISessionFactory BuildSessionFactory()
{
var cfg = new Configuration();
cfg.SessionFactory()
.Proxy.Through<ProxyFactoryFactory>()
.Integrate.Using<MsSql2008Dialect>()
.Connected.ByAppConfing("connectionString");
return cfg.BuildSessionFactory();
}
}</pre>
<p>So we say that we are going to work with SQL Server 2008 and connection string is placed in connection strings section of application settings file (web.config in our case). </p>
<p>This post is written for NHibernate 3.1 version. And this version has no default proxy factory with it. So you need to execute one more NuGet command for Data project: <em>Install-Package NHibernate.LinFu</em>. <a href="http://216.121.112.228/browse/NH-2586" target="_blank">3.2 version will be delivered with default proxy factory</a>. </p>
<p>Now to the mappings. Its so easy, that you even wouldn’t believe: </p>
<pre class="brush: csharp;">public class DomainMapper
{
public HbmMapping GenerateMappings()
{
IEnumerable<Type> domainEntities = GetDomainEntities();
ObjectRelationalMapper relationalMapper = new ObjectRelationalMapper();
relationalMapper.TablePerConcreteClass(domainEntities); // each concrete class should have its own table in DB
relationalMapper.Patterns.PoidStrategies.Add(new NativePoidPattern()); // primary keys are generated by DB with identity field
relationalMapper.Patterns.Sets.Add(new UseSetWhenGenericCollectionPattern()); // ICollection when met in classes should use Set in mappings
relationalMapper.ManyToMany<Post, Tag>(); // Many to many association by some reasons cant be picked by ConfOrm. Need in set it manually
relationalMapper.Cascade<Post, Tag>(Cascade.Persist); // when post is saved, tag also needs to be saved
var patternsAppliers = new CoolPatternsAppliersHolder(relationalMapper); // this is set of column naming packs it used to get nice column names in FKs like PostId in Comments table
patternsAppliers.Merge(new ClassPluralizedTableApplier(new EnglishInflector())); // means that Comment entity will have Comments table in DB
Mapper mapper = new Mapper(relationalMapper, patternsAppliers);
HbmMapping mapping = mapper.CompileMappingFor(domainEntities);
File.WriteAllText(@"D:\mapping.xml", Serialize(mapping));
return mapping;
}
private static IEnumerable<Type> GetDomainEntities()
{
Assembly domainAssembly = typeof(Entity).Assembly;
IEnumerable<Type> domainEntities = from t in domainAssembly.GetTypes()
where t.BaseType == typeof(Entity) && !t.IsGenericType
select t;
return domainEntities;
}
/// <summary>
/// Generates XML string from <see cref="NHibernate"/> mappings. Used just to verify what was generated by ConfOrm to make sure everything is correct.
/// </summary>
protected static string Serialize(HbmMapping hbmElement)
{
var setting = new XmlWriterSettings { Indent = true };
var serializer = new XmlSerializer(typeof(HbmMapping));
using (var memStream = new MemoryStream())
{
using (var xmlWriter = XmlWriter.Create(memStream, setting))
{
serializer.Serialize(xmlWriter, hbmElement);
memStream.Flush();
byte[] streamContents = memStream.ToArray();
string result = Encoding.UTF8.GetString(streamContents);
return result;
}
}
}
}</pre>
<p>This code block contains one utility method called Serialize. It is used just to verify mappings. You can skip it. But its useful. That’s all. Some details I’ve added in comments. So now we are ready to start working with NHibernate.</p>
<p>Now we need provide generated mappings for our ISessionFactory. So add next two lines to the BuildSessionFactory method of NHibernateConfiguratorClass:</p>
<pre class="brush: csharp;">HbmMapping generateMappings = new DomainMapper().GenerateMappings();
cfg.AddDeserializedMapping(generateMappings, "domain");</pre>
<p>Now we are ready to use NHibernate. First thing we need is actually DB schema. We can generate it from mappings with the help of SchemaExport class:</p>
<pre class="brush: csharp;">new SchemaExport(cfg).Execute(true, true, false);</pre>
<p>This command is executed in NHibernateConfigurator class and creates all required tables for us:</p>
<p><img style="display: block; float: none; margin-left: auto; margin-right: auto" title="Data base schema" alt="Data base schema" src="http://lh4.ggpht.com/-Lgr_NJdSrtQ/TfCNCUDqQDI/AAAAAAAAAeM/2LBEeUNtqe4/2011-06-09_1122%25255B6%25255D.png?imgmax=800" width="501" height="426" /></p>
<p>In order to work properly NHibernate requires transactions to wrap all queries. I’ve described asp.net mvc integration in <a href="http://slynetblog.blogspot.com/2011/04/lightweight-nhibernate-and-aspnet-mvc.html">previous post</a>. So I won’t describe it here.</p>
<p>The last thing you need to see here is a way to work with data. Here is how we can save new blog with post and some comments:</p>
<pre class="brush: csharp;">ISessionFactory sessionFactory = new NHibernateConfigurator().BuildSessionFactory();
using (ISession session = sessionFactory.OpenSession())
{
using (ITransaction transaction = session.BeginTransaction())
{
Post post = new Post
{
Content = "test"
};
post.Tags.Add(new Tag
{
Name = "test"
});
post.Comments.Add(new Comment
{
Author = "me",
Content = "NH is awesome",
Post = post
});
session.SaveOrUpdate(post);
transaction.Commit();
}
}</pre>
<p>Notice that we save only Post object, all associations are going to be saved by cascade. Generated SQL is straight forward:</p>
<pre class="brush: sql;">begin transaction with isolation level: Unspecified
INSERT INTO Posts
(Title, Content)
VALUES (NULL /* @p0 */,
'test' /* @p1 */)
select SCOPE_IDENTITY()
INSERT INTO Comments
(Author, Content, PostId)
VALUES ('me' /* @p0 */,
'NH is awesome' /* @p1 */,
1 /* @p2 */)
select SCOPE_IDENTITY()
INSERT INTO Tags
(Name)
VALUES ('test' /* @p0 */)
select SCOPE_IDENTITY()
INSERT INTO PostToTag
(PostId,
TagId)
VALUES (1 /* @p0 */,
1 /* @p1 */)
commit transaction</pre>
<p>NHibernate has <a href="http://ayende.com/blog/1900/complex-searching-querying-with-nhibernate" target="_blank">mature Querying API</a> that was <a href="http://nhforge.org/blogs/nhibernate/archive/2009/12/17/queryover-in-nh-3-0.aspx" target="_blank">improved in 3.0</a> version. Also it has LINQ implemented with session.Query<T> extension method. This post has no intention do describe them. </p>
<p>So you are almost NHibernate Guru already ;). What to read next:</p>
<ul>
<li>Overriding GetHashCode and Equals methods for your domain entities (<a href="http://devlicio.us/blogs/billy_mccafferty/archive/2007/04/25/using-equals-gethashcode-effectively.aspx" target="_blank">link</a>) </li>
<li>Primary keys generations and consequences of using them (<a href="http://nhforge.org/blogs/nhibernate/archive/2009/03/20/nhibernate-poid-generators-revealed.aspx" target="_blank">link</a>) </li>
<li>Lazy, eager loading (<a href="http://nhforge.org/wikis/howtonh/lazy-loading-eager-loading.aspx" target="_blank">link</a>) and select N+1 problem (<a href="http://ayende.com/blog/1328/combating-the-select-n-1-problem-in-nhibernate" target="_blank">link</a>) </li>
<li>Transactions and Sessions lifetime management (<a href="http://slynetblog.blogspot.com/2011/04/lightweight-nhibernate-and-aspnet-mvc.html" target="_blank">link</a>) </li>
<li>Inverse attribute for mapping collections (<a href="http://bchavez.bitarmory.com/archive/2007/10/06/nhibernate-and-inversetruefalse-attribute.aspx" target="_blank">link</a>) </li>
</ul>
<p>Happy NHibernating! </p>
<iframe style="padding-bottom: 0px; background-color: #fcfcfc; padding-left: 0px; width: 98px; padding-right: 0px; height: 115px; padding-top: 0px" title="Source code sample" marginheight="0" src="http://cid-44848200dcefa4cd.office.live.com/embedicon.aspx/.Public/nhrevamp.zip" frameborder="0" marginwidth="0" scrolling="no"></iframe> Anonymoushttp://www.blogger.com/profile/10759235722359109807noreply@blogger.com0tag:blogger.com,1999:blog-3131937327654576099.post-14633790443435289632011-04-12T10:04:00.001+03:002011-04-12T10:04:04.852+03:00Unit testing classes with a lot of dependencies<p>I’m sure every TD developer will scream that it’s not right to have classes with a lot of dependencies. I agree with you, but it happens. It happens for example in <a href="http://www.codeproject.com/KB/architecture/ModelViewPresenter.aspx" target="_blank">MVP</a> pattern for ASP.NET, or for <a href="http://www.asp.net/mvc/tutorials/asp-net-mvc-controller-overview-cs" target="_blank">controllers</a> in <a href="http://www.asp.net/mvc" target="_blank">MVC</a>. </p> <p>Why it happens? The main reason I think are unnecessary abstractions. Many applications have interface like <em>IRepository<T> : where T : IEntity</em>. Such interface seams to be very useful. It gives you strongly typed access to the entities, and gives you ability to put common methods there (like <em>Get<T>(int id)</em>). Everything looks good so far. We <a title="NHibernate and mvc integration" href="http://slynetblog.blogspot.com/2011/04/lightweight-nhibernate-and-aspnet-mvc.html">configure our application</a> to use IoC container and don’t bother with dependencies:</p> <pre class="brush: csharp;">public ProductsController : Controller
{
private readonly IRepository<Product> productsRepository;
public ProductsController(IRepository<Product> productsRepository)
{
this.productsRepository = productsRepository;
}
public ActionResult Index()
{
var products = productsRepository.GetAll();
return View(products);
}
}</pre>
<p>Everything looks good so far and we are able to write very simple unit test for it. Its easy to mock <em>GetAll()</em> method and verify that View.Model contains exact the same collection that returned from repository.</p>
<p>But then you need to create a <em>Create</em> method that requires getting list of all categories that could be assigned to product. And all of a sudden you have two dependencies:</p>
<pre class="brush: csharp;">public ProductsController : Controller
{
private readonly IRepository<Product> productsRepository;
private readonly IRepository<Category> categoriesRepository;
public ProductsController(IRepository<Product> productsRepository, IRepository<Category> categoriesRepository)
{
this.productsRepository = productsRepository;
this.categoriesRepository = categoriesRepository;
}
public ActionResult Index()
{
var products = productsRepository.GetAll();
return View(products);
}
public ActionResult Create()
{
var categories = categoriesRepository.GetAll();
ViewBag.Categories = categories;
return View();
}
}</pre>
<p>Its still looking good. But only if you have 2-3 tests that are using <em>ProductsController</em> constructor with 1 parameter. When you have 20 of those its becoming a nightmare to add new dependency in constructor. </p>
<p>I do understand that its not best example of why a lot of dependencies happens, but its real one. Its very easy to have up to 4-6 decencies in constructor. And to do with it in tests? Each test needs to set 2 or 3 of them and he doesn’t cares about other. My first approach was creating a helper methods as such:</p>
<pre class="brush: csharp;">public ProductsController GetController(IRepository<Product> productsRepository)
{
return new ProductsController(productsRepository, Mock.Of<IRepository<Category>>);
}</pre>
<p>Its perfectly works for classes with less then 3 dependencies. But even there the problem still exists. Consider mocking of current <em>HttpContext.Current.User.Identity</em>. It may happen that each controller action needs access to it, but most of tests doesn’t care what is going to be returned as current user. You will need to copy paste mocking code in each of such methods, or put some complex chain of methods calling each other to get default user for all test controllers.</p>
<p>What I prefer to do now is having a simple factory for controllers (presenters) in test class:</p>
<pre class="brush: csharp;">internal class ProductsControllerFactory()
{
public ProductsControllerFactory()
{
this.CategoriesRepsitory = Mock.Of<IRepository<Category>>();
this.ProductsRepository = Mock.Of<IRepository<Product>>();
// you can put all default stubs here
}
public ProductsController GetController()
{
return new ProductsController(this.CategoriesRepository,
this.ProductsRepository);
}
public IRepository<Category> CategoriesRepsitory { get; set; }
public IRepository<Product> ProductsRepository { get; set; }
}</pre>
<p>So you end up with having constructor call in one place. Adding a dependency won’t affect existing tests, it will affect only controller factory. Each test can easily set dependency it needs and ignore dependencies it doesn’t cares about. </p> Anonymoushttp://www.blogger.com/profile/10759235722359109807noreply@blogger.com0tag:blogger.com,1999:blog-3131937327654576099.post-56227536073848574712011-04-09T12:14:00.001+03:002011-04-09T13:32:33.217+03:00Lightweight NHibernate and ASP.NET MVC integration with Autofac<p>Many times when new project stars and we want to use NHibernate relatively a lot of work need to be done. Among them are:</p> <ul> <li>Mapping of entities (I prefer automapping) </li> <li>ISessionFactory singleton </li> <li>ISession lifetime management (Per web request) </li> <li>Transaction management </li> </ul> <p><a href="https://github.com/sharparchitecture" target="_blank">Sharp architecture</a> project has all of them done and ready to use. But as for me this project has become too big and hard to understand. I wanted to have full control on what is happening in my app and didn’t want to have such a lot of abstractions. For example <em>Repository</em> and <em>Entity</em> objects that have inheritance chain about to 5 or 6 objects. </p> <p>So I decided to <sub></sub>show how very simple integration can be made with a minimum amount of code. To get all mentioned libraries I will use <a href="http://nuget.codeplex.com/" target="_blank">NuGet</a>. We will need:</p> <ul> <li>NHibernate </li> <li>Fluent NHibernate </li> <li>Autofac </li> <li>Autofac.Mvc3 </li> </ul> <p>Lets start with mapping / session factory configuration:</p> <pre class="brush: csharp;">public Configuration Configure()
{
var configuration = new Configuration();
configuration.SessionFactory()
.Proxy.Through<ProxyFactoryFactory>()
.Integrate.Using<MsSql2005Dialect>()
.Connected.ByAppConfing("dbConnection");
FluentConfiguration fluentConfiguration = Fluently.Configure(configuration);
fluentConfiguration.Mappings(map => map.AutoMappings.Add(
new ModelGenerator().Generate()));
return fluentConfiguration.BuildConfiguration();
}
public ISessionFactory GetSessionFactory()
{
var configuration = Configure();
return configuration.BuildSessionFactory();
}</pre>
<p>Few things to note here. Combination of Loquacious and Fluent configuration is used because first one is supporting all NHibernate features, second one handles mappings integration. Also Model generator class is used:</p>
<pre class="brush: csharp;">private class ModelGenerator
{
public AutoPersistenceModel Generate()
{
AutoPersistenceModel automap = new AutoPersistenceModel();
automap.Conventions.AddFromAssemblyOf<ModelGenerator>();
automap.UseOverridesFromAssemblyOf<ModelGenerator>();
automap.AddEntityAssembly(Assembly.GetAssembly(typeof (Entity)))
.Where(objectType => objectType.IsSubclassOf(typeof(Entity)));
return automap;
}
}</pre>
<p>Here we setup location for the conventions, overriding's and entities. All classes that are inherited from <em>Entity </em>will be mapped. For conventions I’m using sharp architecture’s with small tweaks to have nice constraints names when generating schema from mappings:</p>
<pre class="brush: csharp;">public class ReferenceConvention : IReferenceConvention
{
public void Apply(FluentNHibernate.Conventions.Instances.IManyToOneInstance instance)
{
string fkName = string.Format("{0}_{1}_FK",
instance.Name, instance.EntityType.Name);
instance.ForeignKey(fkName);
instance.Column(instance.Property.Name + "Fk");
}
}
public class HasManyToManyConvention : IHasManyToManyConvention
{
public void Apply(IManyToManyCollectionInstance instance)
{
string fkName = string.Format("{0}_{1}_FK",
instance.Member.Name, instance.EntityType.Name);
instance.Key.ForeignKey(fkName);
instance.Cascade.SaveUpdate();
}
}</pre>
<p>Now to the web part. In global asax on Application_Start event we need to setup Autofac and change the default controllers factory. To do this:</p>
<pre class="brush: csharp;">var builder = new ContainerBuilder();
builder.RegisterControllers(Assembly.GetAssembly(typeof (AuthorizationController)));
builder.Register(x => new NHibernateConfigurator().GetSessionFactory())
.SingleInstance();
builder.Register(x => x.Resolve<ISessionFactory>().OpenSession())
.InstancePerHttpRequest();
builder.RegisterModule(new AutofacWebTypesModule());
var container = builder.Build();
DependencyResolver.SetResolver(new AutofacDependencyResolver(container));</pre>
<p>Code here is pretty clear. We setup <em>ISessionFactory</em> to be singleton, <em>ISession </em>instance is resolved by container and has PerHttpRequest lifestyle. Notice call of <em>builder.RegisterModule </em>that is going to add all the required http modules to support per web request lifestyle and change default <a href="http://dotnetslackers.com/articles/aspnet/Inside-the-ASP-NET-MVC-Controller-Factory.aspx" target="_blank">controller factory</a> to the one that uses Autofac. So now we are able to write code like this:</p>
<pre class="brush: csharp;">public class AuthorizationController : Controller
{
private ISession session;
public AuthorizationController(ISession session)
{
this.session = session;
}
public ActionResult Index()
{
var users = this.session.QueryOver<User>().List();
return View(users);
}
}</pre>
<p>So we have controller that depends on ISession, which depends on ISessionFactory, which depends on our Nhibernate configurator class. Isn’t it kind from Autofac to handle all this? <img style="border-bottom-style: none; border-right-style: none; border-top-style: none; border-left-style: none" class="wlEmoticon wlEmoticon-smile" alt="Smile" src="http://lh6.ggpht.com/_1fl5Nhoq6QA/TaAjf-sSpTI/AAAAAAAAAdI/viMJn6a5UXU/wlEmoticon-smile2.png?imgmax=800" /></p>
<p>One last but important thing we need to do. Each call to the data base should be wrapped to correct transaction. You can read <a href="http://nhprof.com/Learn/Alerts/DoNotUseImplicitTransactions" target="_blank">here</a> why. The easiest way to handle this is to create action filter:</p>
<pre class="brush: csharp;">public class TransactionAttribute : ActionFilterAttribute
{
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
DependencyResolver.Current.GetService<ISession>().BeginTransaction(IsolationLevel.ReadCommitted);
}
public override void OnActionExecuted(ActionExecutedContext filterContext)
{
ITransaction currentTransaction = DependencyResolver.Current.GetService<ISession>().Transaction;
if (currentTransaction.IsActive)
{
if (filterContext.Exception != null && filterContext.ExceptionHandled)
{
currentTransaction.Rollback();
}
}
}
public override void OnResultExecuted(ResultExecutedContext filterContext)
{
ITransaction currentTransaction = DependencyResolver.Current.GetService<ISession>().Transaction;
base.OnResultExecuted(filterContext);
try
{
if (currentTransaction.IsActive)
{
if (filterContext.Exception != null && !filterContext.ExceptionHandled)
{
currentTransaction.Rollback();
}
else
{
currentTransaction.Commit();
}
}
}
finally
{
currentTransaction.Dispose();
}
}
}</pre>
<p><a href="https://github.com/sharparchitecture/Sharp-Architecture/blob/master/src/SharpArch/SharpArch.Web/NHibernate/TransactionAttribute.cs" target="_blank">Original implementation</a> is taken from <a href="https://github.com/sharparchitecture" target="_blank">Sharp architecture</a> and small changes made due to Autofac. So now out Index method should be marked with transaction attribute:</p>
<pre class="brush: csharp;">[Transaction]
public ActionResult Index()
{
return View();
}</pre>
<p>That's probably all we need to have NHibernate. As always source code is attached:</p>
<p><iframe style="padding-bottom: 0px; background-color: #fcfcfc; padding-left: 0px; width: 98px; padding-right: 0px; height: 115px; padding-top: 0px" title="Preview" marginheight="0" src="http://cid-44848200dcefa4cd.office.live.com/embedicon.aspx/.Public/light.zip" frameborder="0" marginwidth="0" scrolling="no"></iframe></p> Anonymoushttp://www.blogger.com/profile/10759235722359109807noreply@blogger.com8tag:blogger.com,1999:blog-3131937327654576099.post-85524498580611252842011-01-06T18:03:00.001+02:002011-03-07T16:40:09.569+02:00Episerver CMS 6 search<p>Had to make some additional staff in <a href="http://www.episerver.com/" target="_blank">EPiServer</a> search functionality. I had to add ability to filter pages by <strong>category</strong>, <strong>page type</strong>, and <strong>keyword in content</strong>. Firstly I have tried to do it with <em>DataFactory.Instance.FindPagesWithCriteria</em> method. But the thing is that I couldn’t  find a way to filter data by search keyword.</p> <p>I knew that <em><a href="http://sdk.episerver.com/library/cms6/index.aspx" target="_blank">SearchDataSource</a> </em>does this somehow and wanted to reuse that logic. But when I looked at it in reflector, I found a method 100 lines long doing some really fancy staff to make select by keywords. So the best way to make search that I need is to do it through <em><a href="http://sdk.episerver.com/library/cms6/index.aspx" target="_blank">SearchDataSource</a></em>.</p> <p>Fortunately it provides public property called <em>Criteria</em>. It is a criteria that are going to be passed for underlying <em>DataFactory.FindPagesWithCriteria</em> call. </p> <p>So the search that I needed could be implemented in the this way. Aspx part:</p> <pre class="brush: xml;"><EPiServer:SearchDataSource ID="uiSearchDataSource" runat="server" EnableVisibleInMenu="false"
PageLink="<%# PageReference.StartPage %>">
<SelectParameters>
<asp:QueryStringParameter Name="SearchQuery" QueryStringField="search" DefaultValue="" />
</SelectParameters>
</EPiServer:SearchDataSource></pre>
<p>And in code behind:</p>
<pre class="brush: csharp;">var pageTypeCategory = new PropertyCriteriaControl(new PropertyCriteria
{
Condition = CompareCondition.Equal,
Name = "PageTypeID",
Type = PropertyDataType.PageType,
Value = PageType.Load("Article").ID.ToString(),
Required = true
});
var pageTypeCategory1 = new PropertyCriteriaControl(new PropertyCriteria
{
Condition = CompareCondition.Equal,
Name = "PageCategory",
Type = PropertyDataType.Category,
Value = Category.Find("category2").ID.ToString(),
Required = true
});
this.uiSearchDataSource.Criteria.Add(pageTypeCategory);
this.uiSearchDataSource.Criteria.Add(pageTypeCategory1);</pre>
<p>Note that <em>Name</em> property of each criteria should be as shown in code above, otherwise it won’t work. Hope it helps someone!</p> Anonymoushttp://www.blogger.com/profile/10759235722359109807noreply@blogger.com6tag:blogger.com,1999:blog-3131937327654576099.post-45429085160768467202010-11-29T22:48:00.002+02:002010-11-30T10:23:39.192+02:00TFS for the SVN user<p>Originally I wanted to name this post as <strong>Making your life a little bit easier with all TFS horror</strong> and then decided to change it. The reason for changing title is that my irritation on TFS was called only by VisualSVN usability. I spoke with people who are using TFS for long period and have never used any other tools and they say that it is really good and everything is just obvious. Obvious for them, but not for me. </p> <p>Currently I have no choice except using TFS as a source control system.  Previously I used Subversion with VisualSVN add-in to integrate with visual studio. </p> <p>The only way for me not to go crazy while doing really simple tasks with TFS (like commit, merging) was making some configurations. Maybe some tips will save someone's time. So lets start with them one by one.</p> <h4>Merging tool</h4> <p>First of all when you merge conflicts with build in merging tool you really won’t understand what has changed. Changes are shown only in rows. So if one symbol is change you will need to hunt it in entire row by yourself. Also it is hard for me to see where conflict was resolved successfully with no need for me to do something and where my assistance is required. </p> <p>Now I’m using WinMerge tool. Its also not the best, but really better then default one. To configure it you need to go to the <em>Tools->Options->Source Control->Visual Studio Team Foundation Server</em> and click <em>Configure User Tools</em> button. Click <em>Add…</em> button to add new command.</p> <p><img style="background-image: none; border-right-width: 0px; padding-left: 0px; padding-right: 0px; display: block; float: none; border-top-width: 0px; border-bottom-width: 0px; margin-left: auto; border-left-width: 0px; margin-right: auto; padding-top: 0px" title="User tool configuration window" border="0" alt="User tool configuration window" src="http://lh5.ggpht.com/_1fl5Nhoq6QA/TPQRkaB_i6I/AAAAAAAAAbE/3iQAnbPEXjg/2010-11-29_1950%5B2%5D.png?imgmax=800" width="442" height="334" /></p> <p>Select Compare operation, provide to WinMerge and fill arguments with this value: <em>/ub /dl %6 /dr %7 %1 %2</em>. </p> <p>Click ok and add another new command select Merge, provide WinMerge path and fill arguments: <em>/ub /dl %6 /dr %7 %1 %2 %4. </em>Click Ok.</p> <p><a href="http://blogs.msdn.com/b/jmanning/archive/2006/02/20/diff-merge-configuration-in-team-foundation-common-command-and-argument-values.aspx" target="_blank">Here is a post</a> where described how to configure TFS with other tools.</p> <h4>Compare on double click</h4> <p>Another really annoying thing is that when you are about to commit all your changes and you double click on a file it is just opened (<strong>in Notepad as I rememer O_O</strong>). Why would anyone need to see his pending change file in notepad? To compare it with server version you need to click on special buttons:</p> <p><img style="background-image: none; border-right-width: 0px; padding-left: 0px; padding-right: 0px; display: block; float: none; border-top-width: 0px; border-bottom-width: 0px; margin-left: auto; border-left-width: 0px; margin-right: auto; padding-top: 0px" title="Checkin pending changes dialog" border="0" alt="Checkin pending changes dialog" src="http://lh4.ggpht.com/_1fl5Nhoq6QA/TPQRl8rUO1I/AAAAAAAAAbI/vO_bSVo1C6w/2010-11-29_2009%5B6%5D.png?imgmax=800" width="578" height="357" /></p> <p>I didn’t want to pixel hunt that buttons. I wanted to compare with latest version on double mouse click. The good news that it is possible with TFS. How to configure it you can read <a href="http://www.richard-banks.org/2010/07/how-to-double-click-to-diff-pending.html" target="_blank">here</a> (btw the only option that worked for me is with registry and only after reboot).</p> <h4>Ghost changes</h4> <p>My favourite trick with TFS is just go to WebForm1.aspx press space somewhere and hit ctrl+z. You even didn’t hit save. Then you can open pending changes dialog and there real surprise is waiting for you. <strong>TFS will say that you have 3 pending changes <img style="border-bottom-style: none; border-right-style: none; border-top-style: none; border-left-style: none" class="wlEmoticon wlEmoticon-openmouthedsmile" alt="Open-mouthed smile" src="http://lh6.ggpht.com/_1fl5Nhoq6QA/TPQRm0KW50I/AAAAAAAAAbM/PqMKnIOmMmw/wlEmoticon-openmouthedsmile%5B2%5D.png?imgmax=800" />. </strong>Wow, when I saw this for the first time I couldn’t believe my eyes! </p> <p>Ofcourse  when you click <em>Check In</em> button it will check real changes in the file and won’t commit file with empty changes. But I used to look through all changes that I’m going to commit and don’t want to hunt for really changed files among rest of them.</p> <p>To solve this <a href="http://visualstudiogallery.msdn.microsoft.com/en-us/c255a1e4-04ba-4f68-8f4e-cd473d6b971f" target="_blank">TFS power tools</a> can be used. They contain a lot of useful features and one of them is TFPT.EXE tool. It is command line utility and can do a lot of stuff that you usually do with interface. And guess what? This tool has a special command to roll back unchanged files. </p> <p>I added this tool as an external tool. To do this you can do the following go to the <em>Tools->External Tools </em>and click <em>Add</em> button. There you need to fill all the fields like this:</p> <p><img style="background-image: none; border-right-width: 0px; padding-left: 0px; padding-right: 0px; display: block; float: none; border-top-width: 0px; border-bottom-width: 0px; margin-left: auto; border-left-width: 0px; margin-right: auto; padding-top: 0px" title="external tools configuration" border="0" alt="external tools configuration" src="http://lh6.ggpht.com/_1fl5Nhoq6QA/TPQRoLdNHeI/AAAAAAAAAbQ/OTrYd_pZt_k/externalTools%5B2%5D.png?imgmax=800" width="394" height="385" /></p> <p>I named the command as undo. You can name it the way you want. After you do it in <em>Tools</em> menu new option will be added: </p> <p><img style="background-image: none; border-right-width: 0px; padding-left: 0px; padding-right: 0px; display: block; float: none; border-top-width: 0px; border-bottom-width: 0px; margin-left: auto; border-left-width: 0px; margin-right: auto; padding-top: 0px" title="undo" border="0" alt="undo" src="http://lh6.ggpht.com/_1fl5Nhoq6QA/TPQRojHTASI/AAAAAAAAAbU/4Jl3WQyE6xY/undo%5B2%5D.png?imgmax=800" width="418" height="512" /></p> <p>When clicked following output will be shown for me:</p> <p><img style="background-image: none; border-right-width: 0px; padding-left: 0px; padding-right: 0px; display: block; float: none; border-top-width: 0px; border-bottom-width: 0px; margin-left: auto; border-left-width: 0px; margin-right: auto; padding-top: 0px" title="undo results" border="0" alt="undo results" src="http://lh4.ggpht.com/_1fl5Nhoq6QA/TPQRpBZ3JgI/AAAAAAAAAbY/8KXia4MG2mg/undoResults%5B10%5D.png?imgmax=800" width="551" height="217" /></p> <h4>Auto merge</h4> <p>Another reason why you would want to install <a href="http://visualstudiogallery.msdn.microsoft.com/en-us/c255a1e4-04ba-4f68-8f4e-cd473d6b971f" target="_blank">power tools</a> is auto merge all button.</p> <p><img style="background-image: none; border-right-width: 0px; padding-left: 0px; padding-right: 0px; display: block; float: none; border-top-width: 0px; border-bottom-width: 0px; margin-left: auto; border-left-width: 0px; margin-right: auto; padding-top: 0px" title="automerge all" border="0" alt="automerge all" src="http://lh3.ggpht.com/_1fl5Nhoq6QA/TPQRplhkshI/AAAAAAAAAbc/NY7P69OSDkA/automergeAll%5B5%5D.png?imgmax=800" width="431" height="272" /></p> <p>The thing is: when you update, even if TFS is able to merge changes by himself it will force you to hit auto merge, auto merge, auto merge on each conflicted file. With this button you can do it in one place for all files. <strong>If somebody knows how to force it auto merge when possible without me clicking something I would really appreciate your help</strong>.</p> <h4>Remembering credentials</h4> <p>It won’t be an issue for the most of the TFS users, but it was for me. </p> <p>Our TFS server is located in a different active directory domain. And each time you open a solution it asks you for the credentials and has no option to save them. I found this <a href="http://social.msdn.microsoft.com/Forums/en-US/tfsgeneral/thread/84b4749c-2838-4975-9b61-991cb90582b4" target="_blank">question</a> on the msdn. The solution that worked for me is:</p> <ol> <li>Open start menu and in the quick search find for “manage network passwords” (in Russian version of windows you need to look for “управление сетевыми паролями”) </li> <li>In the opened window click on <em>Add a Windows credential</em> </li> <li>Fill all the required fields and click ok (server name should be provided with no http or something else, just name) </li> </ol> <p>After doing this Visual Studio won’t ask password.</p> <h4>Unshelve with merge</h4> <p>I didn’t really used this feature of power tools yet. But as I was told default unshelving functionality will just replace all the files in your working copy. And if time has passed since you shelved all changes will be lost. Power tools allows you to merge changes and not just override.</p> <p>So I think that those are all the configurations that I have made for today. It I remember something else I’ll write a new post.</p>Anonymoushttp://www.blogger.com/profile/10759235722359109807noreply@blogger.com3tag:blogger.com,1999:blog-3131937327654576099.post-34345690397779725152010-11-11T21:44:00.001+02:002010-11-11T21:44:33.900+02:00Don't use constants<p>I don’t really remember when, but inside my current team I have told that I really hate to see classes that are like this:</p> <pre class="brush: csharp;">public class Contants
{
public class Settings
{
public const ConnectionStringName = "SomeProject.ConnectionString";
}
public class FieldNames
{
public const SomeField = "MyField";
}
}</pre>
<p>Almost all of the projects that I saw had this kind of class. Usage of it seams to be obvious – to store reusable strings to remove repetition. </p>
<p>But for me such a classes are lacking of the one thing – intention. Each constant exists inside our application for some reason and Constants class completely removes context that is connected to its value.</p>
<p>Today we had a some kind of “real world” scenario where I can show what to do with constants. I our case we had an email template where system will substitute some user input. So template contains %UserName%, %Reason% and so force string patterns that are going to be replaced with some values. The most obvious storage for these patterns is Constants class, but we don’t want it :)! So what I think we should do is to create a set of extension methods like this:</p>
<pre class="brush: csharp;">static class StringExtensions
{
public static string SubstituteUserName(this string template, string userName)
{
return template.Replace("%UserName%", userName);
}
}</pre>
<p>This approach will allow us easily to write unit tests, because method is separated from others and really simple, and it will allow to write code in the following way:</p>
<pre class="brush: csharp;">"Hello %UserName%, thanks for your message about %Reason%"
.SubstituteUserName(userName)
.SubstituteReason(reason);</pre>
<p>I think its really readable and looks nice.</p>
<p>The next thing why such an approach works is the way how developer works with the legacy code. In legacy system it is really often for developer to perform text based search to find places where similar tasks were already solved. For example when you need to get some value from the web.config file you can either use ConfigurationManager or WebConfigurationManager or maybe in your project some custom wrapper is implemented. For me the easiest way to find such information is to grab existing key of some value and try find all the references on it. In the good project you will meet only one place of usage :). In other cases it may be two places – Constants class and some domain specific wrapper (in this case why do need this constant separately of its usage?). But if you find that this constant is reused in 100 places and there is no common pattern you will just add the 101th place and forget about it. The reason why that happened is the <em>existence </em>of this Constans class. </p>
<p>So my approach for constants is that they can be only private in some classes that are using them. This allows new developers easy way to understand why those values are here and how to use them. </p> Anonymoushttp://www.blogger.com/profile/10759235722359109807noreply@blogger.com10tag:blogger.com,1999:blog-3131937327654576099.post-33386402744070583082010-11-02T21:47:00.001+02:002010-11-02T21:58:35.175+02:00ConfOrm nice column naming conventions<p>I have decided to write all further posts in English. The reason for that is simple – when I try to find something related to programming topics I always use English. I think most people do the same.</p> <p>The next step of learning <a href="http://code.google.com/p/codeconform/" target="_blank">ConfOrm</a> for me was creation of <a href="http://code.google.com/p/sharp-architecture/">sharp-architecture</a> like conventions for table and column mappings.  I even wrote some pattern appliers, but when ManyToMany turn has came I had to google a bit. And the result was that Conform.Shop already implemented everything that I wanted so this post will be about usage of this library and the next one about implementation of your own appliers. </p> <p>To create your own naming conventions we need to understand overall process. Basically mapping in <a href="http://code.google.com/p/codeconform/" target="_blank">ConfOrm</a>  consists of two major steps:</p> <ol> <li><strong>Getting the domain entities</strong>. This is done with the help of <em>ObjectRelationalMapper</em> class. This class is responsible for creating all the entities and associations between them. So is you want to change association type, or remove some property from the mappings this a place where you should do it. </li> <li><strong>Creating mappings</strong>. <em>Mapper </em>class<em> </em>is responsible here. If you want to provide tables, columns and other conventions this is the right place. </li> </ol> <p>Lets begin with domain model:</p> <p><img title="domain model" alt="domain model" src="http://lh5.ggpht.com/_1fl5Nhoq6QA/TMsAqNkF0cI/AAAAAAAAAP4/ZtgYTVOoVYQ/2010-10-29_1826%5B2%5D.png?imgmax=800" /> </p> <p>Simple but with some associations. </p> <p>Probably the easiest way to understand how NHibernate is configured is to look at XML (<a href="http://code.google.com/p/codeconform/" target="_blank">ConfOrm</a> doesn't generate XML, but you can get it with <a href="http://code.google.com/p/codeconform/source/browse/ConfOrm/ConfOrmExample/CreateXmlMappingsDemo.cs" target="_blank">this</a> approach). So lets check <em>User</em> entity map:</p> <pre class="brush: xml;"><class name="User">
<id name="Id" type="Int32">
<generator class="native" />
</id>
<property name="FirstName" />
<property name="LastName" />
<property name="VeryLongProperty" />
<set name="Orders" inverse="true" cascade="all,delete-orphan">
<key column="User" on-delete="cascade" />
<one-to-many class="Order" />
</set>
</class></pre>
<p>The first thing that I want to change is the table name. I want it to be plural. To do this all that is required to do the following:</p>
<pre class="brush: csharp;">var englishInflector = new EnglishInflector();
mapper.PatternsAppliers.Merge(
new ClassPluralizedTableApplier(englishInflector));</pre>
<p>There are also SpanishInflector and ItalianInflector if somebody wants them. After adding this one we get directive <em>table=Users</em> and so on for all other entities.</p>
<p>Next thing that I don’t like is names for column in the <em><key </em>property. To change it I need to do the following:</p>
<pre class="brush: csharp;">mapper.PatternsAppliers.Merge(
new OneToManyKeyColumnApplier(relationalMapper));</pre>
<p>Now the mapping for the set is:</p>
<pre class="brush: xml;"><set name="Orders" inverse="true" cascade="all,delete-orphan">
<key column="UserId" on-delete="cascade" />
<one-to-many class="Order" />
</set></pre>
<p>That is probably all that I wanted to change in <em>User</em> mapping. Now Lets look at the <em>Product</em> map:</p>
<pre class="brush: xml;"><class name="Product" table="Products">
<id name="Id" type="Int32">
<generator class="native" />
</id>
<property name="Price" />
<property name="Name" />
<property name="Description" />
<set name="Categories" table="CategoryProduct" inverse="true">
<key column="product_key" />
<many-to-many class="Category" column="category_key" />
</set>
</class></pre>
<p>There is definitely a better way of naming columns in the joining table. To change them we need to add next appliers:</p>
<pre class="brush: csharp;">mapper.PatternsAppliers.Merge(
new ManyToManyColumnApplier(relationalMapper));
mapper.PatternsAppliers.Merge(
new ManyToManyKeyIdColumnApplier(relationalMapper));</pre>
<p>Those will give:</p>
<pre class="brush: xml;"><set name="Categories" table="CategoryProduct" inverse="true">
<key column="ProductId" />
<many-to-many class="Category" column="CategoryId" />
</set></pre>
<p>That is what I really wanted. So all the mappings for the domain:</p>
<pre class="brush: csharp;">var relationalMapper = new ObjectRelationalMapper();
relationalMapper.TablePerConcreteClass(domainEntities);
relationalMapper.Patterns.PoidStrategies.Add(new NativePoidPattern());
relationalMapper.Cascade<Category, Product>(Cascade.Persist);
relationalMapper.ManyToMany<Category, Product>();
relationalMapper.Cascade<Order, Product>(Cascade.Persist);
var mapper = new Mapper(relationalMapper);
var englishInflector = new EnglishInflector();
mapper.PatternsAppliers.Merge(new ClassPluralizedTableApplier(englishInflector));
mapper.PatternsAppliers.Merge(new OneToManyKeyColumnApplier(relationalMapper));
mapper.PatternsAppliers.Merge(new ManyToManyColumnApplier(relationalMapper));
mapper.PatternsAppliers.Merge(new ManyToManyKeyIdColumnApplier(relationalMapper));</pre>
<p>So not a lot of code and settings. Also this assembly contains a lot of other very nice patterns for example <em>ManyToManyPluralizedTableApplier</em> after applying it table name for joining products and categories becomes  <em>CategoriesToProducts</em>. Just beautiful <img style="border-bottom-style: none; border-right-style: none; border-top-style: none; border-left-style: none" class="wlEmoticon wlEmoticon-smile" alt="Smile" src="http://lh4.ggpht.com/_1fl5Nhoq6QA/TNBq0foSwiI/AAAAAAAAASQ/ga6TUc-EApU/wlEmoticon-smile%5B2%5D.png?imgmax=800" />. </p>
<p>Here is the source code for this post</p>
<p><iframe style="padding-bottom: 0px; background-color: #fcfcfc; padding-left: 0px; width: 98px; padding-right: 0px; height: 115px; padding-top: 0px" title="Preview" marginheight="0" src="http://cid-44848200dcefa4cd.office.live.com/embedicon.aspx/.Public/WhatsNew.zip" frameborder="0" marginwidth="0" scrolling="no"></iframe></p>
<p>So use <a href="http://code.google.com/p/codeconform/" target="_blank">ConfOrm</a>! </p> Anonymoushttp://www.blogger.com/profile/10759235722359109807noreply@blogger.com15tag:blogger.com,1999:blog-3131937327654576099.post-86787945039922294522010-10-29T20:13:00.001+03:002010-10-30T11:05:54.515+03:00Что нового в NHibernate 3<p>9 октября был выпущен <a href="http://sourceforge.net/projects/nhibernate/files/" target="_blank">NHibernate 3 beta 1</a>. Решил посмотреть что в нем нового. Для этого быстренько набросал модель:</p> <p><img style="background-image: none; border-right-width: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px; padding-top: 0px" title="2010-10-29_1826" border="0" alt="2010-10-29_1826" src="http://lh5.ggpht.com/_1fl5Nhoq6QA/TMsAqNkF0cI/AAAAAAAAAP4/ZtgYTVOoVYQ/2010-10-29_1826%5B2%5D.png?imgmax=800" width="497" height="480" /></p> <p>Для маппинга я буду использовать ConfOrm. Его настройки:</p> <pre class="brush: csharp;">public class DomainMapper
{
public HbmMapping GenerateMappigs()
{
IEnumerable<Type> domainEntities = this.GetDomainEntities();
var relationalMapper = new ObjectRelationalMapper();
relationalMapper.TablePerConcreteClass(domainEntities);
relationalMapper.Patterns.PoidStrategies.Add(new NativePoidPattern());
relationalMapper.Cascade<Category, Product>(Cascade.Persist);
relationalMapper.ManyToMany<Category, Product>();
relationalMapper.Cascade<Order, Product>(Cascade.Persist);
var mapper = new Mapper(relationalMapper);
mapper.PatternsAppliers.RootClass.Add(new TableNamesApplier());
mapper.Class<User>(x => x.Property(y => y.VeryLongProperty, map => map.Lazy(true)));
HbmMapping mapping = mapper.CompileMappingFor(domainEntities);
return mapping;
}
/// <summary>
/// Gets all objects that are inherited from <see cref="BaseEntity"/>.
/// </summary>
private IEnumerable<Type> GetDomainEntities()
{
Assembly domainAssembly = typeof(BaseEntity).Assembly;
IEnumerable<Type> domainEntities = from t in domainAssembly.GetTypes()
where t.BaseType == typeof(BaseEntity) && !t.IsGenericType
select t;
return domainEntities;
}
}</pre>
<p>В предыдущих постах говорили что стоит добавлять и схему базы данных. Скажу честно, я её не создавал, а попросил это сделать NHibernate. Поэтому я не буду её тут приводить <img style="border-bottom-style: none; border-right-style: none; border-top-style: none; border-left-style: none" class="wlEmoticon wlEmoticon-smile" alt="Smile" src="http://lh4.ggpht.com/_1fl5Nhoq6QA/TMsAq8RV9uI/AAAAAAAAAP8/fL_4C056xM0/wlEmoticon-smile%5B2%5D.png?imgmax=800" /></p>
<p>Чтобы сгенерировать базу данных можно использовать SchemaExport:</p>
<pre class="brush: csharp;">new SchemaExport(cfg).Execute(true, true, false);</pre>
<p>Дальше по порядку:</p>
<h4>1. И конечно самое ожидаемое – Linq Provider</h4>
<p>Чтобы попробовать его я решил попробовать запросы, которе прошлый провайдер (основанный на ICriteria) не мог выполнить.</p>
<ol>
<li>
<pre class="brush: csharp;">(from u in session.Query<User>()
where u.Orders.Count > 5
select u).Count()</pre>
Выполняет следующий sql:
<pre class="brush: csharp;">select
cast(count(*) as INT) as col_0_0_
from
Users user0_
where
(
select
cast(count(*) as INT)
from
Orders orders1_
where
user0_.Id=orders1_.OrderUser
)>@p0;
@p0 = 5 </pre>
</li>
<li>Интересно было так же посмотреть как выбираются анонимные объекты, запрос:
<pre class="brush: csharp;">var firstName = (from u in session.Query<User>()
select new { u.FirstName }).FirstOrDefault();</pre>
Выполняет
<pre class="brush: sql;">select
TOP (@p0) user0_.FirstName as col_0_0_
from
Users user0_;
@p0 = 1 [Type: Int32 (0)]</pre>
Я думаю комментарии тут не нужны, тем более если еще вспомнить с каким количеством баз данных умеет работать NHibernate и насколько это круто <img style="border-bottom-style: none; border-right-style: none; border-top-style: none; border-left-style: none" class="wlEmoticon wlEmoticon-smile" alt="Smile" src="http://lh4.ggpht.com/_1fl5Nhoq6QA/TMsAq8RV9uI/AAAAAAAAAP8/fL_4C056xM0/wlEmoticon-smile%5B2%5D.png?imgmax=800" /> </li>
<li>Но вот такой запрос уже выполнить не получилось:
<pre class="brush: csharp;"> var rows = (from u in session.Query<User>()
let order = u.Orders.FirstOrDefault()
where u.Orders.Count > 5 && order != null && order.Id == 4
select u).ToList();</pre>
</li>
</ol>
<h4>2. Fluent синтаксис для конфигурации SessionFactory</h4>
<p>Для этого примера мне потребовалась следующая кофигурация:</p>
<pre class="brush: csharp;">DomainMapper mapper = new DomainMapper();
HbmMapping generatedMappigs = mapper.GenerateMappigs();
var cfg = new Configuration();
cfg.SessionFactory()
.Proxy.Through<ProxyFactoryFactory>()
.Integrate
.Using<MsSql2008Dialect>()
.AutoQuoteKeywords()
.Connected
.By<SqlClientDriver>()
.ByAppConfing("connectionString")
.CreateCommands
.ConvertingExceptionsThrough<SQLStateConverter>();
cfg.SetProperty("show_sql", "true"); // I haven't found how to configure them
cfg.SetProperty("format_sql", "true");
cfg.AddDeserializedMapping(generatedMappigs, "WhatsNew");</pre>
<p>чтобы использовать такой синтаксис необходимо подключить неймспейс NHibernate.Cfg.Loquacious. Конечно название они выбрали... Но дословно гугл переводит как "словоохотливый". Может оно и правильно, но я раньше нигде не встречал. Наиболее полный пример всех настроек я смог <a href="http://fabiomaulo.blogspot.com/2009/07/nhibernate-fluent-configuration.html" target="_blank">найти у Fabio Maulo</a>. </p>
<p>Так же конфигурировать можно с помощью лямбда выражений. Но в примере я их не использовал. Подробнее можно почитать <a href="http://fabiomaulo.blogspot.com/2009/07/nhibernate-configuration-through.html" target="_blank">тут</a>.</p>
<h4>3. QueryOver синтаксис</h4>
<p>Это API позволяет упросить написание ICriteria запросов. Во первых больше нет необходимости использовать строки (раньше это решалось с помощью библиотеки <a href="http://code.google.com/p/nhlambdaextensions/" target="_blank">NHLambdaExtensions</a>), во вторых теперь возможны сложные условия внутри одного вызова через &&, ||, в третьих упрощена работа с алиасами (можете <a href="http://slynetblog.blogspot.com/2010/04/complex-criteria-query-sample.html" target="_blank">посмотреть</a> насколько это было не просто в предыдущей версии), в четвертых работа с проекциями теперь гораздо более читабельна, и в пятых мне наверно стоило сделать из этого абзаца n-тых список c номерами <img style="border-bottom-style: none; border-right-style: none; border-top-style: none; border-left-style: none" class="wlEmoticon wlEmoticon-smile" alt="Smile" src="http://lh4.ggpht.com/_1fl5Nhoq6QA/TMsAq8RV9uI/AAAAAAAAAP8/fL_4C056xM0/wlEmoticon-smile%5B2%5D.png?imgmax=800" />.</p>
<p>Приведу простой примерчик как теперь можно делать запросы:</p>
<pre class="brush: csharp;">var query = session.QueryOver<User>()
.Select(x => x.FirstName, x => x.LastName)
.WhereRestrictionOn(x => x.FirstName).IsLike("first", MatchMode.Anywhere)
.WhereRestrictionOn(x => x.LastName).IsLike("last")
.List<object[]>()
.Select(properties => new
{
FirstName = properties[0],
LastName = properties[1]
});</pre>
<p>Это выполнит запрос:</p>
<pre class="brush: sql;">SELECT
this_.FirstName as y0_,
this_.LastName as y1_
FROM
Users this_
WHERE
this_.FirstName like @p0
and this_.LastName like @p1;
@p0 = '%first%' [Type: String (4000)],
@p1 = 'last' [Type: String (4000)]</pre>
<p>Ну и обращаться к результатам можно как и к обычным анонимным объектам:</p>
<pre class="brush: csharp;">foreach (var user in query)
{
Console.WriteLine(user.FirstName);
}</pre>
<p>Хорошая документация о том как использовать этот синтаксис можно найти <a href="http://nhforge.org/blogs/nhibernate/archive/2009/12/17/queryover-in-nh-3-0.aspx" target="_blank">тут</a>.</p>
<h4>4. Lazy Property</h4>
<p>Не знаю почему эта фича была реализована только сейчас, но теперь она работает, так если у сущности есть какое то поле, которое не хотелось бы загружать каждый раз, можно пометить его как Lazy. Но с этой штукой надо быть осторожно, потому как если из базы данных достается список таких объектов, то вероятнее всего весь этот список будет перебираться (иначе зачем его было доставать) это может стать причиной Select N+1.  Ну демонстрация очень простая <img style="border-bottom-style: none; border-right-style: none; border-top-style: none; border-left-style: none" class="wlEmoticon wlEmoticon-smile" alt="Smile" src="http://lh4.ggpht.com/_1fl5Nhoq6QA/TMsAq8RV9uI/AAAAAAAAAP8/fL_4C056xM0/wlEmoticon-smile%5B2%5D.png?imgmax=800" /> :</p>
<pre class="brush: csharp;">var user = session.Get<User>(1);
string veryLongProperty = user.VeryLongProperty;</pre>
<p>И запросы:</p>
<pre class="brush: sql;">/* 1 */
SELECT
user0_.Id as Id4_0_,
user0_.FirstName as FirstName4_0_,
user0_.LastName as LastName4_0_
FROM
Users user0_
WHERE
user0_.Id=@p0;
@p0 = 1 [Type: Int32 (0)]
/* 2 */
SELECT
user_.VeryLongProperty as VeryLong4_4_
FROM
Users user_
WHERE
user_.Id=@p0;
@p0 = 1 [Type: Int32 (0)]</pre>
<p>Примечательно то, что благодаря этой фиче смогли реализовать Lazy Load one-to-one связи которой раньше не было.  </p>
<h4>5. Нет зависимости от log4net</h4>
<p>Последняя версия log4net вышла в 2003 году… Видимо поэтому многие сейчас отказываются от него. В общем теперь вам нет необходимости таскать log4net за собой, его даже нет в архиве со всеми файлами NHibernate. Вот <a href="http://nhforge.org/wikis/howtonh/using-nlog-via-common-logging-with-nhibernate.aspx" target="_blank">пример</a> как настраивать для работы с другими логгерами.</p>
<h4></h4>
<h4>6. Остальное</h4>
<p>В releasenotes еще длиннющий список повакшенных багов и некоторых импрувментов. Но для меня наверно они не так понятны потому как никогда на них не натыкался. Ну либо вот такие фичи:</p>
<ul>
<li>[NH-2309] - Add support for Future() with the new Linq provider </li>
<li>[NH-626] - Adding XmlDoc to NH types </li>
<li>[NH-2135] - Compatible with Mono </li>
<li>[NH-2256] - Add support for user-provided extensions to the Linq provider </li>
</ul>
<p>В общем прогресс проекта виден. Я боялся что EF задавит хибер за счет майкросовской рекламы, но видимо нет, слишком уж он хорош <img style="border-bottom-style: none; border-right-style: none; border-top-style: none; border-left-style: none" class="wlEmoticon wlEmoticon-smile" alt="Smile" src="http://lh4.ggpht.com/_1fl5Nhoq6QA/TMsAq8RV9uI/AAAAAAAAAP8/fL_4C056xM0/wlEmoticon-smile%5B2%5D.png?imgmax=800" />. </p> Anonymoushttp://www.blogger.com/profile/10759235722359109807noreply@blogger.com2tag:blogger.com,1999:blog-3131937327654576099.post-28177192633298895052010-09-29T23:55:00.003+03:002010-10-14T16:53:59.057+03:00Почему не asp.net и про собеседования<p>После довольно продолжительной работы с asp.net mvc пришлось перечитать книгу по asp.net. Казалось бы многие утверждают, что начинать проще с веб форм, а только потом уже переходить на mvc. Но давайте посмотрим на 2 картинки из книги для подготовки к экзамену по asp.net:</p> <p><img style="border-right-width: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px" title="simple request" border="0" alt="simple request" src="http://lh5.ggpht.com/_1fl5Nhoq6QA/TKOnygsPZNI/AAAAAAAAALI/5KKT53sUVJk/2010-09-29_2252%5B2%5D.png?imgmax=800" width="538" height="348" /> </p> <p>Все просто и понятно. Браузер запросил страничку, сервер её построил и вернул. Теперь посмотрим на другую:</p> <p> <img style="border-right-width: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px" title="жизненный цикл страницы" border="0" alt="жизненный цикл страницы" src="http://lh3.ggpht.com/_1fl5Nhoq6QA/TKOnzALrkhI/AAAAAAAAALM/rOZam6VIVB8/2010-09-29_2255%5B7%5D.png?imgmax=800" width="655" height="589" /> </p> <p></p> <p></p> <blockquote> <p>Несколько длинный путь по сравнению с первой картинкой… </p> <p>Итак мы имеем гору этапов на которых разработчик может вмешаться в построение страницы. Чем больше таких мест, тем сложнее в поддержке приложение. По моему личному опыту понять как же в какую ни будь лейбочку попадает текст далеко не всегда так просто. </p> <p>Мало того, если еще гора правил по использованию этих событий (которые почему то все невероятно любят спрашивать на разного рода тестах и собеседованиях) типа: тему и мастер пейдж можно задать только на этапе PageInit, но на PageInit еще не доступен ViewState, последнее событие, на котором можно изменить ViewState это Prerender – на последующих событиях эти изменения игнорируются и т.д. </p> <p><img style="border-right-width: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; margin-left: 0px; border-left-width: 0px; margin-right: 0px" title="confused-man" border="0" alt="confused-man" align="right" src="http://lh5.ggpht.com/_1fl5Nhoq6QA/TKOnziILnxI/AAAAAAAAALQ/a0lcMou2ZlI/confused-man%5B17%5D.jpg?imgmax=800" width="161" height="100" /> Событийная модель asp.net невероятно сложна в понимании. Особенно весело становится, если рассмотреть порядок вызова событий для страницы, которая содержит MasterPage + ContentPage + UserControl. Это список из <a href="http://msdn.microsoft.com/en-us/library/dct97kc3.aspx" target="_blank">17 ОСНОВНЫХ событий</a>,  без учета всех PreInit, PreRender и прочих. <br />И это все чтобы создать одну html страницу… 17 событий, хотя случилось всего одно событие – на сервер пришел запрос. </p> <p>Так же в ASP.NET есть поддержка тем (кстати ни одного приложения с использованием этой фичи не встречал), но опять же, есть ряд правил их применения:</p> <p><img style="border-right-width: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px" title="порядок применения тем" border="0" alt="порядок применения тем" src="http://lh4.ggpht.com/_1fl5Nhoq6QA/TKOn0MLIqjI/AAAAAAAAALU/VO6xNtj2D0s/2010-09-29_2307%5B2%5D.png?imgmax=800" width="676" height="192" /></p> <p>Подобные примеры можно приводить до бесконечности. Под конец беглого просмотра книги складывается впечатление что asp.net это огромный набор различных “gotcha” и “WTF!?”.</p> <p>Сертификационные экзамены сплошь набиты вопросами именно на эти темы. Порядок вызовов, приоритеты применения, файлы настроек, и так до бесконечности. </p> <p>Видимо из-за этого подобные же вопросы очень любят задавать на разного рода собеседованиях. Неужели именно эти знания делают человека хорошим разработчиком? Что проверят экзаменующие получив ответы?</p> <p>В mvc нет событийной модели, но мне совершенно это не мешало, а скорее наоборот сделало гораздо более понятней происходящее в веб. Стало понятно почему приложения должны следовать правилу <a href="http://en.wikipedia.org/wiki/Post/Redirect/Get" target="_blank">post-redirect-get</a>, и что означает окошко браузера типа:</p> <p><img style="border-bottom: 0px; border-left: 0px; display: block; float: none; margin-left: auto; border-top: 0px; margin-right: auto; border-right: 0px" title="2010-09-29_2340" border="0" alt="2010-09-29_2340" src="http://lh3.ggpht.com/_1fl5Nhoq6QA/TKOn0vslHEI/AAAAAAAAALY/v3f0Jvo4oHw/2010-09-29_2340%5B9%5D.png?imgmax=800" width="356" height="159" /> </p> <p>Раньше я даже не задумывался о том, что же браузер спрашивает. </p> <p>Т.е. у меня ушло около года чтобы изучить все эти совершенно не очевидные правила asp.net, но не понять основных и самых элементарных принципов работы веб приложений и http протокола в целом. </p> <p>Возможно это лично моя проблема, хотя мой (хоть и не богатый) опыт обучения новых сотрудников говорит об обратном.</p> <p>Не могу сказать что сейчас надо всем срочно бросать asp.net и бежать учить mvc. Просто хотелось бы посоветовать обратить внимание больше на саму концепцию веб приложений нежели на конкретную их реализацию.  </p></blockquote>Anonymoushttp://www.blogger.com/profile/10759235722359109807noreply@blogger.com8tag:blogger.com,1999:blog-3131937327654576099.post-10028456819589552372010-09-08T11:28:00.001+03:002010-09-10T19:15:31.244+03:00Про ConfORM<p><a href="http://code.google.com/p/codeconform" target="_blank">ConfORM</a> это еще один способ маппинга NHibernate сущностей используя код. Главное его отличие от <a href="http://fluentnhibernate.org/" target="_blank">FluentNHibernate</a> в том, что ConfORM вообще не генерирует XML, а работает с открытым  в NHibernate 3 API (более подробно об API можно почитать <a href="http://fabiomaulo.blogspot.com/2010/03/nhibernate-mappings-path.html" target="_blank">тут</a>). </p> <p>Ознакомится с этим фреймворком оказалось довольно не просто. Во первых его название практически не поддается поиску, т.е. вбивая confORM в гугле вы врядли найдете что-то связанное с ним. Во вторых у проекта нет wiki, нет документации, найдено было только следующее:</p> <ol> <li><a href="http://fabiomaulo.blogspot.com" target="_blank">блог Fabio Maulo</a> – менеджер проекта NHibernate, ConfOrm и еще нескольких OSS проектов. </li> <li>Блог <a href="http://testdrivendevelopment.wordpress.com/tag/conform/" target="_blank">testdrivendevelopment.wordpress.com</a>. </li> <li>Страница на <a href="http://code.google.com/p/codeconform/" target="_blank">google code</a>. </li> <li>И <a href="http://groups.google.com.ua/group/codeconform" target="_blank">гугло-группа</a> где обсуждается текущее положение вещей. </li> </ol> <p>Вот в общем то и все что мне удалось накопать за 2-3 часа поисков. Если кто-то найдет еще что-то милости прошу добавляйте в коменты.</p> <p>Теперь касательно его применения. Насколько я понял ConfORM поддерживает только автомаппинг основываясь на Ваших объектах. Для примера рассмотрим следующую модель:</p> <p><img style="border-right-width: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px" title="domain_model" border="0" alt="domain_model" src="http://lh5.ggpht.com/_1fl5Nhoq6QA/TIpZoWldb2I/AAAAAAAAAK4/Ouytr2SDsMA/domain_model22.png?imgmax=800" width="582" height="343" /> </p> <p>Все классы наследуют EntityBase который содержит единственное свойство Id типа int. </p> <p>Теперь попробуем замапить эту модель. Для этого ConfORM использует класс ObjectRelationalMapper. Использовать его можно следующим образом:</p> <pre class="brush: csharp;">public HbmMapping GenerateMappigs()
{
IEnumerable<Type> domainEntities = this.GetDomainEntities();
ObjectRelationalMapper relationalMapper = new ObjectRelationalMapper();
relationalMapper.TablePerConcreteClass(domainEntities); // каждый не абстрактный объкт будет замаплен на свою таблицу.
Mapper mapper = new Mapper(relationalMapper);
HbmMapping mapping = mapper.CompileMappingFor(domainEntities); // создание самих маппингов.
File.WriteAllText(@"d:\mappings.xml", Serialize(mapping)); // сохраняем маппинги в файл.
return mapping;
}
/// <summary>
/// Gets all objects that are inherited from EntityBase.
/// </summary>
private IEnumerable<Type> GetDomainEntities()
{
Assembly domainAssembly = typeof (EntityBase).Assembly;
IEnumerable<Type> domainEntities = from t in domainAssembly.GetTypes()
where t.BaseType == typeof(EntityBase) && !t.IsGenericType
select t;
return domainEntities;
}</pre>
<p>Наверно что-то замапилось :). Чтобы это проверить можно сохранить конфигурацию в XML виде. Для того используется метод Serialize, его реализация такова:</p>
<pre class="brush: csharp;">/// <summary>
/// Generates XML string from NHibernate mappings
/// </summary>
protected static string Serialize(HbmMapping hbmElement)
{
var setting = new XmlWriterSettings { Indent = true };
var serializer = new XmlSerializer(typeof(HbmMapping));
using (var memStream = new MemoryStream())
{
using (var xmlWriter = XmlWriter.Create(memStream, setting))
{
serializer.Serialize(xmlWriter, hbmElement);
memStream.Flush();
byte[] streamContents = memStream.ToArray();
string result = Encoding.UTF8.GetString(streamContents);
return result;
}
}
}</pre>
<p>Выполнив этот код, получим следующий xml: </p>
<pre class="brush: xml;"><?xml version="1.0" encoding="utf-8"?>
<hibernate-mapping xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" namespace="Conf.Entities" assembly="Conf" xmlns="urn:nhibernate-mapping-2.2">
<class name="User">
<id name="Id" type="Int32">
<generator class="hilo" />
</id>
<property name="Name" />
<property name="BirthDate" />
<bag name="Blogs" inverse="true" cascade="all,delete-orphan">
<key column="Owner" on-delete="cascade" />
<one-to-many class="Blog" />
</bag>
</class>
<class name="Blog">
<id name="Id" type="Int32">
<generator class="hilo" />
</id>
<property name="Name" />
<many-to-one name="Owner" />
</class>
<class name="Comment">
<id name="Id" type="Int32">
<generator class="hilo" />
</id>
<property name="Text" />
<many-to-one name="Author" />
<many-to-one name="Blog" />
</class>
</hibernate-mapping></pre>
<p>В общем то для существующей базы с такими маппингами уже можно работать. Как видите по умолчанию для первичных ключей используется hilo алгоритм, для коллекций используются Bag тэги, также для связи User-Blog выставляется правило каскадирования all, delete-orphan (толи это breaking change, но кажется раньше эта настройка выглядела как all-delete-orphan) и свойства называются так же как поля в базе.</p>
<p>В следующих постах хочу описать как кастомизировать автоматические маппинги и сделать полностью работающий пример.</p>
<p><a href="http://cid-44848200dcefa4cd.office.live.com/self.aspx/.Public/confSample.zip" target="_blank">Исходники</a></p> Anonymoushttp://www.blogger.com/profile/10759235722359109807noreply@blogger.com2tag:blogger.com,1999:blog-3131937327654576099.post-55149858141853806702010-04-20T12:27:00.002+03:002010-05-12T17:46:00.655+03:00WCF NHibernate управление сессией и транзакциями<p>В WCF службах, как и в любых других приложениях где есть БД и ORM становится вопрос об управлении жизненным циклом объектов и транзакциями. Для собственных служб мне хотелось реализовать схему как в веб приложениях, т.е. :</p> <ul> <li>ISession живет один запрос. </li> <li>Транзация открывается в начале запроса, и закрывается в конце. </li> <li>В случае наличия ошибки транзакция откатывается, в обратном подтверждается. </li> </ul> <p>Реализовать это можно при помощи <a href="http://msdn.microsoft.com/en-us/library/system.servicemodel.dispatcher.idispatchmessageinspector.aspx" target="_blank">IDispatchMessageInspector</a>. Данный интерфейс позволяет обработать события начала и окончания запроса. Итак реализация:</p> <pre class="brush: csharp;">public class TransactionManager : IDispatchMessageInspector
{
public object AfterReceiveRequest(ref System.ServiceModel.Channels.Message request, System.ServiceModel.IClientChannel channel, System.ServiceModel.InstanceContext instanceContext)
{
SessionStorage.OpenSession();
SessionStorage.CurrentSession.BeginTransaction(IsolationLevel.ReadCommitted);
return null;
}
public void BeforeSendReply(ref System.ServiceModel.Channels.Message reply, object correlationState)
{
var session = SessionStorage.CurrentSession;
if (session.Transaction.IsActive)
{
if (reply.IsFault)
{
session.Transaction.Rollback();
}
else
{
session.Transaction.Commit();
}
}
}
}</pre>
<p>Чтобы зарегистрировать TransactionManager нам потребуется еще два вспомогательных класса. 1й добавит TransactionManager к службе:</p>
<pre class="brush: csharp;">public class TransactionBehaviour : IEndpointBehavior
{
public void AddBindingParameters(ServiceEndpoint endpoint, BindingParameterCollection bindingParameters)
{
}
public void ApplyClientBehavior(ServiceEndpoint endpoint, ClientRuntime clientRuntime)
{
throw new Exception("Behavior not supported on the consumer side!");
}
public void ApplyDispatchBehavior(ServiceEndpoint endpoint, EndpointDispatcher endpointDispatcher)
{
TransactionManager inspector = new TransactionManager();
endpointDispatcher.DispatchRuntime.MessageInspectors.Add(inspector);
}
public void Validate(ServiceEndpoint endpoint)
{
}
}</pre>
<p>Второй будет использоваться для регистрации секции в конфигурационном файле:</p>
<pre class="brush: csharp;">public class TransactionBehaviorExtensionElement : BehaviorExtensionElement
{
protected override object CreateBehavior()
{
return new TransactionBehaviour();
}
public override Type BehaviorType
{
get
{
return typeof(TransactionBehaviour);
}
}
}</pre>
<p>И последний штрих – настройка самого сервиса где все это собирается воедино:</p>
<pre class="brush: xml;"><system.serviceModel>
<services>
<service name="WcfTransactions.TestService">
<endpoint behaviorConfiguration="transactionsEnabledBehaviour" address="" binding="basicHttpBinding" contract="WcfTransactions.ITestService"/>
<endpoint address="mex" binding="mexHttpBinding" contract="IMetadataExchange"/>
</service>
</services>
<behaviors>
<serviceBehaviors>
<behavior>
<serviceMetadata httpGetEnabled="true"/>
<serviceDebug includeExceptionDetailInFaults="true"/>
</behavior>
</serviceBehaviors>
<endpointBehaviors>
<behavior name="transactionsEnabledBehaviour">
<transactionBehaviour />
</behavior>
</endpointBehaviors>
</behaviors>
<extensions>
<behaviorExtensions>
<add
name="transactionBehaviour"
type="WcfTransactions.Transactions.TransactionBehaviorExtensionElement, WcfTransactions, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" />
</behaviorExtensions>
</extensions>
</system.serviceModel></pre>
<p>Тут есть маленькая особенность, когда мы регистрируем behaviorExtension <strong>обязательно указывать полный путь к типу, с указанием версии, культуры и токена сборки</strong>.</p>
<p>Как по мне это слишком сложно и слишком много всего приходится делать. В Asp.net mvc есть <a href="http://mvcactionfilter.codeplex.com/" target="_blank">ActionFilters</a> которые позволяют сделать все тоже самое, но с помощью всего одного атрибута на контроллере. Подобных вещей для WCF я не нашел, буду рад если кто-то подскажет. </p>
<p>Разобраться в этом помогут только <a href="http://public.blu.livefilestore.com/y1prVX157AgJcOWcnKDy4FI-sNLFdUIwuc8Yj0NIynLSOOui-AVi2nUUP-J6930tO2-LgmLQCtL7_6fjAaCkRYO7w/WCF.zip?download" target="_blank">исходники</a> :).</p>Anonymoushttp://www.blogger.com/profile/10759235722359109807noreply@blogger.com3