Saturday, July 08, 2017
Type Erasure
Say that I have a view model that has a reference to a data source object generated by another object like a factory. In my view model I would have a property that is typed as AnyDataSource. I also have a property that is of type FRCDataSource, this is the object that will be wrapped in the AnyDataSource object.
class AddToDoViewModel {
var dataSource : AnyDataSource<Item>!
var frcDataSource : FRCDataSource<Item>!
FRCDataSource is defined as :
class FRCDataSource<T: NSManagedObject> : DataSourceProtocol {
var fetchedResultsController : NSFetchedResultsController<T>!
init(fetchedResultsController: NSFetchedResultsController<T>) {
self.fetchedResultsController = fetchedResultsController
}
}
The FRCDataSource class is a wrapper around an NSFetchedResultsController type. The generic type constraint on FRCDataSource limits the type of T to be an object that derives from NSManagedObject. Also note that FRCDataSource is specialized by the same type as NSFetchedResultsController. To create an FRCDataSource we do the following in reloadData()
func reloadData() {
self.frcDataSource = FRCDataSource(fetchedResultsController:self.fetchedResultsController)
}
We create an FRCDataSource, passing in the NSFetchedResultsController class property. Now our frcDataSource property is set to a FRCDataSource instance that internally stores a NSFetchedResultsController instance. In this case, I am creating an instance of FRCDataSource directly, although I could easily delegate this responsibility to another object which would create and return an FRCDataSource.
Once I’ve created an FRCDataSource object from the NSFetchedResultsController, I can now add a method that will create an AnyDataSource derived object and store it in the dataSource property of the view model:
func createDataSource<T: DataSourceProtocol>(dataSource: T) where T.DataSourceItem == Item
{
self.dataSource = AnyDataSource(dataSource)
}
The createDataSource() takes an object that adopts the DataSourceProtocol, creates an AnyDataSource object and sets the dataSource property. There are a few things to take note of here:
- The createDataSource method is a generic method and is specialized by an object that must adopt DataSourceProtocol. The input parameter dataSource is of the specialized type.
A generic constraint is specified which constrains the DataSourceItem associated type of the input parameter to be of type Item. This is done to make sure that the type of the dataSource property matches the type returned by the AnyDataSource(dataSource) init call.
Now we just have to pass in the frcDataSource property to the createDataSource() method.createDataSource(dataSource: self.frcDataSource)
Once the data source is created we will have an AnyDataSource typed object that can be used in place of a DataSourceProtocol type.
Basically what we’ve done is create wrappers for two types
Thursday, December 20, 2007
Derived classes and events
public event EventHandlerNotifyClient;
Here, I used the generic event template to eliminate the need to define a separate delegate. To register a callback method with the event, we do the following in the class that will receive the event notification from the class that raised the event:
message.NotifyClient += new EventHandler(message_NotifyClient);
static void message_NotifyClient(object sender, EventArgs e)
{
throw new Exception("The method or operation is not implemented.");
}
When the event is raised the message_NotifyClient method will execute. To raise the event we add the following to the class that raises the event:
protected virtual void OnNotifyClient(EventArgs e)
{
EventHandlerhandler = NotifyClient;
if (handler != null)
handler(this, e);
}
Here, we make a copy of the event since access to the event is not thread-safe (i.e. another thread could delete/modify the event before the if (handler != null) completes). When the client wants to raise the event, it does so by executing the OnNotifyClient method:
private void DoSomething()
{
// Do something...
OnNotifyClient(new EventArgs());
}
When OnNotifyClient is executed, the message_NotifyClient method is executed.
Derived classes and events
An event can only be raised from methods declare within the class that declared the event. So events declared in class A can only be raised from methods within class A. By default, events are not inherited by derived classes, so in the following base class definition:
public class Base
{
public event EventHanderNotifyClient;
}
with the derived class definition
public class Derived : Base
{
private void DoSomething()
{
EventHandlerhandler = NotifyClient;
if (handler != null)
handler(this, new EventArgs());
}
}
The derived class will not have access to the NotifyClient event since the NotifyClient event was not declared in the Derived class. There are two ways to workaround this requirement:
- Define the OnNotifyClient method in the Base class as virtual and then override the OnNotifyClient method in the Derived class
- Declare the event as virtual in the Base class, then override the event in the Derived class
protected override void OnNotifyClient(EventArgs e)
{
EventHandlerhandler = NotifyClient;
if (handler != null)
handler(this, e);
}
protected void DoSomething()
{
// Do something Derived-class specific, then call OnNotifyClient
OnNotifyClient(new EventArgs());
}
The second approach is to declare the event virtual in the Base class, then override the event in the Derived class:
public class Base
{
public virtual event EventHandlerNotifyClient;
...
}
public class Derived : Base
{
public override event EventHandlerNotifyClient;
public void DoSomething()
{
EventHandlerhandler = NotifyClient;
// Here, we can access the NotifyClient directly since we've overridden access
if (handler != null)
handler(this, new EventArgs());
}
}
Which approach you take is up to you. Both approaches give the Derived class the flexibility to perform Derived-specific tasks around the decision to raise the event. It really comes down to which approach is preferred.
Wednesday, November 28, 2007
public override string ToString()
{
StringBuilder sb = new StringBuilder();
Type t = this.GetType();
PropertyInfo[] pInfo = t.GetProperties();
sb.Append(t.ToString() + "\r\n");
foreach (PropertyInfo p in pInfo)
{
string fmt = string.Format("{0} = {1}", p.Name, p.GetValue(this, null));
sb.Append(fmt + "\r\n");
}
return sb.ToString();
}
The function uses reflection to get the public properties of the class and it iterates over the collection of properties and builds a string that represents the state of the class. The string returned from ToString() would look like this:
Property1 = Value1
Property2 = Value2
.
.
.
The PropertyInfo.GetValue() function takes two parameters; the first parameter is the reference to this, while the second parameter is an object array that is used when the PropertyInfo references an indexed property. When using reflection to enumerate properties on a class that contains other classes, you must accomodate the case where a class might contain an indexed property:
public class Details
{
public EDIProcess.Model.EstimateDetailType this[int index]
{
get { return (EDIProcess.Model.EstimateDetailType) DetailCollection[index]; }
}
}
In this case, the Details class contains an indexer that is used to retrieve EstimateDetailType objects by index from the DetailCollection property. You can use the GetValue() function and pass in an object array that contains a single entry that is the value of the offset into the collection, which will reference the indexed item at that position within the collection.
public class Details
{
public override string ToString()
{
StringBuilder sb = new StringBuilder();
try
{
Type t = this.GetType();
string fmt;
PropertyInfo[] pInfo = t.GetProperties();
sb.Append(t.ToString() + "\r\n");
foreach (PropertyInfo p in pInfo)
{
if (p.Name == "Item")
{
for (int i = 0; i < Count; i++)
{
fmt = string.Format("{0} = {1}", p.Name, p.GetValue(this, new object[] { i }));
sb.Append(fmt + "\r\n");
}
}
else
{
fmt = string.Format("{0} = {1}", p.Name, p.GetValue(this, null));
sb.Append(fmt + "\r\n");
}
}
}
catch (Exception ex)
{
log.Error(ex.Message);
}
return sb.ToString();
}
}
The items in bold represent the changes to support the indexed properties. The Details class contains a DetailCollection class (which derives from ArrayList), and the Details class contains an indexer to retrieve an item from the collection as well as a property that returns the number of items stored in the collection. The modification looks for a property named Item in the Details class, and it then retrieves the count of objects in the collection and for each object in the collection it calls the GetValue(), passing in a reference to the Details class (this) and a new object[] array that contains a single member that stores the integer value of the item within the collection.
Friday, September 21, 2007
Threading in C#
Friday, November 03, 2006
CREATE TABLE [dbo].[TEST_TABLE] (
[Data] [varchar] (5) COLLATE Latin1_General_BIN NOT NULL
)
and this table contains a row that contains:
Data
=====
ABCDE
Now, if we have a stored procedure that updates the row called UpdateData:
CREATE PROCEDURE UpdateData
@Data varchar(3) = null
AS
Begin Tran
update TEST_TABLE
set Data = ISNULL(@Data, Data)
IF @@ERROR <> 0
Rollback Tran
else
Commit Tran
The stored procedure sets the @Data variable to null by defualt, otherwise it will use the value passed to it via the @Data variable. The update statement uses the ISNULL function to check if the @Data variable is the default value of null and if it is, then it should use the current value in the Data column on the table. If we call this stored procedure, without passing in the @Data parameter, the stored procedure will set the value of the @Data parameter to null, and this is what we'll get:
Data
=====
ABC
What happened, why did my data get truncated? Think about how the ISNULL function works, it sets the variable specified in the first parameter to the value of the variable specified in the second parameter if the first parameter is null. So, it should set the @Data variable to the value in the Data column. Also note that the stored procedure is implicitly creating the @Data variable in the procedure declaration and also note that the @Data variable has a width of 3 varchars instead of the 5 varchars in the TEMP_TABLE Data column declaration. Essentially what happens is that the ISNULL function sees that the @Data variable is null, reads the Data column from the table and then copies the value from the Data column into the @Data variable. Since the @Data variable is only 3 varchars wide, only 3 varchars are copied to it. The set clause then updates the Data column with these 3 varchars from the @Data variable. That's why the data gets truncated. So, the moral of the story is to make sure of the following:
- Your table column widths and your stored procedure widths match
- You pass the right amount of data from your code to your stored procedure
Wednesday, October 04, 2006
SQL Server pseudo nested transactions
SP1:
create procedure sp1
as
begin tran
exec sp2
if (@@error <> 0)
goto on_error
commit tran
on_error:
rollback tran
SP1:
create procedure sp2
as
begin tran
insert table values ('sp2')
if (@@error <> 0)
goto on_error
commit tran
on_error:
rollback tran
The rules for nested begin transactions is:
- Any begin tran statements are ignored, but each begin tran statement does update the @@trancount variable.
- Any commit statements executed after the first commit transaction statement are ignored. The transaction is committed only when the outermost commit statement is executed. The @@trancount variable is decremented by one for each commit statement executed.
- Any rollback statements executed will rollback the entire transaction and set the @@trancount variable to 0.
When using the ADO.NET SqlTransaction object you need to be aware of how transactions are committed and rolled back. When the SqlTransaction.BeginTransaction method is executed, it implicitly executes the BEGIN TRAN SQL statement. Any SQL statements executed will then execute on this main transaction. If the SQL statements that are executed perform a commit or rollback, the main transaction is affected based on the rules above. There may be certain cases where a servere SQL error will cause the main transaction to rollback, and if the main transaction is created by the SqlTransaction.BeginTransaction method, when the exception is caught, you will need to check if the transacion was actually already rolled back before executing the SqlTransaction.Rollback method:
try
{
try
{
SqlTransaction txn = new SqlTransaction();
txn.BeginTransaction();
Data.CallSproc1();
Data.CallSproc2(); // throws an exception and rolls back the transaction
txn.CommitTransaction();
}
catch(SqlException ex)
{
txn.RollbackTransaction(); // If the transaction is already rolled back, this will throw an exception
}
}
catch(SqlException ex)
{
// Transaction was already rolled back, do something
}
}