Upload
others
View
7
Download
0
Embed Size (px)
Citation preview
Getting Started with LightSpeed 1
Getting Started with LightSpeed 2
At its worst business logic can be very complex. Rules and logic describe many different cases and slants of behaviour, and it’s this complexity that objects were designed to work with. A Domain Model creates a web of interconnected objects, where each object represents some meaningful individual, whether as large as a corporation or as small as a single line on an order form.
Martin Fowler
Contents
Getting Started with LightSpeed ............................................................................................................. 3
About LightSpeed ................................................................................................................................ 4
Objects and Databases .................................................................................................................... 4
LightSpeed and Object-Relational Mapping ................................................................................... 4
Getting Started ........................................................................................................................................ 6
Building Your First Domain Model .................................................................................................. 6
Modelling ........................................................................................................................................ 7
Designing the Model using the Visual Studio Designer .................................................................. 8
Code Generation ........................................................................................................................... 10
Conventions .................................................................................................................................. 11
LightSpeedContext and UnitOfWork ............................................................................................ 11
Scoping Your Unit of Work ............................................................................................................ 14
Querying ........................................................................................................................................ 14
Creating and Updating Entities ..................................................................................................... 15
Next Steps ............................................................................................................................................. 18
About This Book .................................................................................................................................... 19
Getting Started with LightSpeed 3
Getting Started with LightSpeed
Welcome to the Quick Start walkthrough for LightSpeed. This document will introduce you to the
core concepts, classes and techniques of LightSpeed. We will look at how to create a domain model,
load your domain data and make changes to that data and save it back to your database.
In this walkthrough, we will be using the LightSpeed Visual Studio Designer which takes care of most
routine tasks for you so you will need to have installed LightSpeed prior to embarking on this
document.
Getting Started with LightSpeed 4
About LightSpeed
LightSpeed is a domain modelling and object to relational mapping framework for the .NET
Framework. Simply put, it allows you to design the business entities around which your system will
be formed and handles the retrieval and persistence of those entities allowing you to concentrate on
developing the solution at hand.
LightSpeed has been designed around the idea of a domain model and the philosophy is centred on
the following guiding principles:
Convention over configuration.
Support idiomatic .NET domain models: validation, data binding, change notification etc.
Highly usable API and low barrier to entry.
Encapsulate and encourage best practice patterns: session per request, Unit of Work etc.
Testability built in.
Small, lightweight and fast.
LightSpeed provides a number of runtime components and an integrated Visual Studio designer
which allows you to get productive quickly and helps you integrate with some of the other useful
frameworks available as part of the standard Microsoft .NET development offering.
Objects and Databases
When you analyse a business domain, you are creating a conceptual model of that domain. You
identify the entities in that domain, the state and behaviour of those entities, and their
relationships. However, at some point, that conceptual model has to be translated into a concrete
software implementation.
In fact, in almost all practical business applications, it has to be translated into (at least) two
concrete software implementations: one implementation in terms of programming entities
(objects), and one in terms of a relational database. This is where things start getting tedious and
potentially complex, because the object and relational worlds use quite different representations. At
best, the code to query the database, load objects and save them again is laborious and repetitive.
This is where object-relational mapping comes in. An object-relational mapper, or ORM, takes care
of the mechanical details of translating between the worlds of programmatic objects and relational
data. The ORM figures out how to load and save objects, using either explicit instructions such as an
XML configuration file, or its own heuristics, or a combination of the two. This lets you, the
programmer, focus on writing your business logic and application functionality against the domain
model (in its object representation), without having to worry about the details of the relational
representation.
LightSpeed and Object-Relational Mapping
LightSpeed as an object-relational mapper leans strongly towards using its own heuristics to figure
out how to load and save data: that is, it works out how objects and properties map to tables and
Getting Started with LightSpeed 5
columns without having to be told. This is known as convention over configuration. The immediate
practical benefit of this is that we don’t need to write rules telling the ORM how to load and save
objects. The impact is that it requires us to keep our object design and our database design
reasonably in sync. However, even this has a higher-level benefit: it guides us towards a consistent
data design that reflects the business domain.
Getting Started with LightSpeed 6
Getting Started
Let’s get started with LightSpeed by:
Building up a simple domain model
Running some queries
Making changes to entities
This example will assume you are building using the C# language and are targeting a .NET 3.5 based
solution. The designer experience is the same for both Visual Studio 2008, Visual Studio 2010 and
Visual Studio 2012 but the screenshots depict a Visual Studio 2010 environment.
Building Your First Domain Model
To create your first domain model, start by creating a new project within Visual Studio. For the
purposes of this example we will create a ConsoleApplication but the steps described are common
to any type of application you might be building.
The next step is to create a LightSpeed model. To do this, Add a New Item to the project and then
select LightSpeed Model from the list of available item templates. Enter an appropriate name to
describe the domain for this model, or if you are only likely to use a single domain model for the
entire application the standard is to name this Model.
In this example we will create our model using the name Model, which will generate a new file called
Model.lsmodel within our project.
Getting Started with LightSpeed 7
Note: If you are using Visual Studio 2010 or Visual Studio 2012, you may see this warning dialog pop
up after creating a new model file in a Console Application or Windows Application project.
The reason for this is that by default Visual Studio 2010 and 2012 creates these project types
targeting the .NET 4.0 or .NET 4.5 Client Profile. As LightSpeed is an external dependency you need
to target the standard .NET Framework profile to use it, so you will need to follow through the
instructions shown on the dialog to switch the profile setting over.
Modelling
LightSpeed supports several ways in which you can elaborate your domain model. You can either
start by describing your domain model prior to creating your database (this is known as Model First),
or by starting with an existing database and using that to create your initial model (this is known as
Database First) or by hand crafting your entities using code (this is known as Code First).
We find that most users of LightSpeed start by designing their model first and then
creating the database using tools within the LightSpeed designer.
For the purposes of this example, we will be designing a simple which has 2 entities, a Movie and a
Comment. There is a relationship between Movie and Comment in that a movie may have one or
more comments about it.
We will model this in LightSpeed using a Model First approach which means we will use the
LightSpeed designer to describe our model and then commit this to a database. You could equally
achieve the same goal by using the Database First or Code First approaches but we find that typically
users prefer the Model First approach.
Getting Started with LightSpeed 8
Designing the Model using the Visual Studio Designer
Designing your domain model using the Model First approach is simple using the LightSpeed Design
Surface. To get started, double click on the Model.lsmodel file which opens up the design surface in
Visual Studio.
Expanding the Toolbox pane gives you access to the objects which you can use to model with. We
will start by dragging on 2 entity shapes, one for each of the domain entities we described above.
As you drag these on, you can can set the properties for the Entity underneath the properties pane,
and you can also start adding your entity properties by right clicking and selecting “Add > Entity
Property” (or you can press the ‘Insert’ key – this will create a property after the one that is currently
selected).
It is a good idea to keep your Properties pane pinned within Visual Studio while
performing your modelling as the context will change depending on what you have
selected. You are likely to want to change a number of aspects particularly for entity
properties so this saves you some time.
Getting Started with LightSpeed 9
The next step is to model the relationships between the entities. Drag a one to many association
shape on to Movie and then link it to Comment, this will set up the one to many relationship
between these entities.
Once we have completed modelling, we will want to save our schema to the database. To do this we
first need to describe the type of database we are dealing with and supply an appropriate
connection string so that we can connect to that database. These are entered underneath the
properties for the model. Expand the Properties window and enter the Connection String
accordingly.
Getting Started with LightSpeed 10
Once you have this set up, you can right click on the diagram surface and select “Update Database”.
This will cause LightSpeed to check the existing database and diff between what it finds and what we
have modelled to determine what changes need to be committed from our model back to the
database.
Here is an example of applying our recently created model to an empty database:
The act of creating the table will create the table with any associated properties, and with any
associated relationships to other tables. If you wish to save a copy of the SQL script which it has
generated (this is an exact copy of the statements which will be sent to the database) then check the
Log SQL box.
You may have noticed that there is also an “Update from Source” option. You would use this
to refresh your model with any changes from the database. This also allows you to model
iteratively in both directions.
Code Generation
By using the LightSpeed Designer you are actually generating code for the classes represented by
your models. All LightSpeed domain models are ultimately represented using either C# or VB.NET
code. The design surface provides the productivity boost and convenience in creating these,
however you can craft your own models by using the appropriate conventions and attributes to
describe your intent.
Getting Started with LightSpeed 11
If you expand the Model.lsmodel node within your project you will find a Model.cs file has been
generated and this contains the code definition for the entities we have created. This file will be
regenerated every time you save using the design surface.
If you wish to extend these classes with custom behaviour or additional fields and properties you can
create a partial class to one side of the generated code and add your extensions there. This ensures
that your changes will not be overwritten when regeneration occurs.
Conventions
LightSpeed is an opinionated framework and as such there are a number of conventions that
LightSpeed relies on. It is worth taking a few minutes to examine the output of the LightSpeed
Designers code generation as reviewing the source code for a LightSpeed entity will help highlight to
you some of these conventions.
Here are the key conventions you should expect to find:
1. Every domain entity has a base class of Entity<T>, and more specifically in the example it will
be Entity<int>. This base class provides a lot of the heavy lifting for allowing your entities to
be data bindable, to have validation and to track changes effectively. The type T refers to the
type of your primary key column (the Id property) and you will want to set this accordingly.
2. Every domain entity had an Id property. This is the primary key of the entity and is not
directly assignable. LightSpeed manages the assignment of this value using an identity
strategy and uses this Id when specifying foreign key relationships. If you have an existing
database where the primary key column has a different name you will notice that there will
be a Column attribute attached which will specify the name of the column in the database.
3. LightSpeed has a backing field for each entity property. When LightSpeed looks at what
properties an entity actually contains, it does so by looking at all of the fields on the entity
rather than the properties. This allows you to describe other properties as part of your
domain model which are not stored in the database. If you wish to have additional fields on
the entity you can do so by appending the [Transient] attribute to the fields which should be
ignored by LightSpeed.
4. Every domain entity has been generated as partial so you can extend them as required.
LightSpeedContext and UnitOfWork
When using LightSpeed you will be interacting with a UnitOfWork to perform your queries and
persist any changes to your entities.
The UnitOfWork is scoping pattern which is wrapped around the idea of a business transaction
within your application. As described by Martin Fowler, the Unit Of Work pattern “maintains a list of
objects affected by a business transaction and coordinates the writing out of changes and the
resolution of concurrency problems.”
Getting Started with LightSpeed 12
A unit of work in LightSpeed is primarily two things:
1. A database connection 2. An Identity Map of references to the entities currently in memory.
Typically, you will create a unit of work per thread (in a Windows application) or a unit of work per
request (in a Web application). However, you can create multiple units of work within the same
thread or request if you need to do so. A unit of work should not be shared between threads as it
encapsulates non-thread-safe resources such as database connections. A unit of work is designed to
be short-lived: typically you will create the unit of work, load and/or modify some data, save changes
if required, and then dispose of the unit of work.
In LightSpeed you will instantiate your IUnitOfWork instances by using a LightSpeedContext. The
LightSpeedContext manages the configuration required to connect to a database and any additional
LightSpeed behaviours that are required to work with your schema such as pluralisation of table
names.
As entities are loaded from the database through the IUnitOfWork.Find method, and new entities
are added through the IUnitOfWork.Add method, they become tracked by this unit of work. If any of
the entities are modified they will be automatically persisted during the next save operation.
To save the changes in a unit of work, call IUnitOfWork.SaveChanges.
To dispose of a unit of work and free associated resources, call IUnitOfWork.Dispose.
Configuration for the LightSpeedContext can either be specified via the properties of the context
object, or using application configuration. The suggested approach is to use application
configuration and to assist with this we have a helper which is built in to the LightSpeed designer
which will provide you with the configuration blocks you will need.
To use this, right click on the design surface and select “Get Started”. This will bring up a dialog with
the configuration block information and example code for instantiating your LightSpeedContext
instance.
Getting Started with LightSpeed 13
So in this case our configuration block would look similar to this.
Sample configuration block generated by the Getting Started context menu option
<configSections> <section name="lightSpeedContexts" type="Mindscape.LightSpeed.Configuration.LightSpeedConfigurationSection, Mindscape.LightSpeed" /> </configSections> <lightSpeedContexts> <!-- TODO: Check pluralizeTableNames setting --> <!-- TODO: Add identityMethod="..." if not using KeyTable --> <add name="Development" connectionStringName="Development" dataProvider="SqlServer2005" pluralizeTableNames="False" /> </lightSpeedContexts> <connectionStrings> <add name="Development" connectionString="Data Source=.;Initial Catalog=GettingStarted;Integrated Security=True;Pooling=False"/> </connectionStrings>
Getting Started with LightSpeed 14
Scoping Your Unit of Work
The UnitOfWork has been designed to be short lived and is ideally scoped around a single set of
logical operations. It may however be more sensible for the application you are building to scope the
UnitOfWork more widely; for example, within a Web Application you would generally want to scope
it on a per-request basis.
LightSpeed can automatically create and dispose units of work for you based on the common
patterns mentioned above. To do this, instead of creating a unit of work explicitly using
LightSpeedContext.CreateUnitOfWork, create a UnitOfWorkScopeBase and access the
UnitOfWorkScopeBase.Current property.
For the per-request approach described above, LightSpeed provides a built-in implementation of
UnitOfWorkScopeBase, named PerRequestUnitOfWorkScope<TUnitOfWork>.
Querying
LightSpeed provides both a native query API and a LINQ provider to allow you to express queries to
retrieve entities. Initially we would recommend you focus on using the LINQ provider since this will
be the most familiar to you.
If you are targeting the .NET 3.5 or 4.0 frameworks then the LightSpeed Designer will add code
generation for a strongly typed UnitOfWork class which provides a property for each entity type
which gives you an IQueryable for each entity collection. This gives you a starting point to write LINQ
queries using LightSpeed.
For example, for the model which we generated earlier a class called ModelUnitOfWork. Here is the
associated code which was generated for it.
[System.CodeDom.Compiler.GeneratedCode("LightSpeedModelGenerator", "1.0.0.0")] public partial class ModelUnitOfWork : Mindscape.LightSpeed.UnitOfWork { public System.Linq.IQueryable<Comment> Comments { get { return this.Query<Comment>(); } } public System.Linq.IQueryable<Movie> Movies { get { return this.Query<Movie>(); } } }
We can create new instances of our strongly typed UnitOfWork by using the following syntax. You
will notice that we are specifying the type of our UnitOfWork as a generic argument to the
LightSpeedContext and specifying a configuration name for that context as the argument for its
constructor. That configuration name matches up to the a section in our configuration that we
created earlier using the Getting Started context menu option.
Getting Started with LightSpeed 15
private static LightSpeedContext<ModelUnitOfWork> _context; // initialize the context _context = new LightSpeedContext<ModelUnitOfWork>("Development"); // create a unit of work using (var uow = _context.CreateUnitOfWork()) { }
Once we have created our typed unit of work we can start writing our queries. Here is an example of
a LINQ query we might write using our UnitOfWork instance.
Querying the database
foreach (var movie in uow.Movies.OrderBy(m => m.CreatedOn)) { Console.WriteLine("{0} - comment count: {1}", movie.Title, movie.Comments.Count()); }
LightSpeed’s LINQ provider has been written as a translation engine over the native querying API, so
any query which can be expressed in the native query API can also be expressed in LINQ (and vice
versa).
Because the LightSpeed LINQ provider acts as a translator for you to express queries in the
LightSpeed query API if you try to express a query which could not otherwise be written using the
query API then a NotSupportedException will be thrown. Similarly LINQ allows for an open ended
number of possible queries to be expressed, however not all queries may be sensible or even
possible given the nuances of your particular data provider. Please review the sections on Database
Providers and Querying in the LightSpeed User Guide to understand more about this.
Creating and Updating Entities
As described above, when you create and manipulate entities with LightSpeed, you do so within the
scope of a business transaction which is known as a unit of work, represented by the IUnitOfWork
interface or UnitOfWork class.
To create an entity, you simply instantiate it as with any standard .NET object and assign values to
the applicable parameters. You can then Add it to a UnitOfWork by calling UnitOfWork.Add(entity).
Getting Started with LightSpeed 16
Here is an example from our earlier model.
Creating a new entity and adding it to the unit of work
var movie1 = new Movie(); movie1.Title = "Hackers (1995)"; movie1.Description = "A young boy is arrested by the US Secret Service for writing a computer virus and is banned from using a computer until his 18th birthday."; uow.Add(movie1); uow.SaveChanges();
If you are dealing with an entity which has relationships to other entities, these can either be
assigned directly to the associated property, or will be automatically assigned by LightSpeed if you
add the entity to a child collection of an existing LightSpeed entity (this also means you don’t
actually need to call UnitOfWork.Add either).
Adding an entity to the unit of work by association
var comment1 = new Comment(); comment1.Body = "Best hackers movie ever!"; comment1.PostedBy = "John-Daniel Trask"; comment1.Movie = movie1; uow.SaveChanges(); // comment1 has been added to the UnitOfWork by association
To update an entity, you must first fetch it by using a query. Once you have an instance of the entity
available you can update data on the properties of the entity. LightSpeed implements the standard
.NET INotifyPropertyChanged event and will raise this when a property has been updated.
LightSpeed will also track if an entity has been modified during its lifecycle within the scope of a
UnitOfWork so that it only sends updates for entities which have actually been modified during the
course of the UnitOfWork.
Updating an existing entity
foreach (var comment in uow.Comments) { comment.PostedBy = "A dodgy spammer"; } uow.SaveChanges();
To remove an entity, you can call UnitOfWork.Remove(entity) to mark it as deleted. If the entity has
dependent children they will also be deleted otherwise any objects which have an association to the
Getting Started with LightSpeed 17
entity which is being deleted will be unwired which means the entity would be removed from an
child collections it belongs to, and any direct associations held to the entity will be set to null. This
has the effect that removing an entity may actually cause several entities to be updated in the
process.
Deleting an existing entity
foreach (var movie in uow.Movies) { uow.Remove(movie); } uow.SaveChanges();
Note: If you need to remove many entities at once you should look at using the alternative overload
of UnitOfWork.Remove(query) which allows you to pass in a query object to scope what should be
removed. If you are using this overload please be aware that this means that no entities will be
loaded into the UnitOfWork as a result (this is actually one of the good reasons to use this approach
– to avoid unnecessary entity hydration if you are only intending to immediately remove the entity).
Note also that you must still call SaveChanges to commit the deletion.
When making changes, they are not immediately saved to the database. You explicitly flush any
pending changes by calling UnitOfWork.SaveChanges() which will determine all of the
insert/update/delete statements which need to be sent (in the appropriate ordering based on the
structure of the database) and will issue the statements accordingly.
Note: If the database provider supports command batching then statements will be issued as a batch
of changes. There are certain operations such as Insert statements where the identity strategy for the
entity is IdentityColumn which cannot be batched and will always be issued as individual statements.
Getting Started with LightSpeed 18
Next Steps
Now that you have built a simple application using LightSpeed, take a look at the samples and review
the LightSpeed User Guide for more information about the product. We would recommend
reviewing the Quick Start sample first as this builds on the model we have elaborated in this guide
and extends it to be part of a working web store application.
We have provided a sample which contains the model and code highlighted in this document as part
of the LightSpeed installation. You can find the solution named “Getting Started” underneath the
LightSpeed >> Samples folder in your start menu.
Getting Started with LightSpeed 19
About This Book
This book walks you through the process of creating a simple LightSpeed application. You should
read it in conjunction with these other books:
LightSpeed User Guide, which provides conceptual documentation and guidance
information.
LightSpeed API Reference, which provides detailed documentation for all LightSpeed classes
and members
You can access these books from the Mindscape > LightSpeed folder on the Start menu.