Search code examples
c#linqentity-frameworkdatacontextfunction-attributes

Is it possible to have a make a generic method which can take the place of three methods with a System.Data.Linq.Mapping.FunctionAttribute?


I have three methods in my datacontext class which all call slightly different stored procedures having different behaviours; APPEND, UPDATE, OVERWRITE. However all three methods have in essence the same code. The only difference is the "Name" property of the System.Data.Linq.Mapping.FunctionAttribute which decorates the methods.

[Function(Name = "import.usp_MyData_ProcessImportAppend", IsComposable = false)]

or

[Function(Name = "import.usp_MyData_ProcessImportUpdate", IsComposable = false)]

or

[Function(Name = "import.usp_MyData_ProcessImportOverwrite", IsComposable = false)]

In essence they all look similar to this

/// <summary>
/// Processes the import.
/// </summary>
/// <param name="loadId">The load id.</param>
/// <exception cref="System.ArgumentNullException">loadId</exception>
[Function(Name = "import.usp_MyData_ProcessImportAppend", IsComposable = false)]
public Int32 ProcessGradingImport(string loadId)
{
    // Validate parameter
    if (String.IsNullOrEmpty(loadId)) throw new ArgumentNullException("loadId");

    // Initialise the result and the procedure parametes
    Int32 result = 0;
    object[] parameters = { loadId };

    // Call the procedure, and return the result
    IExecuteResult executionResult = ExecuteMethodCall(this, (MethodInfo)(MethodBase.GetCurrentMethod()), parameters);
    if (executionResult != null) result = (int)executionResult.ReturnValue;
    return result;
}

Is there any way I can write a generic method where the function name to map to is passed in? Thank you.

UPDATE So based upon the excellent solutions by Ron and Alex, I now have

/// <summary>
/// Process imports.
/// </summary>
/// <param name="methodInfo">The method information.</param>
/// <param name="loadId">The load identifier.</param>
/// <returns></returns>
/// <exception cref="System.ArgumentNullException">loadId</exception>
private Int32 ProcessImport(MethodInfo methodInfo, string loadId)
{
    // Validate parameter
    if (String.IsNullOrEmpty(loadId)) throw new ArgumentNullException("loadId");

    // Initialise the result and the procedure parametes
    Int32 result = 0;
    object[] parameters = { loadId };

    // Call the procedure, and return the result
    IExecuteResult executionResult = ExecuteMethodCall(this, methodInfo, parameters);
    if (executionResult != null) result = (int)executionResult.ReturnValue;
    return result;
}

called in decorated function like...

[Function(Name = "common.usp_MyData_ProcessImportAppend", IsComposable = false)]
public Int32 ProcessImportAddNewUpdateCurrentOnly(string loadId)
{
    return ProcessImport((MethodInfo)(MethodBase.GetCurrentMethod()), loadId);
}

Solution

  • I would do something like this for code reuse:

    [Function(Name = "import.usp_MyData_ProcessImportAppend", IsComposable = false)]
    public Int32 ProcessGradingImport(string loadId)
    {
        ProcessLoadId(loadId, (MethodInfo)(MethodBase.GetCurrentMethod(), new object[] { loadId });
    }
    
    [Function(Name = "import.usp_MyData_ProcessImportUpdate", IsComposable = false)]
    public Int32 ProcessGradingImportUpdate(string loadId)
    {
        ProcessLoadId(loadId, (MethodInfo)(MethodBase.GetCurrentMethod(), new object[] { loadId });
    }
    
    [Function(Name = "import.usp_MyData_ProcessImportOverwrite", IsComposable = false)]
    public Int32 ProcessGradingImportOverwrite(string loadId)
    {
        ProcessLoadId(loadId, (MethodInfo)(MethodBase.GetCurrentMethod(), new object[] { loadId });
    }
    
    public Int32 ProcessLoadId(string loadId, MethodInfo method, object[] parameters)
    {
        // Validate parameter
        if (String.IsNullOrEmpty(loadId)) throw new ArgumentNullException("loadId");
    
        // Initialise the result and the procedure parametes
        Int32 result = 0;
    
        // Call the procedure, and return the result
        IExecuteResult executionResult = ExecuteMethodCall(this, methodInfo, parameters);
        if (executionResult != null) result = (int)executionResult.ReturnValue;
        return result;
    }
    

    Unfortunately as of C# 5.0, there isn't a way to do what you are asking. The attribute doesn't allow multiple copies per function, so about all you can do is break out the reused code and factor it down a little bit. It could probably be reduced further to remove the parameters object since its just constructed from the loadId anyway.