tags:

views:

106

answers:

7

I am trying to write an Audit Log method that will log all changes in a Linq-To-Sql model.

I want to get all changes an loop through each type of change an call my method that creates a log record for each type of object.

I'm not sure if this is even possible.

This is my code. I get an error when I try to pass in the type as typeof(original) I don't know the type at desing time as it can be any of the linq objects in my model.

    ChangeSet changedObjects = this._lmsDb.GetChangeSet();

    foreach (var update in changedObjects.Updates)
    {

        Type type = update.GetType();
        var original = this._lmsDb.GetTable(type).GetOriginalEntityState(update);
        // Error from <typeof(original)>
        AuditLog au = AuditLogger.GetAuditLogRecord<typeof(original)>(original, update, "Update", "", userName);

        this._lmsDb.AuditLogs.InsertOnSubmit(au);

    }

Is it possible to do this? And if so, any hints on how to go about this?

A: 

You can probably just use an Object as the type that the function accepts... what does your function definition currently look like?

jle
+2  A: 

Call GetType on original instead of attempting to use typeof to get its type.

pmarflee
A: 

You don't need to specify the type at all. Type inference will do it for you. Just call the method without the type argument: AuditLogger.GetAuditLogRecord(original, update, "Update", "", userName);

Also note that var is not an unknown type at all. There is a specific type, and the compiler knows what it is, otherwise it would complain about your assignment to the variable. In Visual Studio the IDE will tell you the type if you mouse over the var keyword.

Joren
A: 

If I understand your problem correctly, you're trying to use var and typeof() on a heterogenous collection of objects. typeof() gets a compile-time type, and var is also compile-time typing. However, from your code snippet, it looks like you want a run-time type determination. As mentioned in another answer, GetType will give you the run-time type that is specific to an object, but you can't use that in a generic declaration of GetAuditLogRecord.

So in one sense, you're better off sticking with object as your type, at least until you get inside the GetAuditLogRecord method.

I don't know what the origin of your objects are. If these are generated code (from LINQ to SQL or Entity Framework, or some other tool), look to see if there are ways to exploit partial class or partial method implementations to handle your audit requirements. You may also be able to set up an abstract class that all these objects inherit from, that implements at least part of your audit requirements.

Cylon Cat
+1  A: 

How about reflection?:

MethodInfo mi = typeof(AuditLogger).GetMethod("GetAuditLogRecord");
Type[] genericArguments = new Type[] { original.GetType() };
MethodInfo genericMI = mi.MakeGenericMethod(genericArguments);
AuditLog au = 
    (AuditLog)genericMI.Invoke(
        null, new object[] { original, update, "", userName });

Joe

Thanks Joe. This is perfect!
littlechris
A: 

If you don't know the type use an object !

Yassir
A: 

If GetAuditLogRecord is a generic method, you don't have to specify the type it's using at compile time - that is, after all, why you use generic methods in the first place:

static void Main(string[] arguments)
{
    int i = 0;
    string s = "";
    Test(i);
    Test(s);
    Console.ReadLine();
}

static void Test<T>(T arg)
{
    Console.WriteLine(arg.GetType());
}

produces:

System.Int32
System.String

Note that my example doesn't have to be a generic method; it could take a parameter of type object and still work. Methods only need to be generic if they create new variables using the type parameters.

I doubt that you'd actually use generics for what you're trying to do here, though. In a design like what you're describing, I'd expect that GetAuditLog would have a signature like this:

AuditLogRecord GetAuditLog(IAuditable original, IAuditable current, AuditAction action, User user)

...where IAuditable exposes properties/methods that an AuditLogRecord needs and AuditAction is an enum. IAuditable might look like:

interface IAuditable
{
   List<KeyValuePair<string, object>> AuditableProperties;
}

So any object that can be audited is responsible for presenting a list of the property names/values whose changes are relevant to the audit log, and the AuditLogRecord object doesn't need to know anything more about the implementation details of the objects it's auditing than that.

Robert Rossney