Entity Layer - netTiers/netTiers GitHub Wiki
An Entity is not a regular .net type, it is a significant type that holds an intrinsic meaning that is essential in your application. Entities can be real or conceptual and in business applications, which .netTiers is tailored to, holds a special meaning to the data. For example: an Employee, an Order and OrderDetail are all entities. Entities are malleable and have a lifecycle, and their types must take extra special checks and balances to ensure the integrity of the entity.
You can think of an Entity as the Reflection of a Database-table exposed in Object Oriented fashion. The Word Entity is the same which is commonly used and mostly for a Database-Table. So in the .Net environment an object reflecting a Database-table cascadingly, Exposing its Columns as class-Properties, Carefully matching the Data Types between a database-column and a .Net-Type(Let say string for varchar and Int16 for smallint).
Now think of a Database-Table 'USERS' where, definitely, each row of table is representing a single User's complete information(like her ID, Name, Age and Address) and on the other hand in .Net imagine an Object 'User' (with properties ID, Name, Age and Address). It actually is representing One User from database-table 'Users' or One Row of a database-table.
.netTiers' architecture provides us with an environment where we can Control the database entities (tables) by passing in these .Net entities in special methods. In fact those special methods which control these .Net entities are called "Controller Objects". We'll be studying about them in next articles. for now we should make sure that we know that we can call some dedicated methods from Controller objects to Retrieve an Entity or List of Entities. We can pass in an Entity into Controller behaviors to perform *CRUD actions.
*CRUD::Create, Read, Update, Delete
The entity first came to be known as the Entity Relation model, ERM. You can read the white paper that pretty much coined the term Entity and the entity life cycle. The Entity Relationship Model - Toward A Unified View of Data - By Dr. Peter Chen. This is significant because it was the first time there was a segregration in the way we made representations in a program from our data. These representations allows us to focus on creating a rich programmer experience with these valuable representations that we
.netTiers uses the notion of the entity along with the TableModule & Data Transfer Object (DTO) Patterns in order to expose your database as entities. Meaning, for every table in your database, an entity will be generated for that table. The DTO allows you to pass the lightweight entities through the many tiers while still maintaining the loosely coupled open ended architecture of the Entity Layer, since it doesn't depend on any DataProvider.
.netTiers will also attempt to discover all of the relationships that your table has with other tables in the database and will create child properties of those relationships. This will build out your entire entity domain. Currently the relationship types supported are one to one, one to many, and many to many relationships. These relationships make it easy for you to intuitively work with your entities and now have a logical object graph. There are several ways to create a certain relationship, but we'll discuss the rules in the Database Model section.
Example: Customer Entity
///An example object graph of a customer entity looks like this.
/// Customer Parent
/// Order 1:1
/// OrderDetails //1:M
/// ProductCollection //1:M
/// CustomerDemographics //1:M
/// CustomerDemographicsCollection_From_CustomerCustomerDemo //M:M
The entities all inherit from two parent classes, the user class called EntityBase.cs which inherits from EntityBaseCore.generated.cs, the generated class. As mentioned earlier, every .generated class will be generated over and over again, do not modify these classes as your work will be lost when you regenerate your code.
These base classes implement the base behavior across all entities. The EntityBase class provides you with exclusive access to modify the behavior across all entities. You can override our default implementation and these changes will not get overwritten. This is a way for you to make changes and extend the behavior while at the same time, safeguarding your work.
EntityBase The EntityBase classes provide behavior to manage state using the EntityState property.
Entity State provides a way to track the current status of an entity in it's entity lifecycle, which differs from the CLR object lifecycle. There are 4 main EntityStates, found in the EntityState enumeration, Unchanged, Added, Changed, and Deleted. You do not have to manually keep track of state, when you modify a property, or create a new entity, or read an entity from the database .netTiers will automatically change the state and keep track for you.
/// <summary>
/// List of possible state for an entity.
/// </summary>
public enum EntityState
{
/// <summary>
/// Entity is read in from the database, unchanged
/// </summary>
Unchanged=0,
/// <summary>
/// Entity is new and not yet in the database
/// </summary>
Added=1,
/// <summary>
/// Entity has been modified
/// </summary>
Changed=2,
/// <summary>
/// Entity has been deleted
/// </summary>
Deleted=3
}
Assuming you have no data in your database, the very first thing you will do is add data to the database. In order to do this, you will have to create a new entity. Let's use the Customer entity that we've generated from the Northwind database, and create a new Customers entity and walk through the different states of the entity, which as mentioned earlier is different than the object lifecycle.
///STAGE 1: Added
///Create a new entity, whose EntityState is EntityState.Added
Customers customer = new Customers();
customer.Address = "102 West Main Street";
customer.City = "Atlantis";
customer.Region = "Sea";
customer.Phone = "230-555-0909";
Response.Write(customer.EntityState); // EntityState.Added;
///Persist
DataRepository.CustomersProvider.Save(customer);
///STAGE 2: Unchanged
/// The EntityState has been set to EntityState.Unchanged
///Once we persist the entity, it will refresh the entity from the database
///If there is an identity column in your table that the entity represents
/// then the new identity of the inserted value is returned as well.
Response.Write(customer.CustomerID);
Response.Write(customer.EntityState); // EntityState.Unchanged;
///STAGE 3: Changed
/// By modifying a property the entity will automatically
/// change state to an EntityState.Changed state.
customer.Region = "Under The Sea";
Response.Write(customer.EntityState); // EntityState.Changed;
DataRepository.CustomersProvider.Save(customer);
///STAGE 4: Deleted
/// Two ways exist being in an EntityState.Deleted state for an Entity.
/// MarkToDelete() method, or directly calling Delete in the repository.
/// MarkToDelete() is mainly used when using the Save() method,
/// wanting to delete an entity which aggregated
/// and part of a Collection, a TList<Entity>.
/// If you are working with a single entity as depicted, you would
/// just pass the entire entity into the DataRepository for Deletion.
///Using Delete Method Directly on the Entity
DataRepository.CustomersProvider.Delete(customer);
///Using Save Method in DataRepository with MarkToDelete()
customer.MarkToDelete();
Response.Write(customer.EntityState); // EntityState.Deleted;
DataRepository.CustomersProvider.Save(customer);
///EntityState.Unchanged
///Say I want to delete all customers that have a city = Atlantis
/// Whenever you get any entity items from the Database, once created
/// the state is immediately changed to EntityState.Unchanged
TList<Customers> myList = DataRepository.CustomersProvider.GetByCity("Atlantis");
for (int i = 0; i < myList.Count; i++)
{
Response.Write(myList[i].EntityState); // EntityState.Unchanged;
myList.RemoveEntity(myList[i]);
Response.Write(myList[i].EntityState); // EntityState.Deleted;
}
/// Now you've actually removed the entities from the list
Response.Write(myList.Count); // Prints 0
/// They are however moved to a DeletedItems collection
Response.Write(myList.DeletedItems.Count); //Prints 1
///Will delete the items from the Database
DataRepository.CustomersProvider.Save(myList);
///Or you can Iterate the list calling MarkToDelete();
myList.ForEach(
delegate(Customers c)
{
c.MarkToDelete();
}
);
///Will persist all of the Deleted entities.
DataRepository.CustomersProvider.Save(myList);
You can override the default behavior if you wanted and managed how EntityState was used. Perhaps if you wanted to use a StateMachine instead to manage state. Overall, it's a flexible approach that should let you customize however you need to..
There are also some entity state descriptor properties such as IsDeleted, IsDirty, IsNew. One other thing to note, is that you can remove the flag after using MarkToDelete by calling RemoveDeleteMark().
Excerpt from EntityBase.generated.cs
/// <summary>
/// True if object has been <see cref="MarkToDelete"/>. ReadOnly.
/// </summary>
[BrowsableAttribute(false), XmlIgnoreAttribute()]
public bool IsDeleted
{
get { return this.currentEntityState == EntityState.Deleted; }
}
/// <summary>
/// Indicates if the object has been modified from its original state.
/// </summary>
/// <remarks>True if object has been modified; otherwise False;</remarks>
[BrowsableAttribute(false), XmlIgnoreAttribute()]
public bool IsDirty
{
get
{
return this.currentEntityState != EntityState.Unchanged
&& this.currentEntityState != EntityState.Added;
}
}
/// <summary>
/// Indicates if the object is new.
/// </summary>
/// <remarks>True if objectis new; otherwise False;</remarks>
[BrowsableAttribute(false), XmlIgnoreAttribute()]
public bool IsNew
{
get { return this.currentEntityState == EntityState.Added; }
set { this.currentEntityState = EntityState.Added; }
}
/// <summary>
/// Indicates state of object
/// </summary>
/// <remarks>0=Unchanged, 1=Added, 2=Changed</remarks>
[BrowsableAttribute(false), XmlIgnoreAttribute()]
public virtual EntityState EntityState
{
get { return this.currentEntityState; }
set { this.currentEntityState = value; }
}
/// <summary>
/// Accepts the changes made to this object.
/// </summary>
/// <remarks>
/// After calling this method <see cref="IsDirty"/> and <see cref="IsNew"/> are false.
/// <see cref="IsDeleted"/> flag remain unchanged as it is handled by the parent List.
/// </remarks>
public virtual void AcceptChanges()
{
this.bindingIsNew = false;
this.currentEntityState = EntityState.Unchanged;
OnPropertyChanged(string.Empty);
}
///<summary>
/// Revert all changes and restore original values.
/// Currently not supported.
///</summary>
/// <exception cref="NotSupportedException">This method throws exception.</exception>
public abstract void CancelChanges();
///<summary>
/// Marks entity to be deleted.
///</summary>
public virtual void MarkToDelete()
{
if (this.currentEntityState != EntityState.Added)
this.currentEntityState = EntityState.Deleted;
}
///<summary>
/// Remove the "isDeleted" mark from the entity.
///</summary>
public virtual void RemoveDeleteMark()
{
if (this.currentEntityState != EntityState.Added)
{
this.currentEntityState = EntityState.Changed;
}
}
The entities themselves implement several interfaces to provide the full featured needs of consuming layers. The first two layers are custom .netTiers interfaces, the rest are from the System.ComponentModel and System.Runtime.Serialization namespaces.
Some of these interfaces are :
- IEntityId - Gives exposure to an encapsulated primary key for your entity. Supports containment of composite primary keys.
- IEntity- A .netTiers interface that provides all the functionality required for a .netTiers entity.
- IComparable- Implements the ability to compare two entities types.
- ICloneable- Implements the ability to clone an entity
- IEditableObject- Implements the ability to commit or rollback changes to an object that is used as a datasource.
- IComponent- Implements functionality required by all .Net System.ComponentModel Components.
- INotifyPropertyChanged- Notifies subscribed clients that a property value has changed.
- IDataErrorInfo- Provides the functionality to offer custom error information that a user interface can bind to.
- IDeserializationCallback- Indicates that a class is to be notified when deserialization of the entire object graph has been completed.
Generated views object types are not considered entities, although they share many similar attributes. View objects do not maintain state because they currently can not be persisted back into the database.
One of the most powerful features that .netTiers provides to manage the integrity of your data is the Entity Rule Engine. This implements IDataErrorInfo in the EntityBase.generated.cs class. It provides the framework for managing entity business rules and custom error information that a user interface can bind to. Controls such as the DataGridView automatically detect this interface and provide error icons along with descriptions about the error.
There are several properties that assist you in managing your business rules. There are several built in validators ready to use out of the box.
- NotNull- Determines the if the database column accepts null values.
- StringMaxLength- Compares against the column width for the property.
- StringRequired- Determines if the column allows empty string entries.
- MaxWords- Determines whether the property has exceeted the maximum number of words
- RegexIsMatch- Determines whether current property matches the regular expression
- LessThanOrEqualToValue- Less than or equal to current value of property
- LessThanValue- - Less than current value of property
- EqualsValue- Equals current value of property
- GreaterThanValue- Greater than current value of property
- GreaterThanOrEqualToValue- Greater than or equal to current value of property
- CompareValues - Compares values of T using a comparer
- InRange- Ensures T is within a min and max of a Range using a Comparer.
However, we certainly understand that while those are all very useful, they do not cover the spectrum of potential business rules. The most important aspect of the rule engine is that all of the Validators are validated by a delegate which is called ValidationRuleHandler, through which you can set up using any ValidationRule, so long as it returns a bool value.
The target parameter is the object being validated, while e is a ValidationRuleArgs object that contains information about the rule (property to validate, error description). There are a couple of more properties that are of interest. There is now a property called IsValid that checks the rules to see if any have been broken. You can also get all of the broken rules through the BrokenRulesList property in your entity.
Validation/ValidationRuleHandler.cs
/// <summary>
/// Delegate providing method sig that will process validation rules.
/// </summary>
/// <remarks>
/// <para>
/// The method handler should set the Description attribute of the
/// <see cref="ValidationRuleArgs"/> parameter so that a meaningful
/// error is returned.
/// </para><para>
/// If the data is valid, the method must return true. If invalid,
/// the Description should be set the false should be returned.
/// </para>
/// </remarks>
public delegate bool ValidationRuleHandler(object target, ValidationRuleArgs e);
.netTiers will automatically detect the rules that apply for the database to maintain data integrity. However it's still extremely easy for you to add your own. Here's an example of automatically added rules for the Customer Entity we were working with.
protected override void AddValidationRules()
{
//Validation rules based on database schema.
ValidationRules.AddRule(Validation.CommonRules.NotNull,"CustomerID");
ValidationRules.AddRule(Validation.CommonRules.StringMaxLength,
new Validation.CommonRules.MaxLengthRuleArgs("CustomerID",5));
ValidationRules.AddRule(Validation.CommonRules.NotNull,"CompanyName");
ValidationRules.AddRule(Validation.CommonRules.StringMaxLength,
new Validation.CommonRules.MaxLengthRuleArgs("CompanyName",40));
ValidationRules.AddRule(Validation.CommonRules.StringMaxLength,
new Validation.CommonRules.MaxLengthRuleArgs("ContactName",30));
ValidationRules.AddRule(Validation.CommonRules.StringMaxLength,
new Validation.CommonRules.MaxLengthRuleArgs("ContactTitle",30));
ValidationRules.AddRule(Validation.CommonRules.StringMaxLength,
new Validation.CommonRules.MaxLengthRuleArgs("Address",60));
ValidationRules.AddRule(Validation.CommonRules.StringMaxLength,
new Validation.CommonRules.MaxLengthRuleArgs("City",15));
ValidationRules.AddRule(Validation.CommonRules.StringMaxLength,
new Validation.CommonRules.MaxLengthRuleArgs("Region",15));
ValidationRules.AddRule(Validation.CommonRules.StringMaxLength,
new Validation.CommonRules.MaxLengthRuleArgs("PostalCode",10));
ValidationRules.AddRule(Validation.CommonRules.StringMaxLength,
new Validation.CommonRules.MaxLengthRuleArgs("Country",15));
ValidationRules.AddRule(Validation.CommonRules.StringMaxLength,
new Validation.CommonRules.MaxLengthRuleArgs("Phone",24));
ValidationRules.AddRule(Validation.CommonRules.StringMaxLength,
new Validation.CommonRules.MaxLengthRuleArgs("Fax",24));
}
Adding your own rules is easy. There are really several ways to do it since the properties are exposed. The easiest is perhaps to simply override the AddRules() method in your entities and add your own rules along with ones that were generated for you. Here's an example of the Orders entity from the Northwind generation.
Orders.cs -> The User customizable file for your entity class.
/// <summary>
/// Adds custom validation rules to this object.
/// </summary>
protected override void AddValidationRules()
{
base.AddValidationRules();
//Add custom validation rules
ValidationRules.AddRule(Validation.CommonRules.StringRequired, "CustomerID");
ValidationRules.AddRule(Validation.CommonRules.GreaterThanOrEqualToValue<Decimal?>, new Validation.CommonRules.CompareValueRuleArgs<Decimal?> ("Freight", 0));
ValidationRules.AddRule(
Validation.CommonRules.LessThanOrEqualToValue<Decimal?>,
new Validation.CommonRules.CompareValueRuleArgs<Decimal?>(
"Freight", 200));
ValidationRules.AddRule(ValidateOrderDate, "OrderDate");
}
/// <summary>
/// Validates the order date.
/// </summary>
/// <param name="target">The target.</param>
/// <param name="e">The e.</param>
/// <returns></returns>
private bool ValidateOrderDate(object target, Validation.ValidationRuleArgs e)
{
if ((this.OrderDate ?? DateTime.MinValue) > DateTime.Today)
{
e.Description = "The Order Date must not be in the future.";
return false;
}
return true;
}
The easiest way to validate is when using a single entity is just to call the IsValid property. It will automatically trigger the validation process. You can also force the validation process by calling Validate();
Orders o = new Orders();
o.OrderDate = new DateTime(2001, 8, 29);
o.ShipAddress = "302 West Main Street";
o.ShipCity = "Atlantis";
o.ShipCountry = "Anywhere";
o.ShipName = "Frank Sanders";
o.ShipPostalCode = "55512";
o.ShipRegion = "Under the Sea";
o.Validate();
///Error property is a newline delimeted list of your broken rules.
if (!o.IsValid)
lblMessage.Text = o.Error;
///you can actually access all of the rules that were broken on the entity.
foreach(BrokenRule rule in o.BrokenRulesList)
{
lblMessage.Text = string.Format("<li>{0} - {1}</li>",
rule.Property, rule.Description);
}
/// Suppose we have a partner vendor that
/// processes orders for us, and we need
/// to validate all of today's orders.
/// Get today's orders and validate them.
TList<Orders> ordersList = GetOrdersFromVender();
if (!ordersList.IsValid)
{
StringBuilder sb = new StringBuilder();
sb.Append("<li>");
foreach(Orders o in ordersList.InvalidItems)
{
sb.Append(o.Error.Replace("\n", "<li>"));
}
lblMessage.Text = sb.ToString();
}
Overall, the important thing to remember is that since the Rules engine uses delegates, your logic can live any place you would like it to. There will be more coverage with complex business processes and validation in the Component Layer chapter.
.netTiers has two generic lists that it exclusively uses for your entities. TList and VList. The TList is the most full featured and only works with Types that implement IEntity, which are entities that are generated from a table as formerly stated. VList is a list for limited View Entities that don't maintain EntityState.
Orders order = new Orders();
order.OrderDate = new DateTime(2001, 8, 29);
order.ShipAddress = "302 West Main Street";
order.ShipCity = "Atlantis";
order.ShipCountry = "Anywhere";
order.ShipName = "Frank Sanders";
order.ShipPostalCode = "55512";
order.ShipRegion = "Under the Sea";
///Index Of, Get's the location of the order in the list
int index = ordersList.IndexOf(order);
///FindIndex
///Returns the integer value if
///one of the criteria matches your predicate
ordersList.FindIndex(
delegate(Orders orders)
{
return orders.RequiredDate < DateTime.Today.AddDays(2);
});
///Insert
///Useful if you want to insert an order in a specific location
if (index < 0)
ordersList.Insert(0, order);
///Add
///Appends an order to the list
ordersList.Add(order);
///AddNew
///Appends a new order to the list
ordersList.AddNew();
ordersList[ordersList.Count - 1].OrderDate = DateTime.Today;
///RemoveEntity
///Removes the order from the list and places it in DeletedItems
ordersList.RemoveEntity(order);
///RemoveAt
///Removes the first entry of the list
ordersList.RemoveAt(0);
///RemoveAt
///Removes the entity where it exists
ordersList.Remove(order);
///ListChanged
///Fires an event when the list has changed
ordersList.ListChanged +=
new System.ComponentModel.ListChangedEventHandler(ordersList_ListChanged);
///IsDeletedCount
///Returns the count of the DeletedItems Collection
int deletedCount = ordersList.IsDeletedCount;
Debug.Assert(deletedCount == ordersList.DeletedItems.Count);
///IsDirtyCount
///Returns the count of the items that have an
///EntityState == EntityState.Changed
int dirtyCount = ordersList.IsDirtyCount;
Response.Write(string.Format("You have modified {0} entities.",dirtyCount));
///IsNewCount
///Returns the count of the items that have an
///EntityState == EntityState.Added
int newCount = ordersList.IsNewCount;
Response.Write(string.Format("You have added {0} entities.", newCount));
///FindAllBy
///Returns a new List of Entities that
/// match the FindAllByType
///FindAllByType.Contains, FindAllBy.StartsWith, FindAllByType.EndsWith
TList<Orders> sList = ordersList.FindAllBy(TList<Orders>.FindAllByType.StartsWith,
OrdersColumn.ShipCity, "Atl");
TList<Orders> cList = ordersList.FindAllBy(TList<Orders>.FindAllByType.Contains,
OrdersColumn.ShipCity, "ant");
TList<Orders> eList =ordersList.FindAllBy(TList<Orders>.FindAllByType.EndsWith,
OrdersColumn.ShipCity, "tis");
///FindAll
///Returns a new List of Entities that match using the Table Enum Columns
TList<Orders> eqList =
ordersList.FindAll(OrdersColumn.ShipCity, "Atlantis");
///FindAll
///Returns a new List of Entities using a predicate
TList<Orders> eqList2 = ordersList.FindAll(
delegate(Orders o2){
return
o2.OrderDetailsCollection.Count > 0 &&
o2.OrderDate == DateTime.Today;
});
///Exists
///Returns a bool if one of the criteria matches your predicate
if (ordersList.Exists(
delegate(Orders o3)
{
return
o3.OrderDetailsCollection.Count > 0 &&
o3.OrderDate == DateTime.Today;
}))
{
Response.Write("There are orders today");
}
///ToArray
///Creates an orders array from a list
Orders[] orderArray = ordersList.ToArray();
///ToDataSet
///Creates a DataSet with children relationships from your TList.
DataSet ds = ordersList.ToDataSet(true);
/// Filter as a string
/// Creates a view inside of your list using a case sensitive filter
/// Great for cached items that you need to show
/// different sets of entities based on criteria.
ordersList.Filter = "ShipCity = 'Atlantis'";
//ApplyFilter is automatically called on the set of the Filter property
//ordersList.ApplyFilter();
ordersList.ForEach(
delegate(Orders filteredOrder)
{
Debug.Assert(filteredOrder.ShipCity == "Atlantis");
});
///To Remove the filter, you simply call ResetFilter;
ordersList.RemoveFilter();
/// Filter using a Predicate delegate
/// Great for needing to filter on items that
/// different sets of entities based on criteria.
ordersList.ApplyFilter(GetValidAtlantisOrders);
ordersList.ForEach(
delegate(Orders filteredOrder)
{
Debug.Assert(filteredOrder.IsValid
&& filteredOrder.ShipCity == "Atlantis");
});
}//End Page_Load
/// <summary>
/// Gets the valid atlantis orders.
/// </summary>
/// <param name="o">The o.</param>
/// <returns></returns>
public bool GetValidAtlantisOrders(Orders o)
{
return (o.IsValid
&& o.ShipCity == "Atlantis"
&& o.OrderDetailsCollection.Count > 0
&& o.OrderDetailsCollection.IsValid);
}
void ordersList_ListChanged(object sender, System.ComponentModel.ListChangedEventArgs e)
{
throw new Exception("The method or operation is not implemented.");
}
/// <summary>
/// Gets the orders from vendor. Stub method, does nothing.
/// </summary>
/// <returns></returns>
public TList <Orders> GetOrdersFromVendor()
{
//Get Some orders from a vendor
return new TList<Orders>();
}
}
In any application there are often several aspects of your application that you commonly have to do in order to optimize your application.
The EntityFactoryBase is a creational construct that exists to assist the creation of entities of an unknown type at runtime. Entities are/were normally created in the DataRepository in a given EntityProvider's Fill method. So if I had DataRepository.MyEntityProvider, there would be a method called Fill that took an IDataReader, an TList (EntityCollection) and a row params. This method is actually going to hydrate the entities when coming back from the DataRepository.
This version of the templates allow you to create component business objects which inherit from the entity objects. Since these component objects live in the tier on top of the Data Access Layer and Entities. Since the DataRepository creates entities for usage, it's not possible to create those types because the DAL doesn't know about the smart component business objects, only the Entity DTO objects. The role the EntityFactory is used for is you have the ability to define which factory will be used to create your objects to be filled in the app/web.config.
Example:
entityFactoryType="Northwind.Entities.EntityFactory";
// OR
entityFactoryType="Northwind.Entities.ComponentEntityFactory";
Each factory will use the namespace of the factory to create unknown types at runtime using an Activator. This type discovery is cached and so you only face a perf hit once.
There are events that you can subscribe to during this process to inject some logic before the object is created and just after it's created but before being hydrated. This is useful if you wanted to attach your own events to the entity.
The EntityCache class manages the lifetime of entities that you would like to not have to be queried for all the time. In reality, this class simply wraps the Enterprise Library Cache. EntLib offers a full featured and configurable object cache. The entity cache can be used by any object, and does not have to be an entity. More info can be found here: [http://msdn2.microsoft.com/En-US/library/aa480456.aspx|Enterprise Library Caching Block]
You don't "Have" to configure the cache to work, .netTiers will generate a default cache configuration at runtime if one does not exist, but it's recommended that you do create a configuration for your caching block to optimize the cache settings for your application. The entity cache can easily be configured by pointing your Enterprise Library Configurator tool at your app/web.config. We've also recently started including a sample default settings entlib.config in the MyNamespace.UnitTest project.
When this marker interface is applied to an entity, the entity will automatically be placed into cache. The interface provides lifetime and callback parameters used to manage the caching of your entities. This is an interface that you would have to apply yourself to the entity at the concrete class level.
The EntityLocator sits on top of the Locator class of the Microsoft Patterns and Practices Group's ObjectBuilder Framework. The locator is responsible for creating a WeakReference'd Object Store so that as your application is handling a high volume of entities, which many are of the same record, we will return to you the same object for all references until that entity is persisted to the DataRepository. Combining this feature with Optimistic Concurrency with a Timestamp on your Table, you'll end up saving quite a bit on memory consumption. And for those that might not know, a weak reference means that the objects will still be garbage collected as soon as there are no more actual references to that entity any longer.
This feature has to be enabled in the app/web.config under enableEntityTracking="true/false"
The EntityManager is the glue that holds it all together, and should be considered the entry point to most of these features, with the exception of the EntityCache. When an object is about to be created, and DataRepository.Provider.EntityTracking is enabled, the EntityManager get's called. The EntityManager contains a single EntityLocator object and a collection of EntityCache objects, for all of your different entity cache providers. Most likely though, there will only be a single EntityCache object in there.
The EntityManager contains a LocateOrCreate method that determine if an entity already exists and is currently being referenced, if so, then return that entity, otherwise, create a new one using the entity factory defined and begin tracking the entity.
There are several yet to be implemented features which we have in mind for the EntityManager such as managing meta data amongst the entities and being able to determine relationship boundaries at runtime.