If the repository component can persist things in memory, we would not have a need for mocking IQueryable, an example:
https://github.com/MichaelBuen/ToTheEfnhX/blob/master/TestProject/TestTheIRepository.cs
Here's the repository component I made that works on memory, NHibernate, Entity Framework:
https://github.com/MichaelBuen/ToTheEfnhX
Other's approach on unit testing without using repository component + mocking is by using an in-memory database like SQLite, this precludes the need for repository component:
http://ayende.com/blog/3983/nhibernate-unit-testing
I ceased the development of my repository component project as it is hard to abstract the differences of fetching strategies between NHibernate and Entity Framework:
http://www.ienablemuch.com/2012/08/solving-nhibernate-thenfetchmany.html
I tried many ways to make the repository component conform to Entity Framework's intuitive fetching API, but it's hard to make NHibernate's fetching strategy similar to Entity Framework. Even I don't like Entity Framework, I'm not a technology bigot and I'm able to see some of Entity Framework's good points. So if I'm able to abstract the fetching strategy on the repository component, it would be patterned after Entity Framework's fetching strategies, i.e., not on NHibernate's buggy ThenFetchMany nor on NHibernate's ToFuture. Entity Framework's fetching strategy is easier to use and intuitive, It Just Works.
http://www.ienablemuch.com/2012/08/solving-nhibernate-thenfetchmany.html
http://msdn.microsoft.com/en-us/library/gg671236%28VS.103%29.aspx
Here's what can happen if the API can't empower the developer, it compels them to use N+1. A case of too much abstraction: http://ayende.com/blog/156577/on-umbracos-nhibernates-pullout
class Programmer : IEnableMuch {
"Simplicity can't be bought later, it must be earned from the start" -- DB
Friday, June 14, 2013
Thursday, June 13, 2013
NHibernate query caching
Query caching won't work using this Linq construct:
Must be done this way:
If you are a one-stop-shop kind of person, you might prefer the multiple Linq statements be kept together in just one Linq statement:
The above things won't be possible if a DAL/Repository component layered on top of an ORM component hides or neglect to expose useful functionality, or if there's a project policy of not allowing the developer to use NHibernate's API directly, e.g., caching, fetching
The developer should be able to access the caching API when there's a need.
"You can solve every problem with another level of indirection, except for the problem of too many levels of indirection" – D.Wheeler
Here's a case of too many indirections: http://ayende.com/blog/156577/on-umbracos-nhibernates-pullout
Happy Computing! ツ
var query =
(from p in session.Query<Product>()
join t in session.Query<ProductTranslation>() on p.ProductId equals l.ProductId
where p.YearIntroduced >= 1950 && string.Compare(t.ProductName, "B") >= 0
select new { p, t }).Cacheable();
var list = query.ToList();
Must be done this way:
var query = from p in session.Query<Product>()
join t in session.Query<ProductTranslation>() on p.ProductId equals l.ProductId
select new { p, t };
query = query.Where(q => q.p.YearIntroduced >= 1950 && string.Compare(q.t.ProductName, "B") >= 0).Cacheable();
var list = query.ToList();
If you are a one-stop-shop kind of person, you might prefer the multiple Linq statements be kept together in just one Linq statement:
var query =
(from q in
from p in session.Query<Product>()
join t in session.Query<ProductTranslation>() on p.ProductId equals l.ProductId
select new { p, t }
where q.p.YearIntroduced >= 1950 && string.Compare(q.t.ProductName, "B") >= 0
select q).Cacheable();
var list = query.ToList();
The above things won't be possible if a DAL/Repository component layered on top of an ORM component hides or neglect to expose useful functionality, or if there's a project policy of not allowing the developer to use NHibernate's API directly, e.g., caching, fetching
The developer should be able to access the caching API when there's a need.
"You can solve every problem with another level of indirection, except for the problem of too many levels of indirection" – D.Wheeler
Here's a case of too many indirections: http://ayende.com/blog/156577/on-umbracos-nhibernates-pullout
Happy Computing! ツ
Labels:
NHibernate
Wednesday, May 29, 2013
Limited .DefaultIfEmpty support is available on NHibernate now
As of the time of this writing, DefaultIfEmpty is not yet available on NHibernate from NuGet, so just get the latest NHibernate directly from GitHub instead, then compile it.
When we need to do this kind of SQL, we have to use Linq's DefaultIfEmpty.
DefaultIfEmpty is supported now on NHibernate, it works on collections now:
Whereas prior to DefaultIfEmpty support, it's hair-splitting to write the LEFT JOIN query in QueryOver API, it's a bit convoluted to use QueryOver, especially for those who are at ease with Linq and SQL. This is the adhoc-y approach, i.e. using anonymous type, good to use when you wanted to immediately return a JSON object:
This is the DTO approach. All hell break loose if you want to use a DTO on your QueryOver ツ
Contrast that on using DTO with Linq. The Linq version is much shorter, and without the noise of WithAlias and Transformers that gets in the way of your query's beautiful objective. Plus it's easy to mock and unit test in-memory objects with IQueryable:
If lambda is your thing, you can convert the Linq code to the equivalent lambda code, also working on latest NHibernate:
Now, about the limited part, we still cannot use DefaultIfEmpty on manual join operation, this results to runtime error that says the requested feature is not implemented.
And DefaultIfEmpty on NHibernate Linq's GroupBy has a bug on Count, NHibernate's Count cannot do conditional counting even if you passed a condition on it, it always blindly do COUNT(*), which always results to at least a count of 1, never having a count of zero. LEFT JOIN should be able to report aggregated count of zero.
To do a working COUNT with GROUP BY on NHibernate: http://www.ienablemuch.com/2012/12/nhibernates-non-stringly-typed-left-join.html
Happy Computing! ツ
When we need to do this kind of SQL, we have to use Linq's DefaultIfEmpty.
select
b.blog_id,
b.blog_title,
p.blog_text
from blog b
left join blog_post p on b.blog_id = p.blog_id
DefaultIfEmpty is supported now on NHibernate, it works on collections now:
var results =
from b in s.Query<Blog> ()
from p in b.Posts.DefaultIfEmpty ()
select new { b.BlogId, b.BlogTitle, BlogText = p.BlogText };
foreach (var item in results) {
Console.WriteLine ("{0} : {1} : {2}", item.BlogId, item.BlogTitle, item.BlogText);
}
Whereas prior to DefaultIfEmpty support, it's hair-splitting to write the LEFT JOIN query in QueryOver API, it's a bit convoluted to use QueryOver, especially for those who are at ease with Linq and SQL. This is the adhoc-y approach, i.e. using anonymous type, good to use when you wanted to immediately return a JSON object:
BlogPost bpAlias = null;
var results = s.QueryOver<Blog> ()
.Left.JoinAlias (b => b.Posts, () => bpAlias)
.SelectList (z => z.Select(b => b.BlogId).Select(b => b.BlogTitle).Select(b => bpAlias.BlogText))
.List<object[]> ()
.Select (z => new { BlogId = (int)z[0], BlogTitle = (string)z[1], BlogText = (string)z[2] });
foreach (var item in results) {
Console.WriteLine ("{0} : {1} : {2}", item.BlogId, item.BlogTitle, item.BlogText);
}
This is the DTO approach. All hell break loose if you want to use a DTO on your QueryOver ツ
public class BlogPlusPostDto
{
public int BlogId { get; set; }
public string BlogTitle { get; set; }
public string BlogText { get; set; }
}
.
.
.
BlogPost bpAlias = null;
BlogPlusPostDto bpDto = null;
var results = s.QueryOver<Blog> ()
.Left.JoinAlias (b => b.Posts, () => bpAlias)
.SelectList (z => z
.Select(b => b.BlogId).WithAlias(() => bpDto.BlogId)
.Select(b => b.BlogTitle).WithAlias(() => bpDto.BlogTitle)
.Select(b => bpAlias.BlogText).WithAlias(() => bpDto.BlogText)
)
.TransformUsing (Transformers.AliasToBean<BlogPlusPostDto>())
.List<BlogPlusPostDto> ();
foreach (var item in results) {
Console.WriteLine ("{0} : {1} : {2}", item.BlogId, item.BlogTitle, item.BlogText);
}
Contrast that on using DTO with Linq. The Linq version is much shorter, and without the noise of WithAlias and Transformers that gets in the way of your query's beautiful objective. Plus it's easy to mock and unit test in-memory objects with IQueryable:
public class BlogPlusPostDto
{
public int BlogId { get; set; }
public string BlogTitle { get; set; }
public string BlogText { get; set; }
}
.
.
.
var results =
(from b in s.Query<Blog> ()
from p in b.Posts.DefaultIfEmpty ()
select new { b.BlogId, b.BlogTitle, BlogText = p.BlogText }).ToList ()
.Select (x => new BlogPlusPostDto { BlogId = x.BlogId, BlogTitle = x.BlogTitle, BlogText = x.BlogText });
foreach (var item in results) {
Console.WriteLine ("{0} : {1} : {2}", item.BlogId, item.BlogTitle, item.BlogText);
}
If lambda is your thing, you can convert the Linq code to the equivalent lambda code, also working on latest NHibernate:
var xx = s.Query<Blog>()
.SelectMany (blog => blog.Posts.DefaultIfEmpty()
.Select(post => new { blog.BlogId, blog.BlogTitle, BlogText = post.BlogText })
);
foreach(var item in xx) {
Console.WriteLine ("{0} : {1} : {2}", item.BlogId, item.BlogTitle, item.BlogText);
}
Now, about the limited part, we still cannot use DefaultIfEmpty on manual join operation, this results to runtime error that says the requested feature is not implemented.
var results =
from b in s.Query<Blog>()
join p in s.Query<BlogPost>() on b equals p.Blog into bJoinP
from r in bJoinP.DefaultIfEmpty()
select new { b.BlogId, b.BlogTitle, r.BlogText };
foreach(var item in results) {
Console.WriteLine ("{0} : {1} : {2}", item.BlogId, item.BlogTitle, item.BlogText);
}
And DefaultIfEmpty on NHibernate Linq's GroupBy has a bug on Count, NHibernate's Count cannot do conditional counting even if you passed a condition on it, it always blindly do COUNT(*), which always results to at least a count of 1, never having a count of zero. LEFT JOIN should be able to report aggregated count of zero.
To do a working COUNT with GROUP BY on NHibernate: http://www.ienablemuch.com/2012/12/nhibernates-non-stringly-typed-left-join.html
Happy Computing! ツ
Labels:
NHibernate
Friday, May 24, 2013
Automating Integration Test with WatiN and Microsoft Test
A physicist is an atom's way of knowing about atoms. – George Wald
We also have that in software development field. The web developers are the way of the browsers to test itself.
Having said that, we should not be subjected to test the application correctness manually. We can also automate this kind of stuff
To cut to the chase, I used WatiN to test the application automatically. Automating integration test is also good for regression testing
It's very easy to use WatiN for test automation. Here's a sample test for doing integration test:
using System;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using WatiN.Core;
namespace TestTheWatiN.Tests
{
[TestClass]
public class TheUnitTest
{
public void Logon(Browser browser)
{
// Arrange
var uxUsername = browser.TextField(Find.ByName("LoginFields.Username"));
var uxPassword = browser.TextField(Find.ByName("LoginFields.Password"));
var uxLogin = browser.Link(Find.ById("loginButton"));
// Act
uxUsername.TypeTextQuickly("mb");
uxPassword.TypeTextQuickly("opensesame");
uxLogin.Click();
// Assert
Assert.IsTrue(browser.ContainsText("Welcome"));
}
[TestMethod]
[Timeout(10 * 60 * 1000)]
public void TestSocial()
{
string devSite = "http://blah/DEV/Account/Login";
// string qaSite = "http://blah/QA/Account/Login";
string testSite = devSite;
using (var b = new IE(testSite))
{
Logon(b);
Social_test_if_navigation_is_working_properly(b);
Social_post_then_check_if_message_is_added(b);
}
}
public void Social_test_if_navigation_is_working_properly(Browser browser)
{
// Arrange
var link = browser.Link(Find.BySelector(".sectionContainer a"));
// Act
link.Click();
// Assert
Assert.IsTrue(browser.ContainsText("Share"));
}
public void Social_post_then_check_if_message_is_added(Browser browser)
{
// Arrange
const string placeholder = "Write your status...";
const string waitBeforeAssert = "Just Now";
string expectedAdded = Guid.NewGuid().ToString() + " are like snowflakes";
var uxTextToPost = browser.TextField(Find.ByName("PostedText"));
var uxSave = browser.Link(Find.ById("SaveButton"));
// Act
uxTextToPost.Focus();
uxTextToPost.TypeTextQuickly(expectedAdded);
uxSave.Click();
browser.WaitUntilContainsText(waitBeforeAssert);
// Assert
Assert.AreEqual(placeholder, uxTextToPost.Text);
Assert.IsTrue(browser.ContainsText(expectedAdded));
}
}
public static class WatiNExtensions
{
public static void TypeTextQuickly(this TextField textField, string text)
{
// Slow:
// textField.TypeText(text);
// Fast:
textField.SetAttributeValue("value", text);
}
}
}
Happy Computing! ツ
Labels:
Automation
My musings on domain modeling. To map or not to map a collection
There's no such thing as bloated model, especially NHibernate doesn't eagerly load an entities' collections when we don't explicitly tell it so.
To paraphrase:
Using Linq's join and .DefaultIfEmpty combo or using QueryOver's .Left.Join is just a missed collection mapping.
I would rather have the Requirements Analyst read the domain models which reflects that we have a business need for a model to report its collections than let them read the QueryOver or join-using Linq. Another reason why ORM and modeling is invented, so for us to have a common language between developers and Requirements Analysts. We should not let our query be our single source-of-truth to reflect the business needs / model relationships
Domain modelling should reflect the fact that we have a business need for something, not that we developers just have a need for a beautiful facade for the data store. So if there's a business need to report a given model's collection, we shall map it then, and let the domain models reflect that fact. We should not let the QueryOver, Linq,and even SQL be the go-to place to verify a business need.
The only time the collection doesn't need to be mapped is if there's no business needs for it. Let's just map collections on-the-fly, that is when when we need them, that is when the business needs arises. We will not be overzealous with model mappings and merrily map all the collections to their owning model.
To make a concrete example of when not to map a collection to their owning entity is the Person model. It's a given that all models have a CreatedByPerson property that indicates the user who created the record. But we will not blindly map all the models as a collection to Person if there's no business need to report all the created records of a given person. Doing so even there's no need, will just complicate/bloat our domain model.
The guidance is, we should not be overly-worried about bloating the models(we will not bloat things deliberately, conscious software development in the action), but rather we should be overly-worried if the domain models doesn't reflect the business needs.
To paraphrase:
A bug is just a missed unit/integration test
Using Linq's join and .DefaultIfEmpty combo or using QueryOver's .Left.Join is just a missed collection mapping.
I would rather have the Requirements Analyst read the domain models which reflects that we have a business need for a model to report its collections than let them read the QueryOver or join-using Linq. Another reason why ORM and modeling is invented, so for us to have a common language between developers and Requirements Analysts. We should not let our query be our single source-of-truth to reflect the business needs / model relationships
Domain modelling should reflect the fact that we have a business need for something, not that we developers just have a need for a beautiful facade for the data store. So if there's a business need to report a given model's collection, we shall map it then, and let the domain models reflect that fact. We should not let the QueryOver, Linq,and even SQL be the go-to place to verify a business need.
The only time the collection doesn't need to be mapped is if there's no business needs for it. Let's just map collections on-the-fly, that is when when we need them, that is when the business needs arises. We will not be overzealous with model mappings and merrily map all the collections to their owning model.
To make a concrete example of when not to map a collection to their owning entity is the Person model. It's a given that all models have a CreatedByPerson property that indicates the user who created the record. But we will not blindly map all the models as a collection to Person if there's no business need to report all the created records of a given person. Doing so even there's no need, will just complicate/bloat our domain model.
The guidance is, we should not be overly-worried about bloating the models(we will not bloat things deliberately, conscious software development in the action), but rather we should be overly-worried if the domain models doesn't reflect the business needs.
Labels:
Discipline,
NHibernate
Wednesday, May 22, 2013
When a language feature renders the naming convention / design pattern obsolete
I initially wrote this kind of code:
I don't want to create a separate class just to emphasize the related variables (e.g. labels), it's too ceremonial. I put prefix on those label variables so I can segregate them from business variables(i.e. variables use for computation, flow, etc), achieving separation of concerns via naming convention. Whether prefixing things is desirable or not is entirely another matter; but certainly, separation of concerns must be addressed.
And then I realize, I can do adhoc-y structure with anonymous type, and voila! naming convention is not necessary anymore to achieve separation of concerns. And suddenly, naming convention feels so WET
Happy Computing! ツ
string i18n_EmployeeName = I18nUtility.GetGlobalResourceString("EmployeeName");
string i18n_CurrentRecipient = I18nUtility.GetGlobalResourceString("CurrentRecipient");
string i18n_SubmittedBy = I18nUtility.GetGlobalResourceString("SubmittedBy");
string i18n_Submitted = I18nUtility.GetGlobalResourceString("Submitted");
string i18n_EffectiveDate = I18nUtility.GetGlobalResourceString("EffectiveDate");
string i18n_Reason = I18nUtility.GetGlobalResourceString("Reason");
string i18n_OffCycleApprovalStatus = I18nUtility.GetGlobalResourceString("Status");
string dateFormat = "m/dd/yyyy";
.
.
.
byte[] ret = ExcelResultFromList.Create(
mapAction: map =>
{
map.Include(x => x.EmployeeName).Label(i18n_EmployeeName);
map.Include(x => x.CurrentRecipient).Label(i18n_CurrentRecipient);
map.Include(x => x.SubmittedBy).Label(i18n_SubmittedBy);
map.Include(x => x.Submitted).Label(i18n_Submitted).Format(dateFormat);
map.Include(x => x.EffectiveDate).Label(i18n_EffectiveDate).Format(dateFormat);
map.Include(x => x.ReasonCode).Label(i18n_Reason);
map.Include(x => x.OffCycleApprovalStatus).Label(i18n_OffCycleApprovalStatus);
},
mapActionForSecond: map =>
{
map.Include(x => x.EmployeeName).Label(i18n_EmployeeName);
map.Include(x => x.CurrentRecipient).Label(i18n_CurrentRecipient);
map.Include(x => x.SubmittedBy).Label(i18n_SubmittedBy);
map.Include(x => x.Submitted).Label(i18n_Submitted).Format(dateFormat);
map.Include(x => x.EffectiveDate).Label(i18n_EffectiveDate).Format(dateFormat);
map.Include(x => x.ReasonCode).Label(i18n_Reason);
map.Include(x => x.OffCycleApprovalStatus).Label(i18n_OffCycleApprovalStatus);
}
)
I don't want to create a separate class just to emphasize the related variables (e.g. labels), it's too ceremonial. I put prefix on those label variables so I can segregate them from business variables(i.e. variables use for computation, flow, etc), achieving separation of concerns via naming convention. Whether prefixing things is desirable or not is entirely another matter; but certainly, separation of concerns must be addressed.
And then I realize, I can do adhoc-y structure with anonymous type, and voila! naming convention is not necessary anymore to achieve separation of concerns. And suddenly, naming convention feels so WET
var i18n = new
{
EmployeeName = I18nUtility.GetGlobalResourceString("EmployeeName"),
CurrentRecipient = I18nUtility.GetGlobalResourceString("CurrentRecipient"),
SubmittedBy = I18nUtility.GetGlobalResourceString("SubmittedBy"),
Submitted = I18nUtility.GetGlobalResourceString("Submitted"),
EffectiveDate = I18nUtility.GetGlobalResourceString("EffectiveDate"),
Reason = I18nUtility.GetGlobalResourceString("Reason"),
OffcycleApprovalStatus = I18nUtility.GetGlobalResourceString("Status")
};
string dateFormat = "m/dd/yyyy";
.
.
.
byte[] ret = ExcelResultFromList.Create(
mapAction: map =>
{
map.Include(x => x.EmployeeName).Label(i18n.EmployeeName);
map.Include(x => x.CurrentRecipient).Label(i18n.CurrentRecipient);
map.Include(x => x.SubmittedBy).Label(i18n.SubmittedBy);
map.Include(x => x.Submitted).Label(i18n_Submitted).Format(dateFormat);
map.Include(x => x.EffectiveDate).Label(i18n.EffectiveDate).Format(dateFormat);
map.Include(x => x.ReasonCode).Label(i18n.Reason);
map.Include(x => x.OffCycleApprovalStatus).Label(i18n.OffCycleApprovalStatus);
},
mapActionForSecond: map =>
{
map.Include(x => x.EmployeeName).Label(i18n.EmployeeName);
map.Include(x => x.CurrentRecipient).Label(i18n.CurrentRecipient);
map.Include(x => x.SubmittedBy).Label(i18n.SubmittedBy);
map.Include(x => x.Submitted).Label(i18n.Submitted).Format(dateFormat);
map.Include(x => x.EffectiveDate).Label(i18n.EffectiveDate).Format(dateFormat);
map.Include(x => x.ReasonCode).Label(i18n.Reason);
map.Include(x => x.OffCycleApprovalStatus).Label(i18n.OffCycleApprovalStatus);
}
)
Happy Computing! ツ
Labels:
Design Pattern,
Discipline
Thursday, May 16, 2013
.NET The Future
Windows/Azure has a bright future and .NET does not. Learn as many as platforms as you needed for your career, it's not on Microsoft's favor to push only .NET to developers when they can earn billions if they support all developer tools, regardless if it come from them or not. Developer's tools from across all platforms, be it programming language, database, and heck even an OS, are all given equal treatment with Azure, it's not partial to .NET nor Windows only.
And if in 1999, the necessary technology and infrastructure for building clouds(e.g. Azure, Amazon, Heroku, etc) were already available then and ready for prime time, Microsoft doesn't even need to create a better Java (read: .NET) to earn billions and developers mindshare. That's a food for thought.
http://odetocode.com/blogs/scott/archive/2013/05/15/where-is-net-headed.aspx
And if in 1999, the necessary technology and infrastructure for building clouds(e.g. Azure, Amazon, Heroku, etc) were already available then and ready for prime time, Microsoft doesn't even need to create a better Java (read: .NET) to earn billions and developers mindshare. That's a food for thought.
http://odetocode.com/blogs/scott/archive/2013/05/15/where-is-net-headed.aspx
Labels:
Stray Tweets
Subscribe to:
Posts (Atom)