1

C# Entity framework 4.0

I have a database with 10's of table with 2 common columns 'id' and 'modstamp'

to access modstamp in a table I have a function

protected internal override string GetModStampinChild(int sid)
{
    DBContext sq = new DBContext();
    return sq.xxxx.Where(s => s.id == sid)
        .Select(s => s.modstamp).SingleOrDefault().ToModStampString();
}

where xxxx change for every table. I am presently overriding this function for every table.

Is there a way to use some kind of generic "class" which I could use where "xxxx" would be any table?

4
  • 7
    I believe you can do this by making all your table entities implement an interface that exposes the id and modstamp properties. Commented Jan 7, 2015 at 20:52
  • 3
    There is a very real risk of leaking connections when DBContext sq = new DBContext(); is used without a try{}finally{} or using(){} block. Commented Jan 7, 2015 at 20:58
  • 1
    Can you clarify why you want to retrieve the ModStamp independently of the other columns in the Entity? And please wrap your new DBContext() call inside a using statement or you will leak that connection. Commented Jan 7, 2015 at 21:13
  • add using everywhere too bad it is not in Microsoft template. Commented Jan 8, 2015 at 15:07

4 Answers 4

2

First, you would need to have all of your Entities implement either an interface or an abstract class that contains both the ID and ModStamp properties in it, let's call it Stampable:

public abstract class Stampable 
{
  [Key]
  public int ID { get; set; } 

  [Required]
  public string ModStamp { get; set; }
}

At that point, all you need to do for your method is to have it implement generic typing:

protected internal override string GetModStampInChild<T>(int sid) where T : Stampable
{
    using (var sq = new DbContext())
    {
      return sq.Set<T>.Where(s => s.id == sid)
                      .Select(s => s.modstamp)
                      .SingleOrDefault()
                      .ToModStampString();
    }
}
Sign up to request clarification or add additional context in comments.

3 Comments

From a performance perspective do you really want to make another Database round trip to get the ModStamp when you probably already have retrieved the value in a different DbContext().
it is a good point, but I am looking this example as a general concept, as opposed to fixing a problem. modstamp is a timestamp and is used to detect optimistic concurrency problems. but yes in most cases I need to put the entire row.
Is this a Sql Server TimeStamp datatype? If so you shouldn't have anything to do as EF will map this to a byte[] datatype.
0

If I understand you correctly, you need a property Set<T> of DbContext class:

First, create base class of all your entity classes with id and modstamp properties. Then:

protected internal override string GetModStampInChild<T>(int sid) where T : BaseEntity
{
    using (var sq = new DbContext())
    {
        return sq.Set<T>.Where(s => s.id == sid)
                        .Select(s => s.modstamp)
                        .SingleOrDefault()
                        .ToModStampString();
    }
}

But you must use code-first paradigm for this method.

3 Comments

I am using DB first is this why I get a compile error: 'System.Data.Entity.DbContext.Set<TEntity>()' is a 'method', which is not valid in the given context.
if I add () after I get an other compile error: The type 'T' must be a reference type in order to use it as parameter 'TEntity' in the generic type or method
Yes, I forgot that's Set is the method, not the property, but it does not matter. Unfortunately this method can't be used with DB first, cause you can't entities are autogenerated in this case.
0

Another option would be add a new Property to your entity class via the partial class feature of c#.

So the entity definition generated might look like this, note I have no idea what the actual DataType of your ModStamp column is:

public partial class Company
{
    public int Id { get; set; }
    public byte[] ModStamp { get; set; }
    public string Name { get; set; }
    public string City { get; set; }
    public string State { get; set; }
}

Note the ModStamp column that you want to convert.

Then add to the Partial.cs file that EF creates code like this, note I have no idea what you actually want to do with the ModStamp value:

public static class ModConverter
{
    public static string ToModStampString(byte[] modStamp)
    {
        return BitConverter.ToString(modStamp);
    }
}

public partial class Company
{
    public string ModStampString 
    {
        get
        {
            return ModConverter.ToModStampString(this.ModStamp);
        }
    }
}

You would then have to manually add a new ModStampString Get Property for every Entity with a ModStamp Column like I did for the Company Entity.

Comments

0

Here is a solution that uses the Set method on the DbContext and expression trees to dynamically query that object.

private Expression<Func<TArg, bool>> CreatePredicate<TArg, TPredicateField>(string fieldName, TPredicateField value)
{
    ParameterExpression parameter = Expression.Parameter(typeof(TArg), "o");

    MemberExpression memberExpression = Expression.Property(parameter, fieldName);

    var condition = Expression.Equal(memberExpression, Expression.Constant(value));
    var lambda = Expression.Lambda<Func<TArg, bool>>(condition, parameter);

    return lambda;
}

private Expression<Func<TArg, TPredicateField>> CreateSelector<TArg, TPredicateField>(string fieldName)
{
    ParameterExpression parameter = Expression.Parameter(typeof(TArg), "o");
    Expression propertyExpr = Expression.Property(parameter, fieldName);

    var lambda = Expression.Lambda<Func<TArg, TPredicateField>>(propertyExpr, parameter);

    return lambda;
}

public TSelectorField GetModStamp<TEntity, TPredicateField, TSelectorField>(TPredicateField id) where TEntity : class
{
    using (var ctx = new OnTheFlyEntities("Data Source=(local);Initial Catalog=AscensionBO;Integrated Security=True;MultipleActiveResultSets=True"))
    {
        var predicate = CreatePredicate<TEntity, TPredicateField>("Id", id);
        var selector = CreateSelector<TEntity, TSelectorField>("ModStamp");

        TSelectorField item = ctx.Set<TEntity>().Where(predicate).Select(selector).SingleOrDefault();

        return item;
    }
}

You can then call it like this:

GetModStamp<Entity2, int, string>(1)

If you were willing to just return the found entity, you could eliminate the TSelectorField and then grab the ModStamp from the item after it is retrieved. That will drop one of the expression tree methods and a generic input on the main method.

As someone else suggested, you could go the interface route and use that example, it will be much simpler.

1 Comment

As above the Set<TEntity>() give a compile error. there is not "where" extension for SET(). EF 6.0 (4.0 if the assembly rev)

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.