This morning at #SCNA Michael Feathers (@mfeathers) showcased an interesting Ruby function each_cons(). At first glance I didn’t think the function was all that useful, but then he showed how you could use it to see if a list is sorted. I couldn’t find a .NET implementation anywhere, so I added it to my Isg.Collections NuGet package as EachConsecutive().
The algorithm relies on knowing the count of items in its source enumerable. This requires that the enumerable be materialzied. I didn’t want to create deferred execution on Enumerable, so the extension method operates on ICollection<T> instead of IEnumerable<T>.
Here’s some sample code:
// Arrange: Declare any variables or set up any conditions // required by your test. var array = new[] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }; // Act: Perform the activity under test. var isSorted = array.EachConsecutive() .All(pair => { var left = pair.First(); var right = pair.Last(); return left < right; }); var isNotSorted = array .Reverse() .ToArray() .EachConsecutive() .All(pair => { var left = pair.First(); var right = pair.Last(); return left < right; }); // Assert: Verify that the activity under test had the // expected results Assert.That(isSorted, Is.True); Assert.That(isNotSorted, Is.False);
It should go without saying that Skyrim is a beautiful game. The visuals are highly detailed and fantastic. However, a game can be pretty and boring too.
The gameplay in Skyrim is very good. My favorite new feature is that you can “dual-cast” a spell by assigning it to both hands. This makes the spell more powerful. You can also augment your character with “perks,” which are similar to Feats in 3rd and 4th edition D&D. The crafting system seems richer than in previous games at first glance. For example, you can cure the pelts from animals you kill into leather. Leather can be cut into strips. Leather strips can be used as components are arms and armor you create.
The UI in Skyrim could be a little better in my opinion. First, the tutorial doesn’t really tell you how important the Tab key is. It basically gets you into and out of your character. Gone is the grid image of your backpack (which is fine). Instead you just have a long list of items. You can click on item categories to get sublists, but the items are just sorted alphabetically. It’s hard to see everything your character has equipped. It would be nice to be able to sort items by weight or value. You still have to play the game of deciding which loot to keep and sell and which gear to drop. It’s harder when you can’t sort by weight and value.
You can select gear, skills, and spells and add them to a “favorites” menu. Pressing ‘Q’ at any time during the game brings up the favorites menu allowing you to use potions or switch out weapons or spells. This is pretty cool, but there doesn’t seem to be a way to hotkey certain items. I know I’ve wished for a healing potion hotkey during combat. On the other hand, entering the favorites menu does pause the action, so there’s no penalty for having to search through your favorites for the healing potion.
Another thing that’s new is the ability to hire followers. I asked an archer to accompany me and he’s helped me through some difficult combat scenarios. The only drawback to the followers is that they are sometimes in the way. Much of the interaction with NPC’s is scripted, and if the follower is standing in the NPC’s path the NPC isn’t usually smart enough to go around. Instead I have to move around a bit to get my follower to move out of the NPC’s way.
The story is interesting so far. I’ve got about 4 hours in the game at this point. There are other gameplay elements that I haven’t mentioned for fear of giving spoilers. In the entire 4 hours I played, I had no game crashes or obvious bugs of any kind. I purchased a copy of the game through Steam.
In my previous post I introduced the Isg.EntityFramework.Interceptors I created. In this post, I will demonstrate the usage of another package that builds on the first: Isg.EntityFramework.Interceptors.SoftDelete
The entire SoftDelete package consists of two classes. The first is an interface:
1: public interface ISoftDelete
2: {
3: bool IsDeleted { get; set; }
4: }
The second is the SoftDeleteChangeInterceptor class.
If your domain class implements ISoftDelete, the SoftDeleteChangeInterceptor will catch the delete operation, set the IsDeleted field to true, and repurpose the operation to an Update.
Here is a unit test that demonstrates this behavior.
1: [Test]
2: public void Delete()
3: {
4: var name = Guid.NewGuid().ToString();
5:
6: using (var db = new CustomerDbContext())
7: {
8: var customer = new Customer {IsDeleted = false, Name = name};
9: db.Customers.Add(customer);
10: db.SaveChanges();
11: }
12:
13: using (var db = new CustomerDbContext())
14: {
15: var customer = db.Customers.SingleOrDefault(i => i.Name == name);
16: db.Customers.Remove(customer);
17: db.SaveChanges();
18: }
19:
20: using (var db = new CustomerDbContext())
21: {
22: var customer = db.Customers.SingleOrDefault(i => i.Name == name);
23: Assert.That(customer, Is.Not.Null);
24: Assert.That(customer.IsDeleted, Is.True);
25: }
26: }
Happy Coding!
I’ve recently published two NuGet packages that may be of interest to Entity Framework Code First users. The first is Isg.EntityFramework.Interceptors which provides an interface for intercepting operations during a call to DbContext.SaveChanges() and taking some action before or after. The 2nd package is Isg.EntityFramework.Interceptors.SoftDelete which provides a concrete implementation of an interceptor.
Using the interceptors library involves three steps.
1) Implement an interceptor.
2) Register your interceptors.
3) Subclass InterceptorDbContext
Implementing an Interceptor
Implementing an interceptor is very easy. The IInterceptor has two methods—Before and After—each of which refer to the SaveChanges() method of DbContext.
1: public interface IInterceptor
2: {
3: void Before(InterceptionContext context);
4: void After(InterceptionContext context);
5: }
The InterceptionContext consists of information your interceptor may need. You get the InterceptorDbContext that is in the process of being saved, the ObjectContext, ObjectStateManager, DbChangeTracker, and list of DbEntityEntry objects that are participating in the operation.
A TypeInterceptor implementation is provided that filters for entities of a certain type. A ChangeInterceptor implementation is provided that filters entities being Added, Modified, or Deleted. The ChangeInterceptor provides Before and After methods that can be overridden for each of those operations.
Register Your Interceptors
Interceptor registration is easy. In the case of the SoftDeleteInterceptor, registration looks like this:
1: InterceptorProvider.SetInterceptorProvider(
2: new DefaultInterceptorProvider(
3: new SoftDeleteChangeInterceptor()));
InterceptorProvider is a global cache of Interceptor instances that relies on a provider to resolve the instances. In this case, I am manually providing the SoftDeleteInterceptor, but you can plug in a dependency injection framework or any other source of providers that you prefer. If you do this at application startup, any instance of InterceptorDbContext will automatically pull in the Interceptors and apply them during the SaveChanges operation.
Subclass InterceptorDbContext
This one really is as easy as it sounds. Instead of inheriting directly from DbContext, inherit from InterceptorDbContext. For example:
1: public class CustomerDbContext : InterceptorDbContext
2: {
3: public DbSet<Invoice> Invoices { get; set; }
4: public DbSet<Customer> Customers { get; set; }
5:
6: protected override void OnModelCreating(DbModelBuilder modelBuilder)
7: {
8: base.OnModelCreating(modelBuilder);
9:
10: modelBuilder.Entity<Invoice>()
11: .HasKey(i => i.InvoiceId)
12: ;
13: modelBuilder.Entity<Invoice>()
14: .HasRequired(invoice => invoice.Customer)
15: .WithMany(customer => customer.Invoices)
16: .HasForeignKey(i => i.CustomerId)
17: ;
18:
19: modelBuilder.Entity<Customer>()
20: .HasKey(customer => customer.CustomerId)
21: ;
22: }
23: }
InterceptorDbContext will build the InterceptionContext object and pass it to your interceptors Before and After the SaveChanges method is called.
Your Input Matters
The current version of the libraries are 0.2.1. If there are other commonly used interceptor implementations that should be implemented I’ll be happy to create packages for those. If you have other feature requests for the package I’d love to hear them.