All posts by Julie

Today is last chance for EF Profiler 30% discount

Ayende has reminded us that Entity Framework Profiler’s Beta period ends tomorrow when it goes RTM. But that means the 30% discount also ends. It will go from $220US to $315US tomorrow.

I spend a lot of time looking at what’s going on in my database when using EF and the views that EFProf provide are indispensible. It’s organized by context instance and provides links back to the .NET code that executed each command.

I sure wish I had stock in this tool!

efprof

Vermont.NET Meeting Monday April 12th – Our Own Visual Studio 2010 Launch

RSVP – http://vtdotnet.eventbrite.com/

The Official Visual Studio 2010 Launch is April 12th in Las Vegas
The UnOfficial Visual Studio Launch is April 12th in Burlington!

Vermont.NET April Meeting_____________________________

Where: G.E. Healthcare in South Burlington

When: Monday April 12th, 6-8:30pm

Speakers: Various Members of the Vermont .NET Community

clip_image002

Topic: These are a Few of Our Favorite Things in VS2010 and .NET 4.0
While Visual Studio 2010 officially launches *today* (April 12th), it’s been around in various forms (Betas and Release Candidate) for quite a while. The votes are in and VS2010 completely rocks. (Really, we’re not just saying it.) There are also some amazing new features in .NET 4.0. At this meeting, various members of the Vermont.NET community will show off some of their favorite new features in VS2010 and .NET 4.0.

More information at www.vtdotnet.org

FOOD & DRINK provided by Green Mountain Coffee Roasters
        Notice this doesn’t say “Pizza”? It will be something different for a change!

GREAT SWAG:

· WINDOWS 7 Ultimate 64bit License!!  (Thanks to Microsoft User Group Support Services)

· VS2010 Books from WROX & O’REILLY

· Telerik License

· ReSharper License

· And some other stuff… 😉

RSVP – http://vtdotnet.eventbrite.com/

A ShareFest

A 30-Day Eval of VS2010 Pro should be online on April 12th. (msdn.microsoft.com/vstudio) If you have the time and inclination, make some DVDs to share with other VTdotNET members. Or bring a thumb drive and get a copy.

Upcoming Meetings_____________________________

May 2010: Tom Cooley will pick a development pattern he’s been using (possibly DDD or TDD ) and teach us about it J Maybe SOLID overview?

June 2010: Microsoft MVP, Joel Hebert, comes from Ottawa with ASP.NET 4.0 Unleashed!

Agile Entity Framework 4 Repository: Part 6: Mocks & Unit Tests

I did finish this series, honest I did. But not in the blog. I’ve shown this in a number of conferences and even in my book, but I never came back and wrote it all down. In fact, I had the whole solutino written before I began the series, but it has gone through a lot of changes.

Where did I leave off?

  1. Agile Entity Framework 4 Repository: Part 1- Model and POCO Classes
  2. Agile Entity Framework 4 Repository: Part 2- The Repository
  3. Agile EF4 Repository: Part 3 -Fine Tuning the Repository
  4. Agile EF4 Repository: Part 4: Compiled LINQ Queries
  5. Agile Entity Framework 4 Repository Part 5: IObjectSet and Include
  6. Agile Entity Framework 4 Repository: Part 6: Mocks & Unit Tests

So finally, I can create the mocks and the tests.

I’m not going to walk through this in great detail since I’ve already done that in my book and don’t feel like laying the whole thing out again. And because my solution has evolved, this last bit of code might not line up exactly with the prior blog posts. You’ll find

I have a single project for tests. It has references to the model classes, the Repositories, the interfaces (IContext, IObjectSet and IEntityRepository), the model and ObjectContext (for integration tests) and System.Data.Entity.

In order to run unit tests, I have a mock context that implements IContext. My real “hit the database” context, also implements IContext. But the mock contexts will return data by creating some fake data on the fly. Yes I said contexts with the “s” on the end. I have more than one.

Because this is all dependent on using IObjectSet (previous post) rather than the ObjectSet, and you can’t instantiate an interface, you’ll need another implementation of the IObjectSet which you can use in your fake context.

It’s pretty simple. The key is to implement all of the interfaces that IObjectSet implements as well and the logic of those methods is fairly common. The trick is to replace the specific methods of the ObjectSet (e.g. Attach, DeleteObject, etc) with generic List methods. So Attach will become Add, DeleteObject will become Remove, etc.

using System;
using System.Collections.Generic;
using System.Collections;
using System.Linq;
using System.Data.Objects;

namespace POCOEFTests
{
  class MockObjectSet<T>: IObjectSet<T> where T : class
  {
    readonly IList<T> _container=new List<T>();

      public void AddObject(T entity)
    {
      _container.Add(entity);
    }

    public void Attach(T entity)
    {
      _container.Add(entity);
    }

    public void DeleteObject(T entity)
    {
      _container.Remove(entity);
    }

    public void Detach(T entity)
    {
      _container.Remove(entity);
    }

    public IEnumerator<T> GetEnumerator()
    {
      return _container.GetEnumerator();
    }

    IEnumerator IEnumerable.GetEnumerator()
    {
      return _container.GetEnumerator();
    }

    public Type ElementType
    {
      get{return typeof(T);}
    }

    public System.Linq.Expressions.Expression Expression
    {
      get { return _container.AsQueryable<T>().Expression; }
    }

    public IQueryProvider Provider
    {
      get { return _container.AsQueryable<T>().Provider; }
    }
  }
}

The IContext interface requires that any implementor return the same set of IObjectSets (Contact, Customers, etc). Our real context already does this (again, by hitting the database). You’ll see that the IObjectSet properties in the mock call local methods which create fake data on the fly to populate the ObjectSet. The various sets of data are related to each other through their foreign keys.

using System;
using System.Collections.Generic;
using System.Linq;
using BAGA;
using BAGA.Repository.Interfaces;
using System.Data.Objects;

namespace POCOEFTests
{
  class MockContext : IContext
  {

    private IObjectSet<Reservation> _reservations;
    private IObjectSet<Customer> _customers;
    private IObjectSet<Trip> _trips;
    private IObjectSet<Payment> _payments;
    private IObjectSet<Contact> _contacts;

    #region IContext Members

    public IObjectSet<Contact> Contacts
    {

      get
      {
        CreateCustomers();
        return _contacts;
      }

    }

    public IQueryable<Customer> Customers
    {
      get
      {
        CreateCustomers();
        return _customers;
      }
    }

    public IObjectSet<Trip> Trips
    {
      get
      {
        CreateTrips();
        return _trips;
      }
    }

    public IObjectSet<Reservation> Reservations
    {
      get {
        CreateReservations();
        return _reservations;
      }
    }



    public IObjectSet<Payment> Payments
    {
      get
      {
        CreatePayments();
        return _payments;

      }
    }

    public string Save()
    {
      throw new NotImplementedException();
    }

public IEnumerable<T> ManagedEntities<T>()
{
  if (typeof(T) == typeof(Reservation))
  {
    var newRes = new Reservation
                   {
                     ReservationID = 1,
                     ContactID = 20,
                     TripID = 1,
                     ReservationDate = new DateTime(2009, 08, 01)
                   };

    var managedRes = new List<Reservation>{newRes};
    return (IEnumerable<T>) managedRes.AsEnumerable();
  }
  return null;
}
    
  
    public bool PreSavingValidate(out string validationErrors)
    {
      bool isvalid = true;
      validationErrors = "";

      foreach (var res in ManagedEntities<Reservation>())
      {
        string validationError;
        bool isResValid = res.ValidateBeforeSave(out validationError);
        if (!isResValid)
        {
          isvalid = false;
          validationErrors += validationError;
        }
      }

      return isvalid;
    }

    #endregion

    private void CreateReservations()
    {
      if (_reservations == null)
      {
        _reservations = new MockObjectSet<Reservation>();
        //one customer has two reservations, the other only has one
        _reservations.AddObject(new Reservation { ReservationID = 1, TripID = 1, ContactID = 19 });
        _reservations.AddObject(new Reservation { ReservationID = 2, TripID = 2, ContactID = 19 });
        _reservations.AddObject(new Reservation { ReservationID = 3, TripID = 1, ContactID = 23 });
      }
    }
    private void CreateCustomers()
    {
      if (_customers == null)
      {
        _customers = new MockObjectSet<Customer>();
        _customers.AddObject(new Customer { ContactID = 1, FirstName = "Matthieu", LastName = "Mezil" });
        _customers.AddObject(new Customer { ContactID = 19, FirstName = "Kristofer", LastName = "Anderson" });
        _customers.AddObject(new Customer { ContactID = 23, FirstName = "Bobby", LastName = "Johnson" });
      }
    }

    private void CreateContacts()
    {
      if (_contacts==null)
      {
        _contacts = new MockObjectSet<Contact>();
        _contacts.AddObject(new Customer { ContactID = 1, FirstName = "Matthieu", LastName = "Mezil" });
        _contacts.AddObject(new Customer { ContactID = 19, FirstName = "Kristofer", LastName = "Anderson" });
        _contacts.AddObject(new Customer { ContactID = 23, FirstName = "Bobby", LastName = "Johnson" });
      }
    }
    private void CreatePayments()
    {
      if (_payments == null)
      {
        _payments = new MockObjectSet<Payment>();
        //create (not enough) payments for reservation 1 (a $1000 trip)
        _payments.AddObject(new Payment
        {
          PaymentID = 1,
          ReservationID = 1,
          Amount = 500
        });
        //create a full payment for reservation 2
        _payments.AddObject(new Payment
        {
          PaymentID = 2,
          ReservationID = 2,
          Amount = 1200
        });
      }
    }
    private void CreateTrips()
    {
      if (_trips == null)
      {
        _trips = new MockObjectSet<Trip>();
        //one customer has two reservations, the other only has one
        _trips.AddObject(new Trip { TripID = 1, DestinationID = 1, TripCostUSD = 1000 });
        _trips.AddObject(new Trip { TripID = 2, DestinationID = 2, TripCostUSD = 1200 });
      }
    }

  }
}

 

This mock context creates valid data. I also have some other mock contexts that intentionally create data that is bad. I can use those contexts to test that my validation code properly responds to bad data.

Here’s an example of two tests that use two different mock contexts. They test the PreSavingValidate method that I created in an earlier post in this series. Because that method is part of the context, I literally copied it from the “real” context into the mock context so that I can test the same exact code.

    [TestMethod()]
    public void PreSavingValidationsGOODTest()
    {
      IContext context = new MockContext();
      bool valid;
      string validationErrors = "";
      valid = context.PreSavingValidate(out validationErrors);
      Assert.IsTrue(valid);
      Assert.AreEqual(validationErrors, "");
    }

    [TestMethod()]
    public void PreSavingValidationsBADDATATest()
    {
      IContext context = new MockContextBadData();
      bool valid;
      string validationErrors = "";
      valid = context.PreSavingValidate(out validationErrors);
      Assert.IsFalse(valid);
      Assert.AreNotEqual(validationErrors, "");
    }

Here’s a test that tests the GetReservationsForCustomer method in the Reservation repository. That is a method that first validates the incoming ID and then if it’s good executes the query. This method is a really good example of why you want to mock. I can’t separate the ID validation from the query execution so I need  a way to “fake” the query execution when I test the ID validation. I’m instantiating the mock and passing it into the ReservationRepository u sing an overload of the constructor. IF I did not pass in a context, the repository will instantiate a new BAEntities, which is the context implementation that executes the real queries against the database.

[TestMethod()]
public void GetReservationsForCustomerValidId()
{
  var context = new MockContext();
  var target = new ReservationRepository(context);
  int customerId = 20; //valid id
  IList<Reservation> actual = target.GetReservationsForCustomer(customerId);
  Assert.IsNotNull(actual);
}

There are a lot of other tests I ran through as well.

Now that I have posted how to build this whole thing, I know everyone will just ask me to post the code so they can just use it rather than READING the blog posts I made such an effort to write. I’ll have to find the time to clean it up (it’s a big sample from my book) before I post it but I REALLY need to get the BOOK FINISHED! (I Know you want me to). So in the mean time, everything you need is in this series. And the rough draft of the relevant chapter is online as part of the Rough Cuts publication of the 2nd edition of my book. One BIG difference from this series and the solution in my book is that I’ve also implemented a unit of work in the book. Therefore I have tests and production code where follow this pattern instead:

[TestMethod()]
public void GetReservationsForCustomerValidIdUnitOfWork()
{
  var context = new MockContext();
    var uow = new UnitOfWork(context);
  var target = new ReservationRepository(uow);
  int customerId = 20; //valid id
  IList<Reservation> actual = target.GetReservationsForCustomer(customerId);
  Assert.IsNotNull(actual);
}

Enjoy!

Stephen Forte visits IDEP Foundation – Recipient of our post-tsunami .NET Charity Auction

Earlier this week I got an email from Stephen Forte saying he was in Bali and would love to meet the folks from IDEP Foundation. IDEP is an Indonesian NGO for which Stephen and I coordinated a charity auction 5 years ago and raised $10,000. We raised another chunk of money in a repeat auction in June of that year as well.

I sent an email to my dear friend Petra, a Canadian ex-pat, who runs IDEP. I met Petra through Keith Pleas who had connections to IDEP and was looking for help for them after the tsunami hit. Petra is in Ubud. Turns out that Stephen was in Ubud, not Bali. Perfect. So they met (I was so happy and also a little envious…but I’ll get their someday.)

Stephen has written a wonderful blog post on his visit with Petra and the continuing amazing work that IDEP does. He says, “I can report back to the .NET community and all of those who donated that our contribution made a difference.”

.NET Charity Auction Follow-up 5 years later

Rough Cuts version of Programming Entity Framework 2nd Edition: Ch 2-22

rc_lrgUpdate August 14, 2010: The Rough Cuts program is over and the book is now out in Digital Format. You can purchase the digital version directly from OReilly at http://oreilly.com/catalog/9780596807252. The print version should be shipping by August 20th. You can also purchase that through O’Reilly, Amazon or elsewhere.

In December, the first 5 chapters of the rough draft of Programming Entity Framework 2nd Edition (for EF4 & VS2010) were published to Safari Rough Cuts. There are now 20 chapters online there. There is no Chapter 1 yet and Chapter 4 is going to be split into 4 and 5. You’ll find that to make up for this split, there is no chapter 17 since 5 will become 6, 6 becomes 7, etc. Beginning with Chapter 18, the numbering is correct.

Just remember this is the *rough draft*. I have a number of amazing people providing feedback that will get incorporated and it still has to go through the copy edit phase as well. But even as a rough draft, it should be pretty good. 😉

Here is the url: http://my.safaribooksonline.com/9780596807276

Note: the day after I posted this, Safari accidentally sent an email to Safari subscribers saying that the book is now in print. That was what we call a SNAFU. Not in print yet. Still a few more months.

RIA Services and Entity Framework POCOs

WCF RIA Services contains a special domain service for entity framework which will automatically be used if you use the Domain Service template in Visual Studio.

This winter I tried the template with a few projects. One of them had EntityObjects and the other had POCO entities.

The EntityObjects worked great, but the POCO entities didn’t work at all.

The bulk of the problems were based on the fact that the EntityDomainService used methods from the first version of Entity Framework that relied on EntityObject being passed in.

I showed this to some of the RIA Services team and how using ObjectSet methods and other .NET 4 methods would easily fix this so that the EntityDomainServive would work with EntityObjects and POCO entities which do not inherit from EntityObject.

The new RIA Services bits released at MIX reflected most of these changes to the EntityDomainService template. Here’s a domain service generated from a POCO entity.

 [EnableClientAccess()]
  public class DomainService1 : LinqToEntitiesDomainService<AdventureWorksSuperLTEntities>
  {
    public IQueryable<Customer> GetCustomers()
    {
      return this.ObjectContext.Customers;
    }

    public void InsertCustomer(Customer customer)
    {
      if ((customer.EntityState != EntityState.Detached))
      {
        this.ObjectContext.ObjectStateManager.ChangeObjectState(customer, EntityState.Added);
      }
      else
      {
        this.ObjectContext.Customers.AddObject(customer);
      }
    }

    public void UpdateCustomer(Customer currentCustomer)
    {
      this.ObjectContext.Customers.AttachAsModified(currentCustomer, this.ChangeSet.GetOriginal(currentCustomer));
    }

    public void DeleteCustomer(Customer customer)
    {
      if ((customer.EntityState == EntityState.Detached))
      {
        this.ObjectContext.Customers.Attach(customer);
      }
      this.ObjectContext.Customers.DeleteObject(customer);
    }
  }

But there is still one more tweak you need to make.

Notice that the EntityState property is used in the Insert and Delete methods. EntityState is exposed by EntityObject so classes that inherit from EntityObject will have an EntityState property. POCO classes, however don’t have an EntityState property. Even if they did, these methods rely on the EntityState value that the ObjectContext supplies, which is what EntityObject.EntityState provides.

So this template still won’t work with POCOs.

Here’s how you can fix that.

Introduce a method into the class that knows how to read the EntityState from the context.

   private EntityState GetEntityState(object entity)
    {
      System.Data.Objects.ObjectStateEntry ose;
      if (this.ObjectContext.ObjectStateManager.TryGetObjectStateEntry(entity, out ose))
        return ose.State;
      else
        return EntityState.Detached;
    }

Then replace code which needs EntityObject.EntityState with a call to this method.

In the above example, it is two calls to customer.EntityState which need to be replaced.

 OLD: if ((customer.EntityState == EntityState.Detached)) 
 NEW: if ((GetEntityState(customer) != EntityState.Detached))

Visual Studio 2010 RC and Crystal Reports 2008 – Just Works

I have Crystal Reports 2008. This is the full application, not the Crystal Reports extension embedded into Visual Studio.

This morning I pulled a solution with many projects and many crystal report files into Visual Studio 2010 for the first time. This application originated in VS2003, migrated to VS2005/.NET 2.0, migrated again to VS2008 (still .NET 2.0) and now migrated to VS2010 (still .NET 2.0). Wow everything is SO much faster working with this solution in VS2010. It loads up so quickly. It builds quickly. Hooray. This is exactly why I wanted to ditch VS2008.

I know that Crystal Reports for Visual Studio is no longer embedded in VS2010 (see my earlier blog post Crystal Reports and Visual Studio 2010) but there is an extension you can easily install. I haven’t installed that yet on this machine.

Now it was time to see what happens with my reports.

It was easy. I double clicked on a report in Solution Explorer. The result was that Crystal Reports 2008 started up and the report opened up in that designer.

No fuss no muss. Thank goodness. I’ve been using Crystal Reports for, I dunno, 15 years or so and every time there’s some type of upgrade, it’s been disastrous. But I wasn’t really upgrading the Crystal Reports app, just Visual Studio this time. SO it was 100% painless.

I did have one problem at runtime because I had forgotten that on  my 64bit machine, I needed to target x86 when building. Luckily I wrote about this in an article for ASPAlliance last year (Lessons Learned: Sorting out Crystal Reports 2008 Versioning, Service Packs and Deployment). Unfortunately I wasted a few hours wrestling with this problem again before I cam to this realization.