Você está na página 1de 7

Using the entity classes

Using the primary key value One way to instantiate the entity in an object is by passing all primary key values to the constructor of the entity class to use:
DataAccessAdapter adapter = new DataAccessAdapter(); CustomerEntity customer = new CustomerEntity("CHOPS");

adapter.FetchEntity(customer);
Using a related entity Another way to instantiate this same entity is via a related entity. The example uses the KeepConnectionOpen feature by passing true to the constructor of the DataAccessAdapter object. The example explicitly closes the connection after the DataAccessAdapter usage is finished.
DataAccessAdapter adapter = new DataAccessAdapter(true); OrderEntity order = new OrderEntity(10254); adapter.FetchEntity(order); order.Customer = (CustomerEntity)adapter.FetchNewEntity(new CustomerEntityFactory(), order.GetRelationInfoCustomer());

adapter.CloseConnection(); Using a unique constraint's value


DataAccessAdapter adapter = new DataAccessAdapter(); CustomerEntity customer = new CustomerEntity(); customer.CompanyName = "Chop-suey Chinese"; adapter.FetchEntityUsingUniqueConstraint(customer, customer.ConstructFilterForUCCompanyName());

Using a Context object


CustomerEntity customer = (CustomerEntity)myContext.Get(new CustomerEntityFactory(), "CHOPS"); if(customer.IsNew) { // not found in context, fetch from database (assumes 'adapter' is a DataAccessAdapter instance) adapter.FetchEntity(customer);

}
Polymorphic fetches Imagine the following entity setup: BoardMember entity has a relation (m:1) with CompanyCar. CompanyCar is the root of a TargetPerEntityHierarchy inheritance hierarchy and has two subtypes: FamilyCar and SportsCar. Because BoardMember has the relation with CompanyCar, a field called 'CompanyCar' is created in the BoardMember entity which is mapped onto the m:1 relation BoardMember - CompanyCar.

CompanyCarEntity car = adapter.FetchNewEntity(new CompanyCarEntityFactory(),

myBoardMember.GetRelationInfoCompanyCar());
However, 'car' in the example above, can be of a different type. If for example the BoardMember instance in myBoardMember has a FamilyCar as company car set, 'car' is of type FamilyCar. Because the fetch action can result in multiple types, the fetch is called polymorphic. So, in our example, if 'car' is of type FamilyCar, the following code would also be correct:

FamilyCarEntity car = (FamilyCarEntity)adapter.FetchNewEntity(new CompanyCarEntityFactory(),

myBoardMember.GetRelationInfoCompanyCar());
Creating a new / modifying an existing entity

CustomerEntity customer = new CustomerEntity();


customer.CustomerID = "FOO"; customer.Address = "1, Bar drive"; customer.City = "Silicon Valey"; customer.CompanyName = "Foo Inc."; customer.ContactName = "John Coder"; customer.ContactTitle = "Owner"; customer.Country = "USA"; customer.Fax = "(604)555-1233"; customer.Phone_Number = "(604)555-1234"; customer.PostalCode = "90211"; // save it. We require an adapter for this DataAccessAdapter adapter = new DataAccessAdapter();

adapter.SaveEntity(customer, true);
Modifying an entity

1. Loading an existing entity in memory, alter one or more fields (not sequenced fields) and call a DataAccessAdapter object's SaveEntity() method
CustomerEntity customer = new CustomerEntity("CHOPS"); DataAccessAdapter adapter = new DataAccessAdapter(true); adapter.FetchEntity(customer); customer.Phone = "(605)555-4321"; adapter.SaveEntity(customer);

adapter.CloseConnection() 2. Create a new entity, set the primary key values (used for filtering), set the IsNew to false, set one or more other fields' values and call a DataAccessAdapter object's SaveEntity() method. This will not alter the PK fields.
CustomerEntity customer = new CustomerEntity(); customer.CustomerID="CHOPS";

customer.IsNew=false; customer.Phone = "(605)555-4321"; DataAccessAdapter adapter = new DataAccessAdapter();

adapter.SaveEntity(customer);

3. Via the DataAccessAdapter's UpdateEntitiesDirectly() method, specifying the primary key fields as the filter.
RelationPredicateBucket bucket = new RelationPredicateBucket(); bucket.PredicateExpression.Add(ProductFields.CategoryId == 3); ProductEntity updateValuesProduct = new ProductEntity(); updateValuesProduct.Discontinued=true; DataAccessAdapter adapter = new DataAccessAdapter();

int amountUpdated = adapter.UpdateEntitiesDirectly(updateValuesProduct, bucket);


Setting the EntityState to Fetched automatically after a save By design an entity which was successfully saved to the database gets as EntityState OutOfSync. To use this feature, you've to set the static/Shared property EntityBase2.MarkSavedEntitiesAsFetched to true (default is false). This will be used for all entities in your application, so if you have some entities which have to be fetched after the update (for example because they have a timestamp field), you should keep the default, false. <add key="markSavedEntitiesAsFetched" value="true"/> FK-PK synchronization If you set a foreign key field (for example Order.CustomerID) to a new value, the referenced entity by the foreign key (relation) the field is part of will be dereferenced and the field mapped onto that relation is set to null (C#) or Nothing
OrderEntity myOrder = new OrderEntity(); CustomerEntity myCustomer = new CustomerEntity("CHOPS"); adapter.FetchEntity(myCustomer); myOrder.Customer = myCustomer; // A myOrder.CustomerID = "BLONP"; // B

CustomerEntity referencedCustomer = myOrder.Customer; // C

After line 'A', myOrder.CustomerID will be set to "CHOPS", because of the synchronization between the PK of Customer and the FK of Order. At line 'B', the foreign key field CustomerID of Order is changed to a new value, "BLONP". Because the FK field changes, the referenced entity through that FK field, Customer, is dereferenced and myOrder.Customer will return null/Nothing. Because there is no current referenced customer entity, the variable referencedCustomer will be set to null / Nothing at line 'C'.

The opposite is also true: if you set the property which represents a related entity to null (Nothing), the FK field(s) forming this relation will be set to null as well, as shown in the following example:
PrefetchPath2 path = new PrefetchPath2((int)EntityType.OrderEntity); path.Add(OrderEntity.PrefetchPathCustomer); OrderEntity myOrder = new OrderEntity(10254); adapter.FetchEntity(myOrder, path); // A

myOrder.Customer = null;

// B

At line A, the prefetch path loads the related Customer entity together with the Order entity 10254. At line B, this customer is dereferenced. This means that the FK field of order creating this relation, myOrder.CustomerId, will be set to null (Nothing). So if myOrder is saved after this, NULL will be saved in the field Order.CustomerId. Deleting an entity
DataAccessAdapter adapter = new DataAccessAdapter(true); CustomerEntity customer = new CustomerEntity("CHOPS"); adapter.DeleteEntity(customer);

adapter.CloseConnection();
Entity state in distributed systems In distributed environments, you work disconnected: the client holds data and doesn't have a connection with the server for manipulating the data in the client process, it only contacts the service for persistence and data retrieval from the database. To understand the state of an entity object the following explanation could help. Think in these steps: 1. 2. 3. 4. 5. 6. Create containers (entity objects) Add data to/load data in containers (from server for example) Show data in modifiers (forms) Data is modified and collected for persistence Collected data is send to server for persistence Process is ended

After step 6) the state should be considered void. It's up to you to ignore that and keep data around on the client. But as you work disconnected, there is no feedback from the server, so for example if you send an entity from client to server and it is saved there: you won't all of a sudden have an outofsync entity on the client, as that's just a copy of the object on the server. So if you want to keep on working on the client with the data, you have to consider that after step 6) you have to rewind to 1) or 2), unless you know what you can keep (read-only data for example). If you're in 6) and you rewind to 4), you're modifying data which is out of sync with the server. LLBLGen Pro doesn't provide you with a layer which takes care of that, as you should control that yourself, because only then the developer has full control over when what happens. So when you send a UnitOfWork2 object to the server, you have to realize you're in 5) moving to 6) and it's all over for that process. If that's not the case, then you shouldn't move from 4) to 5) there, but wait and persist the data later.

Concurrency control
private class OrderConcurrencyFilterFactory : IConcurrencyPredicateFactory { public IPredicateExpression CreatePredicate( ConcurrencyPredicateType predicateTypeToCreate, object containingEntity) { IPredicateExpression toReturn = new PredicateExpression(); OrderEntity order = (OrderEntity)containingEntity; switch(predicateTypeToCreate) { case ConcurrencyPredicateType.Delete: toReturn.Add(OrderFields.EmployeeID == order.Fields[(int)OrderFieldIndex.EmployeeID].DbValue); break; case ConcurrencyPredicateType.Save: // only for updates toReturn.Add(OrderFields.EmployeeID == order.Fields[(int)OrderFieldIndex.EmployeeID].DbValue); break; } return toReturn; }

}
Adapter contains an advanced concurrency mechanism, in such a way that you can decide how to implement concurrency control in your application. It is often better to schedule concurrency aspects at a high level in your application, however if you are required to check whether a save can take place or not, you can. As does SelfServicing, Adapter allows you to specify a predicate expression object with the SaveEntity() method. This predicate expression is included in the UPDATE query (it's ignored in an INSERT query) so you can specify exactly when a save should take place. Adapter also allows you to implement the interface IConcurrencyPredicateFactory, and instances of that interface can be inserted into entity objects. If such a factory is present inside an entity, SaveEntity() will automatically request a predicate object from that factory to include in the UPDATE query. This way you can still provide concurrency predicates during a recursive save action. Entities, NULL values and defaults
OrderEntity order = new OrderEntity(10254); DataAccessAdapter adapter = new DataAccessAdapter(); adapter.FetchEntity(order); order.ShippingDate = null;

adapter.SaveEntity(order);
To test if a field is currently representing a NULL value, or better: if the entity would be saved now, does the field become NULL in the database, you can use a different method: TestCurrentFieldValueForNull():

// [C#] CustomerEntity customer = new CustomerEntity("CHOPS"); customer.SetNewFieldValue((int)CustomerFieldIndex.ContactTitle, null); customer.TestCurrentFieldValueForNull(CustomerFieldIndex.ContactTitle); // returns true

Extending an entity by intercepting activity calls During the entity's lifecycle and the actions in which the entity participates, various methods of the entity are called, and which might be a good candidate for your own logic to be called as well, for example when the entity is initialized you might want to do your own initialization as well. The entity classes offer a variety of methods for you to override so you can make your code to be called in various situations. These methods start all with On and can be found in the LLBLGen Pro reference manual in the class EntityBase2. The entity classes also offer events for some situations, like the Initializing and Initialized events. If you want to perform a given action when one of these methods are called, you can override them in the generated entity classes, preferably using the methods discussed in Adding your own code to the generated classes. IDataErrorInfo implementation The .NET interface IDataErrorInfo is now implemented on EntityBase. Two methods have been added to the entities: SetEntityError and SetEntityFieldError, which allows external code to set the error of a field and/or entity. If append is set to true with SetEntityFieldError, the error message is appended to an existing message for that field using a semi-colon as separator. Entity field validation, which is triggered by the entity's method SetNewFieldValue() (which is called by a property setter), sets the field error if an exception occurs or when the custom field validator fails. The error message is appended to an existing message.

Using the EntityCollection classes


Adapter contains a general purpose EntityCollection class. The EntityCollection class is located in the HelperClasses namespace in the database generic project. This class is used to work on more than one entity at the same time and it is used to retrieve more than one entity of the same type from the database. This section describes the different kinds of functionality bundled in the EntityCollection class, related to collection class and how to utilize that functionality in your code. Entity retrieval into an entity collection object Using a related entity
CustomerEntity customer = new CustomerEntity("CHOPS"); DataAccessAdapter adapter = new DataAccessAdapter(); EntityCollection<OrderEntity> orders = customer.Orders;

adapter.FetchEntityCollection(orders, customer.GetRelationInfoOrders());
The entity inside 'customer' is used to construct the filter bucket created by GetRelationInfoOrders() which filters the orders in the persistent storage on the CustomerID field and value "CHOPS".

Adapter does not support lazy loading. All loading of data is by hand. This has the advantage that you can transfer an EntityCollection object to another process/tier and be certain no database connection/logic is necessary or required to work with the data inside the collection. It also ensures no extra data is available to the developer/object that you didn't supply. You can filter on more fields, including filtering on fields in different entities by adjusting the RelationPredicateBucket object. The RelationPredicateBucket object is retrieved from the GetRelationInfo*() methods. You can also construct your own if you want. The EntityCollection object to fill and which is passed to the FetchEntityCollection() method has to contain a valid IEntityFactory2 implementing object. LLBLGen Pro will generate such a factory for each entity. In the example above, customer.Orders is an EntityCollection instance created inside the customer object (and created by the constructor of CustomerEntity) and already contains the valid factory object for OrderEntity objects. If Order is in an inheritance hierarchy, the fetch is polymorphic. This means that if the customer entity, in this case customer "CHOPS", has references to instances of different derived types of Order, every instance in customer.Orders is of the type it represents, which effectively means that not every instance in Orders is of the same type. See for more information about polymorphic fetchs also Polymorphic fetches.

Você também pode gostar