Using Enterprise Library's ExecuteSprocAccessor method with generics - c#

I am trying to use one class to handle all of my CRUD on the database by using the ExecuteSprocAccessor method of the .NET Enterprise Library with generics. In my data layer, I was trying something like this:
public static class CRUDDatabase
{
private static Database db = DatabaseFactory.CreateDatabase("ITS");
public static T SelectSingle<T>(string sprocName, int id)
{
return db.ExecuteSprocAccessor<T>(sprocName, id).First();
}
}
However, I get a build error on the return line in the SelectSingle() method that states:
'T' must be a non-abstract type with a public parameterless
constructor in order to use it as parameter 'TResult' in the generic
type or method
'Microsoft.Practices.EnterpriseLibrary.Data.DatabaseExtensions.ExecuteSprocAccessor(Microsoft.Practices.EnterpriseLibrary.Data.Database,
string, params object[])'
The idea behind the SelectSingle() method is that you pass in the stored procedure name and the record id of the object you want from the database. Eventually I would have SelectAll(), Update(), Delete(), etc. The parameters in those methods would be different, but you get the idea of what I'm trying to accomplish.
After reading that error, I am beginning to think this may be impossible, but does anyone know if this can work? Also, my fields in the database match 1:1 with the fields in my classes, so that is why I am not specifying any mappers.
Thanks

The compiler is telling you what's wrong: it's expecting a type that's got a zero-argument public constructor on it. All you've handed it is T, which it can't make any guarantees on, so it won't compile.
You need to add a generic constraint to limit the types T can be. Luckily, that's trivially easy:
public static T SelectSingle<T>(string sprocName, int id)
where T : new() // <---- here's the constraint
{
return db.ExecuteSprocAccessor<T>(sprocName, id).First();
}
That tells the compiler "Any type passed here must have a zero-argument constructor.

Related

C# Inheritance with a generic type appears to be creating a different object type

I have an abstract class that i use for entity framework. I then inherit the class and override when needed. In this case i have a post that i want to delete and with it i want to delete the comments associated with the post. The logic is simple, but i seem to be getting weird class type issues.
This is what i expected would work. However, entity.ID doesnt exist. In fact,
the entity object has no properties at all (in terms of visual studio intellisense). I tried to cast it and it says that it cant cast Post to MyProject.Models.Post, even though it should be the exact same class. So it seems that by using the generic type and casting it during the inheritance, it is making a new class of the same name.
public abstract class MyAbstractService<T>
{
public virtual void Delete<T>(T entity)
{
this.context.Set<T>().Remove(entity);
}
}
public abstract class MyAbstractService :AbstractService<Post>
{
public override void Delete<Post>(Post entity)
{
this.context.PostComments.RemoveRange(this.context.PostComments.Where(x => x.Post.ID == entity.ID));
base.Delete(entity);
}
}
I was able to get around this type issue by using the method below, but i dont understand why this happened in the first place. I am assuming there is a better way to handle this than the method of casting i have used here. If there is no alternative, what is making this happen in the first place?
public override void Delete<Post>(Post entity)
{
MyProject.Models.Post pst = entity as MyProject.Models.Post;
this.context.PostComments.RemoveRange(this.context.PostComments.Where(x => x.Post.ID == pst.ID));
base.Delete(entity);
}
Based on the comments it would seem that something like this would be a more preferred way of implementing a delete, however I still feel the abstract method is of use as i have been using it for many other entities that do not have relationships.
public void Delete(Post entity)
{
this.context.PostComments.RemoveRange(this.context.PostComments.Where(x => x.Post.ID == entity.ID));
base.Delete(entity);
}
Your Delete method on MyAbstractService is defining a method that says that any MyAbstractService is able to delete any object of any type.
Thus when you inherit from that abstract class and provide an implementation of that method you need to provide a method that is able to delete any object of any type. You've named your generic type Post in the implementation (you're able to call it whatever you want), but calling that generic parameter Post doesn't mean that the instances are of type Post, that's just the name of your generic type, so the object can be of any type. If you want to treat the object as if it's of type Post you need to cast it, as you're doing.
Now you have a method that's signature claims it can accept an object of any type, but in reality it'll just fail if it's not an object of type Post. That's bad design; your API is lying to its callers.
Instead it doesn't make sense for MyAbstractService types to have a method that can delete objects of any type. Delete shouldn't be generic to begin with. Instead it should just use the generic type of the class, rather than defining a second generic type (which you also call T, confusingly enough).

Type parameter which inherits from a certain abstract

I was wondering if there is a way to pass in a type which is enforced to be of a certain base type. For instance, for my game engine I have a attribute list (controllable, rigidBody, etc). I have a addAttribute parameter which takes a new object. However, I would like to also have an overload which takes a type instead from which I can create a new object on my own. So for instance
public void addAttribut(Type attribute)
I am thinking of maybe something that implements linq? I have tried but it looks like "where" can be only used on generics:
public void addAttribute(Type attribute) where attribute : Attribute
would be the signature of the function. I believe that Unity does this; however it could be that it can do this through Mono.
I could always throw an exception if it is not of the correct base type. However I was wondering if there is a way to prevent the programmer from passing in the wrong time all together.
Any ideas? Any help is greatly appreciated!
If you're happy for calling code to need to know the type at compile-time, you could make it a generic method with a type parameter:
public void AddAttribute<T>() where T : Attribute
{
// Use typeof(T)
}
Or to enforce a parameterless constructor which you can then call:
public void AddAttribute<T>() where T : Attribute, new()
{
T t = new T();
...
}
But otherwise there's no way of doing it, no - you'd have to validate the argument at execution time, just like any other.

Passing a type generated at RunTime (via TypeBuilder) to GenericClass

I would like to pass a type that is generated at runtime (via TypeBuilder) to a generic method in a generic class. I can't pass it as object as reflection is used to look up various properties.
As I understand, this can't be done as generics are applied at compile-time rather than run-time.
I know I can change an method like
public T Read()
{
T data = new T()
...
return data;
}
to be something like
public object Read(Type newType)
{
object data = Activator.CreateInstance(newType);
...
return data;
}
but this obviously loses all the advantages of generics when the type is known, so I will probably end with both methods, which unfortunately means also duplicating quite a few other other helper functions.
Is there any better way of resolving this issue?
This particular project needs to work under the 3.5 framework, but if it isn't possible in 3.5, but is under 4.0, I wouldn't mind knowing.
Assuming Read() is a member of class Reader<T> where T : new(), you can call the generic version like this (possibly using another overload of GetMethod() if you have more than one method with the name Read on that type):
Type readerType = typeof(Reader<>).MakeGenericType(generatedType);
object result = readerType
.GetMethod("Read")
.Invoke(Activator.CreateInstance(readerType), new object[0]);
This way, you have the advantage of generics inside Read(), but not outside. Depending on what you want to do, maybe it would be better to use interfaces instead of generics. The new() constraint can be emulated by generating a factory type for every generated type, the types implementing the following (non-generated) interfaces:
interface IWhatever { … }
interface IWhateverFactory
{
IWhatever Create();
}

Can I modify this generics method to work without losing (my religion) the usage-style I want?

The method is this one, EntityBase.Get:
public class EntityBase
{
public int Id;
public static T Get<T>(int id) where T : EntityBase
{
DataContextExtender extender = new DataContextExtender();
return extender.DataContext.GetTable<T>().Where(t => t.Id == id).FirstOrDefault();
}
}
How I want to use it:
Event ev = Event.Get(EventId)
, where Event inherits EntityBase.
The method compiles by itself, like it is, but I get an error message if I want to use it that way:
The type arguments for method 'RiotingBits.Data.Entities.EntityBase.Get(int)' cannot be inferred from the usage. Try specifying the type arguments explicitly.
I know I can use it like Event.Get<Event>(EventId), but I really want to use it 'my way'. The code for the method doesn't matter, I suspect there might be a way to hint the method how to infere the right type.
No, you can't. Type inference in C# doesn't take into account what you're trying to do with the returned value, nor does it take into account the class you used to refer to a static method. Type inference is horribly complicated, but ultimately it boils down to the static types of the arguments you've used to invoke the method.
If you only want to do it in a single type (Event) you could always create a new GetEvent static method in Event, which just called Get<Event>.
I don't think so. I believe that types are only inferred based on the parameters to the method. So I think you're stuck with specifying the type of the class explicitly. See MSDN for more information.

Why do I get "error: ... must be a reference type" in my C# generic method?

In various database tables I have both a property and a value column. I'm using Linq to SQL to access the database.
I'm writing a method which returns a dictionary containing the properties/values retrieved from the given database table:
private static Dictionary<string, string> GetProperties<T>(Table<T> table)
{
Dictionary<string, string> properties = new Dictionary<string, string>();
foreach (var row in table)
{
properties[row.Property]=row.Value;
}
return properties;
}
Upon compiling, I get:
Error 1 The type 'T' must be a reference type in order to use it as parameter 'TEntity' in the generic type or method 'System.Data.Linq.Table<TEntity>'
I've tried searching for this error message without luck.
Searching StackOverflow, this question seems similar, though regarding a parameter List: Generic List<T> as parameter on method - though the parameter still isn't a reference type in the answers to that question, either.
Reading the C# Programming Guide on MSDN: http://msdn.microsoft.com/en-us/library/twcad0zb(VS.80).aspx I see their examples all pass the parameters by reference. However, I can't see how to pass by reference in my particular case, since the generic type is just for specifying the generic type of Table.
Any pointers would be much appreciated.
PS: Appologies if it takes time for me to accept an answer, as this feature is currently not accessible (I'm blind and use a screen reader).
This happens because of how Table<T> is declared:
public sealed class Table<TEntity> : IQueryable<TEntity>,
IQueryProvider, IEnumerable<TEntity>, ITable, IQueryable, IEnumerable,
IListSource
where TEntity : class // <-- T must be a reference type!
The compiler is complaining because your method has no constraints on T, which means that you could accept a T which doesn't conform to the specification of Table<T>.
Thus, your method needs to be at least as strict about what it accepts. Try this instead:
private static Dictionary<string, string> GetProperties<T>(Table<T> table) where T : class
Just add the constraint where T : class to your method declaration.
This is required because Table<TEntity> has a where TEntity : class constraint. Otherwise your generic method could be called with a struct type parameter, which would require the CLR to instantiate Table<TEntity> with that struct type parameter, which would violate the constraint on Table<TEntity>.
public class TEntityRepository<TEntity> : EFRepository<TEntity> , ITEntityRepository<TEntity>
where TEntity : class, new()
{
}

Categories

Resources