HiveBrain v1.2.0
Get Started
← Back to all entries
patterncsharpMinor

Generic Constraints and new()

Submitted by: @import:stackexchange-codereview··
0
Viewed 0 times
andgenericconstraintsnew

Problem

I have have attempted to use generics and constraints to create functions that retrieve and save data from a number of similar objects. Each of these objects describe their own properties. When I use these methods for a particular Type I don't understand why I have to specify new() in the constraint and also create a new version of the type in order to access its underlying properties to assist me. I'm sure there is a better way, but my knowledge in this area is a little lacking.

There are a number of objects that inherit from RawDataAggregatedItem.

public class Project : RawDataAggregatedItem
{
    public override string IdColumnName
    {
        get { return "project_id"; }
    }

    public override string DescriptionColumnName
    {
        get { return "project_description"; }
    }

    public override string GetStoredProcedureName
    {
        get { return "R_projects_sp"; }
    }
}


...

public class Category : RawDataAggregatedItem
{
    public override string IdColumnName
    {
        get { return "asset_category"; }
    }

    public override string GetStoredProcedureName
    {
        get { return "R_asset_category_sp"; }
    }
}


RawDataAggregatedItem has it's own interface:

public interface IRawDataAggregatedItem
{
    string IdColumnName { get; }
    string DescriptionColumnName { get; }
}

public abstract class RawDataAggregatedItem : IRawDataAggregatedItem
{

    public abstract string IdColumnName { get; }
    public virtual string DescriptionColumnName
    {
        get { return string.Empty; }
    }
    public abstract string GetStoredProcedureName { get; }
}


I've created functions that get and save data to the database using generics expecting these different object types:

```
public RawDataAggregatedItems GetRawDataAggregated(int? id) where T : IRawDataAggregatedItem, new()
{
try
{
var itemType = new T();
using (var cn = Connection())
{
var c

Solution

Well GetStoredProcedureName is an instance property - you need an instance of the object to get the value of the property. The value belongs to an instance, not to the type. You either need to use static properties and lose the
generics or introduce a configuration source that maps type to a configuration object.

I'd sugges that the object has no need to know what its stored procedures are called so the latter suggestion seems to be the way to go.

Create a configuration class:

public class DataConfiguration
{
    public string GetStoredProcedureName { get; set; }
} 

public class DataConfigurationProvider
{
    public DataConfiguration GetConfiguration()
    {
        // some stuff.
    }
}


Then you can update your functions:

public RawDataAggregatedItems GetRawDataAggregated(int? id) where T : IRawDataAggregatedItem
{
    // _dataConfigurationProvider injected by constructor injection.
    var sprocName = _dataConfigurationProvider.GetConfiguration().GetStoredProcedureName;
    try
    {
        using (var cn = Connection())
        {
            var cmd = new SqlCommand(sprocName, cn) { CommandTimeout = _commandTimeout, CommandType = CommandType.StoredProcedure };
            cmd.Parameters.Add(new SqlParameter("@periodId", SqlDbType.Int)).Value = id;
            return new RawDataAggregatedItems(cmd.ExecuteReader());
        }
    }
    catch (Exception ex)
    {
        throw new Exception("Error fetching aggregated rawdata list.", ex);
    }
}


I've been a bit wooly on the specifics here but hopefully you can see yourself to a working solution!

Code Snippets

public class DataConfiguration<T>
{
    public string GetStoredProcedureName { get; set; }
} 

public class DataConfigurationProvider
{
    public DataConfiguration<T> GetConfiguration<T>()
    {
        // some stuff.
    }
}
public RawDataAggregatedItems<T> GetRawDataAggregated<T>(int? id) where T : IRawDataAggregatedItem
{
    // _dataConfigurationProvider injected by constructor injection.
    var sprocName = _dataConfigurationProvider.GetConfiguration<T>().GetStoredProcedureName;
    try
    {
        using (var cn = Connection())
        {
            var cmd = new SqlCommand(sprocName, cn) { CommandTimeout = _commandTimeout, CommandType = CommandType.StoredProcedure };
            cmd.Parameters.Add(new SqlParameter("@periodId", SqlDbType.Int)).Value = id;
            return new RawDataAggregatedItems<T>(cmd.ExecuteReader());
        }
    }
    catch (Exception ex)
    {
        throw new Exception("Error fetching aggregated rawdata list.", ex);
    }
}

Context

StackExchange Code Review Q#94754, answer score: 5

Revisions (0)

No revisions yet.