When using automapper, we tried to create one mapper class per entity, and it quickly became out of control.
One of the reasons is that we thought Mapper.Reset() is good enough to do the pre-clean-up, but the CreateMap from another Mapper class can also re-define the mapping relationship. Single Mapper works OK in unit-test mode, but when merge together in application, too many mapper classes became too many interferences.
We decide to go with aggregate pattern to solve this mess, mapping should be defined in the “root” mapper class per aggregate. Simple, clean and easy to manage.
A simple rule will be one mapper class per repository class.
Another very useful tip for automapper I learned today is: instead of catching AutoMapperMappingException and look Context, it’s much easier just call this before do any actual map operation
Mapper.AssertConfigurationIsValid();
It will give out more accurate error message in your mapping configuration, saved a lot of time. The message in context in exception is too general and useless.
How to Start
SilverLight sdk or toolkit or whatever bootstrap installation package doesn’t come with VS2008, all kinds of confusions when looking for “How to start”. The default IIS setting doesn’t support xap either.
Cache
Somewhere the magic cache (Server or Browser?) stop me from seeing my code change. I had to delete the clientBin folder, then clean my IE temp files, then to see my changes. Is there a easy way?
clientaccesspolicy.xml never works on my machine! Even silverlight app and WCF service are on the same IIS server. I had to go through the non-localhost host name to visit my localhost.
WCF FaultExcetption
SilverLight can’t consume regular / default WCF service, I had to add the basic http binding to WCF service just like what I did to expose WCF service to PowerBuidler, but, SilverLight doesn’t know WCF FaultException! I had to use fiddler to see the exception/stack info in the traffic to figure out what’s going on.
Add FaultContract into ServiceContract will make WCF serice unavialable to silverlight. There is a workaround to put faultexception into out parameter, but we can’t go this way because our service is shared, not only for silverlight. what I figured out is, it’s safe to add fautdetail as customized fault, do NOT inherited from Exception type.
The easiest way for me is just changing httpstatus code from 500 to 200, the silverlight client side still keeps same:
private void Application_Startup(object sender, StartupEventArgs e)
{
var client = new AglcSecureWebServiceClient();
client.ServcieCallCompleted += client_ServcieCallCompleted;
client.ServcieCallAsync(view);
}
private void client_ServcieCallCompleted(object sender, ServcieCallCompletedEventArgs e)
{
try
{
// good call , continue ...
}
catch (Exception err)
{
Exception innerException = err.InnerException;
if (innerException is FaultException<MyFault>)
{
Debug.WriteLine("FaultException occoured: " + innerException.Message);
// business code ...
}else if(innerException is CommunicationException)
{
MessageBox.Show("Communication exception occoured."+ innerException.Message);
// throw err; // depends app design, hide or throw.
}else
{
throw;
}
}
}
It seems Sandcastle is the only option for auto-doc dotnet app.
What needs to installed:
- Sandcastle help compiler
- Sandcastle help file builder (GUI)
- HTML Help 1 compiler ( I don’t know how to use HTML help 2 format yet, this regular chm file is good enough for us)
The Sandcastle help file builder supports command line very well, it can be very easily added to cc.net.
<target name="build.sandcastle.doc" >
<property name="msbuild.exe" value="c:\windows\Microsoft.NET\Framework\v3.5\msbuild.exe" />
<property name="sandcastle.proj" value="doc.shfbproj" />
<exec program="${msbuild.exe}" failonerror="true">
<arg line="${sandcastle.proj}" />
<arg line="/p:Configuration=release" />
</exec>
<copy file="${dir.base}/help/Documentation.chm" todir="${dir.release}" />
</target>
Reference: Recommended Tags for Documentation Comments
Note:
Namespace comments can be defined in a class, or in sandcastle project config file:
Finally, I got some time to try this must-have tool for NHibernate, downloaded it, asked for the trail key, why 33 days?
No changing to source code sounds a good idea, the assumption is using log4net appender, every NH project suppose use log4net, doesn’t it?
I’m not sure what NHibernate internal statistics is? Because we are using FluentNHibernate, there is no hibernate.cfg.xml file we can change, there is 2 solutions from stack overflow, but without seeing this xml file, how can I know it works? (BTW, how can I export hibernate.cfg.xml to somewhere like what we can do for hbm.xml?)
The result is, I can’t output statistics file for NHprof, so instance is the only way. Fortunately, it works. But, what if the port is being used? Or how to modify it? I couldn’t find it in NHProf.exe.config, it can’t be hardcoded?
tcp://127.0.0.1:22897
Input from Ayende: Changing the port that the profiler listen to is done through the UI, Options > Settings
OK, back to our app, 5 Unbounded result set warnings and one N+1 select alert. Not too bad, the interesting part is, this N+1 select alert only appears in WCF client, not in domain service or repository layer.
It turns out the problem happens in AutoMapper which is doing the property loop through the result set from repository. Some properties are set to lazy-load.
We should be really careful when using AutoMapper, I think we should either explicitly exclude those lazy-load properties, or create a query to eager fetch those needed properties.
Thanks to NHProf, developers can figure out what’s extatly beneath their feet, ice or solid ground.
Problem:
When trying to pass Exception as Fault from server down to client, only in debug mode, the exception can’t be serialized, error message is:
System.Runtime.Serialization.SerializationException: Type ‘System.Collections.ListDictionaryInternal’ with data contract name ‘ArrayOfKeyValueOfanyTypeanyType:http://schemas.microsoft.com/2003/10/Serialization/Arrays’ is not expected. Add any types not known statically to the list of known types – for example, by using the KnownTypeAttribute attribute or by adding them to the list of known types passed to DataContractSerializer.
Reason is describe in reference 1, the the solution/work around from reference 2 is better. As reference 2 explained, this is a side effect from debugger. If debugging is disable in WCF host, we don’t get this problem.
Here is the post from MSDN describing how to enable the auto-attach WCF host process in debug mode. Note: the default debug flag for new WCF service is off.
<system.web>
<!-- default was off, so auto-attach won't work. -->
<compilation debug="true" />
<system.web>
What I figured out is developer also has to make sure in the WCF project property window, the servers url is correct in the web tab.
References:
I was using the mapping skill found this post to implement FluentNHibernate join table, today trying to upgrade to latest RTM version of FN, failed. The new syntax has been changed to:
Join("Stakeholder_CV",
join =>
{
join.Map(prop => prop.StakeholderType, "Stakeholder_Type");
join.Map(Reveal.Property<Individual>("Expired"))
.Access.CamelCaseField(Prefix.Underscore)
.CustomType("string");
join.Map(prop => prop.LastApprovalDate, "Last_Approval_Date");
join.KeyColumn("Stakeholder_Id");
});
- Value object has no identity (or its id has no domain meaning), the equality is decided by their values, (not id), which makes it immutable, changing the value of a value object means (equal) to delete it and add a new one. (This rule apply to value object list as well) Here comes the huge benefit, we can add all validation code right in ctor. Once value object has been successfully created, it must be in valid status. (We have to throw exceptions in ctor?)
- If we found a “Value object” should be updatable, it’s an entity with a value object component. Watch out, value object can have action methods, like Money.Add(Money), this looks like making the modification on the old one, but it returns a new Money internally.
- According to the rule, value object can’t exist by itself, must live with its owner. But sometimes we need a maintaince app to admin value objets, like lookup, dictionary stuff (coutry list), I think I have to create avalue object repository, with no update() in it.
Note: UpdateAll() is doing delete all and add all, who cares the id stuff, we only care values.
Even value object overrode Equals() method, linq.Distinct() is not calling it! Here is the post giving out the workaround.public interface IMyValueObjectRepository<T> where T: MyValueObject<T> { IEnumerable<string> FetchAll(); IEnumerable<T> FetchAllForEditing(); void AddNew(T objToAdd); void Delete(T objToDelete); void UpdateAll(IEnumerable<T> list); T FetchByValue(string value); } [Serializable()] public abstract class MyValueObject<T> : IAuditable , IEquatable<T> where T : class { protected MyValueObject() { CreatedAt = MyDateTime.Default; } public virtual DateTime CreatedAt { get; set; } public virtual string CreatedBy{ get; set; } public new abstract int GetHashCode(); public abstract bool Equals(T other); public override bool Equals(object obj) { if (ReferenceEquals(null, obj)) return false; if (ReferenceEquals(this, obj)) return true; return Equals(obj as T); } // subclass can also implement static operator == and != inside their own class defination // see example http://davybrion.com/blog/2009/03/implementing-a-value-object-with-nhibernate/ } - Value object can be stored into a separated database table with an id column, we can create a protected internal property in it to hide from external users and enable Fluent NHibernate mapping.
- Most of time value object is embedded in an entity, probably stored in same database table as parent entity, e.g., address, amount. One-to-One.
- Value Object has different purpose than DTO, but I just found out it’s very convenient to combine those two in one class, so the same validation rule can be shared between server and client in WCF application.
- We can create a custom list type instead of IList to do the duplication and minim check in ctor.
A interesting question:
I have a lookup list, try to make it value object. Everything works OK until I try to add an “Active” flag on it.
Does this request change it to entity? At least this value object looks like not immutable anymore. Its active flag can be turn on and off, but for me it’s the same object doesn’t matter it’s on or off.
OK, think about turn flag on and off, I think this is same as Money.Add(), (shown in here P. 49 ) turn on/off a value object is no difference than delete the old off/on value object and insert the new one. Good, it’s still immutable.
I had this discussion with Igloo coder before, he suggested that repository.SaveOrUpdate(T) should always return void. I couldn’t agree with him with 2 major reasons:
- We need new generated Id from db
- We need updated time-stamp to do concurrency check for following updates
His answer was:
We shouldn’t use auto-gen Id for business usage, and fetching updated timestamp should be in another repository method like FecthById(). Update() should only do one thing, SRP.
Because we are using his NHibernateWCF framework to implement our WCF service, the NHibernate session/transaction is commit or rollback only when WCF service finished, this means to return T in repository.Update() really won’t get anything new because at that time nothing has been commit yet!
A seperated fetch to get new time-stamp has be used, OK, SRP.
But, if we can not switch from auto-gen id overnight, how to get the new generated Id?
Here are some possible solutions:
- Use a fetch by key fields right after new insert. This won’t be very reliable,unless we use guid for accurate search.
- Organize our own Unit-Of-Work, do commit in repository.Save() then we can get new gen id right way and return it. It works, but the pattern and code will be ugly.
- Try to follow “new->save->back to list->update” work flow, don’t use the same screen to add new and afterward update.
Anyway, this must be another example of anti “persistence ignorance” design.
Our CI server right now can deploy component to local/dev easerver, and export it as the jar file to be deploy to acceptance or production later. We don’t want use Nant to do production depoy, basic dos command should be used instead.
Result we try to achieve : One click deploy command to target server.
Tricks:
- Use “start /wait /min ” syntax to call jagtool to enable waiting for the deploy process.
- Add an exit command in jagtool.bat, to allow this separated window to close after it finished.
- Merge the log result into a single log file, jagtool by default always overwrite the existing file, not append.
- Loop through the jar folder.
Here is the dos command I end up with.
@echo off
@echo #########################################################
@echo You must logon to target server to run this command
@echo #########################################################
if NOT Exist deploy.log (
echo > deploy.log
)
for /f %%a IN ('dir /b *.jar') do (
echo "Deploying %%a" >deploy.log
start /WAIT /MIN jagtool2 -local -logfile tmp.log deploy -type jagjar -jagjartype Package %%a
copy deploy.log+tmp.log
)
del tmp.log
@echo on
pause
I read a post about MEF vs. IOC which cleared the confusion: MEF should be used external of app, while IOC should be used internally.
Good point. I have been struggling with how to separate my view and view model correctly in WPF app for a long time. In fact Bil Simer already suggested that MEF is perfect fit in MVVM/Presentation Model last year during the Edmonton Code Camp 2008.
Ideally, all view models can be compiled into a separated assembly and put into specific folder to be imported by UI views!
Some code changes during my code conversion include:
- Remove ctor args from view model, I haven’t figure out how to feed ctor args when MEF importing. This is not easy as auto-wiring in IOC. I ended up with a separate initialize method in each view model, which is not too bad, because I have to call wireUpViewModel from view anyway, this Initialize method has a perfect place to put.
- I’m still not sure where should I put Container.Compose() method, because this method needs the instance the object to be imported into, I think compose as needed is better for WPF app.
- Shared mode is the default behavior for export and import, it messed up my event handling code, (true, I didn’t do -= before +=). I had to explicitly add this everywhere.
- AllowRecomposition is a pain for me, couldn’t get it work at all. I have tried many different ways to create a second instance of the exported class. PartCreationPolicy? AllowRecomposition attribute? Maybe because I’m working on a beta version? Recomposition doesn’t work in contrustor paramteters, see Glen’s post.
- Too many attributes kinds of pollute my code. Adding those export/import attributes just like playing ping-pong. If you are not good at it, you become dizzy very quickly.
[Export] [PartCreationPolicy(CreationPolicy.NonShared)] [Import(typeof(NewRequestTypeViewModel), RequiredCreationPolicy = CreationPolicy.NonShared)] [ImportingConstructor] [Import(AllowRecomposition=true)] //this can apply directly on ctor parameters, but recompositorion won't work in ctor (for now)
- I still feel using StructureMap to hookup viewmodel inside view is better and more comfortable. Even this violates some principals, but in shell to open the view is much simpler, just new view(). ViewModel should sit behind the view, those UI developers/designers shouldn’t be ware of the exist of any view modles.
public RequestDetailsView(RequestDto requestDto)
{
InitializeComponent();
_viewModel = ObjectFactory.With("requestDto").EqualTo(requestDto).GetInstance<MyViewModel>();
DataContext = _viewModel;
}
