Showing posts with label Linq to Entities. Show all posts
Showing posts with label Linq to Entities. Show all posts

Wednesday, 4 February 2009

LINQ Best Practice 2 – One Data Source Per Repository, Inner and Outer Joins in LINQ

I’ve been using LINQ a lot with my new project, in particular with the Entity Framework.  I have learned a lot about good practices with LINQ and I figured it would be a good idea to post them up here.  Contained in these posts are all those things I’ve learned and suggest could be useful to you.  They are best practices that work for me, if they work for you then great, but everyone’s solution is different.

My first post was about how you can use the repository pattern to increase testability and separate concerns within your code.  This second post extends on that into the data sources.

The Data Source

This is the main table that your repository is concerned with.  If at all possible you should have only one of these per repository.  The main reason for this is the testability and maintainability of your code, setting up a single data source is far more simple than setting up several.  You will also find that the code produced will be more standardised and far more simple to read.

Example Code:

public class TypeRepository : ITypeRepository
{
    private LynxEntities _Entities;
    private IQueryable<Type> _DataSource;
    
    // Implementation here.
}

One to One Inner Join

This is the single data source for my TypeRepository.  This is easy to implement with when you’re only selecting data relating to that type, but when you’re selecting data that crosses multiple tables this becomes a little more difficult.  For example if my Type table has a foreign key to the Usage table and I want to get all types that are within a particular usage I would immediately think of doing a join.  This isn’t really the LINQ way, instead you should use the generated object dependencies to your advantage:

rfType ITypeRepository.GetByUsage(string usage, string type)
{
    rfType result = (from t in _DataSource
                     where t.rfUsage.Name.Equals(usage)
                         && t.Name.Equals(type)
                     select t).FirstOrDefault();
    return result;
}

 

One to Many Inner Join

This becomes a lot more tricky when you’re dealing with a one to many reference, or even worse a many to many reference.    Say for example my Type table has many usages, the above code is not going to work because there are lots of usage records.  But you can do a simple join to ensure that data exists.  You don’t need a second data source to do a join, though most of the examples out there will tell you to do it that way, you can do it like this.

rfType ITypeRepository.GetByUsage(string usage, string type)
{
    rfType result = (from t in _DataSource
                     from u in t.rfUsage
                     where u.Name.Equals(usage)
                         && t.Name.Equals(type)
                     select t).FirstOrDefault();
    return result;
}

One to Many Left Outer Join

The above is fine if you know the dependency exists, but when if you want to get the item even if it’s usage doesn’t exist, but you also want to retrieve the usage?  The Entity Framework is not great at lazy loading through LINQ, if you need something, you really have to ask for it.  So if I were to write:

public Type Get(string type)
{
    return (from t in _DataSource
            where t.Name.Equals(type)
            select t).FirstOrDefault();
}
 
// Other Code
Type returnedType = _TypeRepository.Get("myType");
ProcessUsage(returnType.rfUsage);

The ProcessUsage method would get NULL every time I called the method, even if there were several usages available in the database.  This is because the lazy loading of L2E (LINQ to Entities) is not all that great.  So you need to outer join all the tables you want to use data from.

return (from t in _DataSource
        where t.Name.Equals(type)
 
        let u = (from uLO in t.rfUsage
                 where uLO.Name.Equals(usage)
                 select uLO)
 
        select new 
        {
            t, u
        }).FirstOrDefault().t;

The middle part is the left outer join.   We tell LINQ to get t and u from the database but then return only t.  The entity framework will get all the data and return it in the t object representing the type.  You can also use this to limit your data selection of course.

Test Cases

Now when writing your tests all you need to do to setup a source of data is:

[TestInitialize()]
public void InitialiseTest()
{
    _Items = GetTestData();
    _Target = new TypeRepository(_Items.AsQueryable());
}
 
private List<Type> GetTestData()
{
    List<Type> items = new List<Type>
    {
        new Type
        {
            TypeID = 1,
            Name = Constant.Type.Thickness,
            rfUsage = new rfUsage
            {
                UsageID = 1,
                Name = Constant.Usage.ItemSize
            }
        },
        new Type
        {
            TypeID = 1,
            Name = Constant.Type.Weight,
            rfUsage = new rfUsage
            {
                UsageID = 1,
                Name = Constant.Usage.ItemSize
            }
        }
    };
 
    return items;
}

If you had multiple data sources (like one for type and one for rfusage) you would have to set each of them up individually and the links between them would not be setup in the same way as the links setup in the Entity Framework.

Friday, 19 December 2008

Testable Left Outer Join in LINQ to Entities

Recently I posted on Testable LINQ to Entities using the repository model and doing table joins using your query items.  I’ve found that while this works great for inner joined table queries it doesn’t work for left outer joins, in fact doing a left outer join in LINQ to Entities is quiet hard entirely.

So in my example, here is the SQL used to retrieve the data.

SELECT * FROM
Item
    LEFT OUTER JOIN ItemLocation il
        on Item.ItemCodeID = il.ItemCodeID
    LEFT OUTER JOIN WarehouseLocation wl
        on il.WarehouseLocationID = wl.WarehouseLocationID
    LEFT OUTER JOIN Warehouse w
        on wl.WarehouseID = w.WarehouseID
    LEFT OUTER JOIN Site s
        on w.SiteID = s.SiteID

In LINQ I can do this far more simply:

from item in _ItemData
select item

But when I try and look at the resulting item.ItemLocation it returns an empty list.  When using LINQ to Entities it will only load the data model for items you tell it to load for, in this case I asked for Item, but not all the ItemLocations as well.  This is shown in the screenshot below, the array of item locations has no items even though they are in the database.

image

I want the item to return even though I don’t necessarily have any locations, and I want to be able to get the site.

Well here is how you do a left outer join in LINQ to Entities:

var results = (from item in _DataSource
               where item.Name.Equals(name)
               // Left outer join into the item locations, warehouses and sites to calculate the data.
               let itemLoc = (from itemLocLeftOuter in item.ItemLocation
                              select new
                              {
                                  itemLocLeftOuter,
                                  itemLocLeftOuter.WarehouseLocation,
                                  itemLocLeftOuter.WarehouseLocation.Warehouse,
                                  itemLocLeftOuter.WarehouseLocation.Warehouse.Site
                              })
               select new
               {
                   item,
                   item.Class,
                   item.ItemLocation,
                   itemLoc
               }).FirstOrDefault();
 
Item item = results.item;

I have to join into the ItemLocations because for each item there can be many locations.  But selecting all the data I need, including joined data, my resulting item will have all it’s dependencies loaded from the database.  The screenshot of the watch below shows that my item.ItemLocation holds all the joined items now.

image 

As with my previous post on testable LINQ to Entities, this code is completely testable and works very well within the repository pattern.

Thursday, 13 November 2008

Testable LINQ to Entities with Table Joins

I’m enjoying LINQ to Entities and the Entity Framework, but sometimes it feels like someone forgot to write up the documentation.  It’s nice to have the product, but it can be very hard to use without adequate training or documentation.  But I suppose it’s very new and I’ll just have to wait for more people to jump on board.

In the mean time, I came across a problem.  We are following the repository model design pattern in our data access objects and when using LINQ to Entities we define the table that the model is concerned with as a private member from the Entity Model.  This allows us to use TDD and override this private member with our own queryable datasets for testing.

So your data repository will look something like this:

public class SecurityUserRepository : ISecurityUserRepository
{
    private IQueryable<rfSysAction> _rfSysActionData;
    private MyEntities _Entities;
 
    public SecurityUserRepository()
    {
        _Entities = new MyEntities();
        _rfSysActionData = _Entities.rfSysAction;
    }
 
    public SecurityUserRepository(IQueryable<rfSysAction> sysAction)
    {
        _rfSysActionData = sysAction;
    }
}

Nice code, easy to test, but what if I want to join several tables together to get a set of data back?  For example, my user is part of several security groups, my security groups are linked to my MVC actions and my actions are defined in a table specifying their names. 

Well it’s actually not that hard.

Most often when trying to write this join you’ll get examples that make you reference the entity model to get all the tables you want to join.  This works fine, but it’s not good for me because I have only mocked up a copy of my root object.

Then I found this great example showing how it can be done.  So if I want to return an action table record for a specific action, controller and user my LINQ should look like this:

IQueryable<rfSysAction> query = 
    from sa in _rfSysActionData
        from sas in sa.rfSysActionSecurity
        from usg in sas.rfSecurityGroup.UserSecurityGroup
    where usg.UserID == userIdparam 
        && sa.Controller == controller
        && sa.Name == action
    select sa;

This query will find all security actions and then limit the selection to those for the user, controller and action specified.  This is what my model looks like for the tables in the above query:

image

The code is simple and makes sense when you know what to do, but now that I have it, how do I test it.  With rfSysAction as my base table, all I have to do is mock this up and add in the child table data like this:

private IQueryable<rfSysAction> GetTestUserSecurityGroupData()
{
    List<rfSysAction> data = new List<rfSysAction>
    {
        new rfSysAction()
        {
            ActionID = 1,
            Controller = "controllera",
            Name = "actiona",
            rfSysActionSecurity = new EntityCollection<rfSysActionSecurity>()
            {
                new rfSysActionSecurity()
                {
                    rfSecurityGroup = new rfSecurityGroup()
                    {
                        Name = "groupa",
                        UserSecurityGroup = new EntityCollection<UserSecurityGroup>()
                        {
                            new UserSecurityGroup()
                            {
                                UserID = 1
                            }
                        }
                    }
                }
            }
        }
    };
 
    return data.AsQueryable();
}

And then use the override constructor to set the data:

ISecurityRepository target = new SecurityRepository(GetTestUserSecurityData());

There you have it, testable LINQ to Entities and I haven’t had to overwrite several table object. 

Tuesday, 30 September 2008

Unit Testing Linq To Entities

We have decided that we're going to use Linq to Entities and the Entity Framework Model in our new project. This means that for our model we are going to have to create unit tests that mimic the database environment. We don't want our unit tests to connect to the database itself because that will make the unit tests unnecessarily lengthy and put the developers in a position where they will avoid doing the unit tests where possible. Also when doing unit testing against the database you must make sure that the data int he database is correct before doing the unit test. The solution is to create your own data in code or with XML that you can test against reliably. I came across this blog by Ian Cooper that goes trough a fantastic way to create your own data in exactly this fashion. If you're using Linq to Entities or Linq to SQL and you are trying to follow TDD, I highly recommend reading this blog.