locked
Entity Framework, Interfaces, and abstraction; help requested RRS feed

  • Question

  • This problem involves the use of a legacy Microsoft SQL database which is still in use by legacy code so we can’t modify the existing database schema which has some design defects/limitations. For example: this schema has columns of type string for dates formatted as yyyyMMdd. It also uses nullable byte types for Booleans where null, 0, and 255 represent false and 1 represents true.

    We are currently creating a new wrapper around this database using C# and the latest Entity Framework and we want to abstract some of those imperfect database implementations behind their ideal types like using a regular DateTime or Boolean types for the above respective scenarios. We also want to use a pure interface based design approach so that if we ever alter the database schema or replace it in the future our interfaces can simply be applied to new updated implementations and the calling code won’t have to be updated. This also will afford us complete independency from the Entity Framework so that really we could use any provider in the future.

    Based on this one of our mandates is that we write against an interface that abstracts the Entity Framework default Object Context and then only expose interfaces from this interface.

    For example:

    In our old database schema have a table Participant which is implemented in the database as follows:

     

    Participant (table)
    ParticipantId (column, bigint)
    isActive (column, byte nullable)
    
    
     

    The default EF generator created class Participant for this table which models what is in the database. We then created an interface IParticipant which the Participant class then implements and exposes additional properties that use the ideal types.

    public interface IParticipant
    {
    IsActive bool {get;set;}
    }
    
    
     

    Also class Participant and its EF accessor are marked as internal so only IParticipant is visible to the callers which will be originating from other assemblies.

    There is also an interface on our EF’s ObjectContext class, here is the snippet that relates to IParticipant:

     

    public interface ICustomObjectContext
    {
      IQueryable<IParticipant> Participants{get;}
    }
    
    

    EF’s ObjectContext implements ICustomObjectContext explicitly

     

    partial class ObjectContext : ICustomObjectContext 
    {
      IQueryable<IParticipant> ICustomObjectContext.Participants 
      {
      get {
      return this.Participants.Select<Participant, IParticipant>(part => part);} 
    }
    }
    
    
     

    Now we can implement IParticipant’s property IsActive we can convert to a datetime on the get and set the string from the datetime value on the set.

    This does create a performance problem though because we are forced to force query execution if we want to filter on IsActive by calling .ToList or .ToEnumerable and then executing a .Where because IsActive is not a column in the database and can’t be mapped to isActive because the data types differ.

    I then found a very elegant solution on Damien Guard’s blog entitled Client-side properties and any remote LINQ provider. He uses a compiled expression and then exposes it through a property. Here is an example of how I attempted to implement this design for IsActive:

    partial class Participant : IParticipant
    {
        private static readonly CompiledExpression<Participant, bool> _expIsActive = DefaultTranslationOf<Participant>.Property(e => e.IsActive).Is(x => x.isActive.HasValue && x.isActive == 1);
    
    public bool IsActive {get { return _expIsActive.Evaluate(this);} }
    }
    

     

    This would work fantastically if we had not decided to hide our implementations behind interfaces, this does not work because of the call to change IQueryable<Participant> to IQueryable<IParticipant> and IParticipant does not have property byte? isActive {get;} which is something I really want to avoid adding because it exposes the database  implementation.

     

     

    The following code would generate an exception of type System.NotSupportedException with message The specified type member 'IsActive' is not supported in LINQ to Entities. Only initializers, entity members, and entity navigation properties are supported.

    ICustomObjectContext model = getNewObjectContext();
    foreach(var part in model.Participants.Where(x => x.IsActive))
    { …// do something
    }
    
    
     

    I then tried to alter ExpressionVisitor to update the Expression<Func<T, Result>> types from IParticipant to Participant but that also left me with another exception.

    So far I have spent more than 1 day on researching this solution and I figure now would be a good time to ask the community before I spend more time on this and not get anywhere. So my questions are?

    Is this is even possible? Is there a way to use a pure interface design approach and implement a solution that would abstract the database implementations that I want to hide and still use IQueryable<IParticipant>?

    If so what approach would you recommend I take? Any insight / direction that someone could provide me be most appreciated. 

    One thing I thought of very briefly and that was to use SQL views but that kill my ability to execute insert, update, delete statements because views are read-only. Also I do realize that using this approach for the code I have shown will not allow me to do any insert, delete, update operations but I have already found an elegant solution for that and decided not to convolute my problem description with additional irrelevant details.

    Thanks again for any help that you can provide,

     

    -Igor


    -Igor
    Wednesday, March 2, 2011 7:25 PM

Answers

  • On 3/2/2011 2:25 PM, Igor Wolbers wrote:
    >
    > Based on this one of our mandates is that we write against an interface
    > that abstracts the Entity Framework default Object Context and then only
    > expose interfaces from this interface.
    >
     
    I don't know. Why does it have to be so complicated? Why can you just
    use DTO Data Transfer Object and map entity to DTO and DTO to entity, an
    abstraction away from the EF Model. If you are using a BLL and DAL, then
    both of them are abstracted away from the model due two DTO(s) being
    used between layers.
     
    • Marked as answer by Alan_chen Monday, March 21, 2011 1:32 AM
    Thursday, March 3, 2011 2:12 AM

All replies

  • On 3/2/2011 2:25 PM, Igor Wolbers wrote:
    >
    > Based on this one of our mandates is that we write against an interface
    > that abstracts the Entity Framework default Object Context and then only
    > expose interfaces from this interface.
    >
     
    I don't know. Why does it have to be so complicated? Why can you just
    use DTO Data Transfer Object and map entity to DTO and DTO to entity, an
    abstraction away from the EF Model. If you are using a BLL and DAL, then
    both of them are abstracted away from the model due two DTO(s) being
    used between layers.
     
    • Marked as answer by Alan_chen Monday, March 21, 2011 1:32 AM
    Thursday, March 3, 2011 2:12 AM
  • Hi Igor,

    I am writing to check the status of the issue on your side.  Would you mind letting us know the result of the suggestions? 
     
    If you need further assistance, please feel free to let me know.   I will be more than happy to be of assistance.

    Have a nice day.


    Alan Chen[MSFT]
    MSDN Community Support | Feedback to us
    Get or Request Code Sample from Microsoft
    Please remember to mark the replies as answers if they help and unmark them if they provide no help.

    Tuesday, March 8, 2011 1:10 AM
  • Thank you for your response but I believe I found the answer. I can manipulate the Expression object's expression tree in method WithTranslations() (I forgot to include that call in the original post's code). I analyze the expression and find the Select<TImplementation,TInterface>(part=>part) and move it to the end as the last part to be executed in a new expression tree object.

    I did read through the suggested answer but I found that what I already have is a better fit and less work than creating a bunch of DTO objects. When it comes down to it I already have  Self Tracking Entities but they are just abstracted via an interface, no need to introduce an additional layer.

    I do appreciate the response and also the follow up.

    Thank you,

    -Igor


    -Igor
    Tuesday, March 8, 2011 6:55 PM
  • would u mind sharing your solution pls?
    Friday, October 7, 2011 12:15 AM
  • Alternatively, you can download my project on Codeplex. I have a working example that you can copy.
    https://entityinterfacegenerator.codeplex.com/

    It generates the interface files that you need for IoC purposes.

    Friday, April 25, 2014 2:22 PM