Active Record Design Pattern?
I've been looking into various ORM frameworks lately (mostly .net) and see that the Active Record design pattern is commonly used to persist data.
I just wondered what everyone's take on the active record pattern is? Personally I think it puts too much responsibility on the data object, be it just a data container or an entity bean. I have always approached persisting objects from a central controller that exposes a method such as Persist() that takes in an interface say IEntityBean rather than letting the bean sort it's persistence 开发者_JS百科out. This is also the approach I take for population I get a dataset and generically populate the bean rather than letting the bean take in the dataset and populate itself. I just don't like beans having logic. Is this an old fashioned approach or do other share my fears?
How do ORM frameworks that don't use the active record pattern mapping tables to object and object to tables? Is a central controller that controls persisting a bad approach?
Thanks for your opinions in advance!
I don't use ActiveRecord because ActiveRecord violates SRP big time. we have our own inhouse custom made ORM.
EDIT
In our ORM we heavily leverage from C# 3.0 features. We have a library project called Application.Infrastructure
that hosts all bases classes for our business objects. We have a validation framework and automatic mapping using attributes.
The ORM heavily uses Attributes to automate most of the tedious tasks like mapping and validation. I can add new feature as new aspect in the framework.
The TableMapping
attribute will let us know which table this object maps to. The DataFieldMapping
attribute will let us know which coloumn the property of theobject object maps to. The validation framework
lets me validate each field with the help of designated ValidationAttribute
.
Each business-object has a base class EntityBase and, business-collections have EntityCollectionBase. We use traditional data-sets for database interaction and we have written extension method for EntityBase, EntityCollectionBase, DataRow and DataTable. This exntesion methods will read the mapping meta-data from an object's attributes and perform [two-way] mapping via Reflection.
A sample business object would look like this in my application.
User.cs
[TableMapping("Users")]
public class User : EntityBase
{
#region Constructor(s)
public AppUser()
{
BookCollection = new BookCollection();
}
#endregion
#region Properties
#region Default Properties - Direct Field Mapping using DataFieldMappingAttribute
private System.Int32 _UserId;
private System.String _FirstName;
private System.String _LastName;
private System.String _UserName;
private System.Boolean _IsActive;
[DataFieldMapping("UserID")]
[DataObjectFieldAttribute(true, true, false)]
[NotNullOrEmpty(Message = "UserID From Users Table Is Required.")]
public override int Id
{
get
{
return _UserId;
}
set
{
_UserId = value;
}
}
[DataFieldMapping("UserName")]
[Searchable]
[NotNullOrEmpty(Message = "Username Is Required.")]
public string UserName
{
get
{
return _UserName;
}
set
{
_UserName = value;
}
}
[DataFieldMapping("FirstName")]
[Searchable]
public string FirstName
{
get
{
return _FirstName;
}
set
{
_FirstName = value;
}
}
[DataFieldMapping("LastName")]
[Searchable]
public string LastName
{
get
{
return _LastName;
}
set
{
_LastName = value;
}
}
[DataFieldMapping("IsActive")]
public bool IsActive
{
get
{
return _IsActive;
}
set
{
_IsActive = value;
}
}
#region One-To-Many Mappings
public BookCollection Books { get; set; }
#endregion
#region Derived Properties
public string FullName { get { return this.FirstName + " " + this.LastName; } }
#endregion
#endregion
public override bool Validate()
{
bool baseValid = base.Validate();
bool localValid = Books.Validate();
return baseValid && localValid;
}
}
BookCollection.cs
/// <summary>
/// The BookCollection class is designed to work with lists of instances of Book.
/// </summary>
public class BookCollection : EntityCollectionBase<Book>
{
/// <summary>
/// Initializes a new instance of the BookCollection class.
/// </summary>
public BookCollection()
{
}
/// <summary>
/// Initializes a new instance of the BookCollection class.
/// </summary>
public BookCollection (IList<Book> initialList)
: base(initialList)
{
}
}
I used Castle ActiveRecord (based oon NHibernate) after having experimented a lot with NHibernate itself. The ORM approach performs fast enough as long as you are not reading or writing a lot of objects.
With NHibernate you had to specify the mappings through XML files (big pain to keep them in sync with the code) and last time I worked with it, there was the possibility to define them using Attributes in the code. Obviously this makes heavy use in the background of code reflexion and code generation to perform all the "magic". By going further by using the ActiveRecord, I was satisfied with the maintainability of the ORM approach.
What I disliked in Castle ActiveRecord is that you had to derive your ActiveRecord class from a base ActiveRecordBase. What sometimes is a bit strong requirement, because essentially you might want to derive it from something else and let it only behave like an ActiveRecord. There is a way to avoid this by using the ActiveRecordMediator.
I have no experience yet with the MS solution as at the time I needed one they had nothing (they came far too late with a working framework). The Castle Project provided nearly everything I needed for bigger Web Developement from Inversion of Control containers to high level ORM.
DataMapper is way more powerful.
The only ORM that I like (though I don't know .Net) and I deem flexible enough is SQLAlchemy, have a look at some of the examples.
精彩评论