Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Exposing Entity Framework over WCF Service

I need some help here! I'm trying to create my first application using Entity Framework, WCF, MVVM and WPF technologies. I'm new to all of them. So I create a model for my database using Entity Framework. Then I create my WCF service class. I have read close to 50 articles about EF and WCF services and I'm all about confusion right now. I know that I'm not supposed to expose my model directly. Currently I'm using this code as service contract:

namespace Ohmio.DataService
{
    [ServiceContract]

    public class OhmioService
    {
        [OperationContract]
        public IEnumerable<vw_Pedidos> ListarPedidos(string IDComprobante, bool bEntregados, int Numero = -1, int IDCliente = -1)            
        {
            using (var context = new OhmioTestNet())
            {
                string sqlString="SELECT VALUE cs FROM OhmioTestNet.vw_Pedidos AS cs WHERE cs.ID_Comprobante=='" + IDComprobante + "' AND ";
                if (Numero != -1) sqlString += "cs.Numero=="+ Numero.ToString() +" AND ";
                if (IDCliente != -1) sqlString += "cs.ID_Cliente=="+ IDCliente.ToString()+" AND ";
                if (!bEntregados) sqlString += "cs.Entregado==false AND ";                 
                sqlString =sqlString.Substring(0,sqlString.Length-4);

                ObjectQuery<vw_Pedidos> Pedidos = context.CreateQuery<vw_Pedidos>(sqlString);                
                var result = Pedidos.ToList();
                result.ForEach(e => context.Detach(e));
                return result;
            }
        }

        [OperationContract]
        public IEnumerable<Clientes> GetClientes()
        {
            using (var context = new OhmioTestNet())
            {
                var result = context.Clientes.Where(f => f.Activo == true).ToList();
                result.ForEach(e => context.Detach(e));
                return result;
            }
        }
    }
}

My questions:

  1. The problem with it is that don't suppose to expose my data model directly, right? So here I'm returning detached entity of class vw_Pedidos. Is this a bad practice?

  2. I have read a lot about DTO's and POCO's object, which should I use instead of the above code?

  3. If I use DTO or POCO to transfer over WCF, do I have to manually create a DTO for each database object? Do I have to manually create all the properties (fields) for each DB object?

  4. If I add a new field to a database object and need to show it on client, do I have to update my EF model, add the new field manually to the DTO or POCO object? This sounds like a maintenance nightmare!

  5. Is using EntitiesToDTOs a good option for automatic create DTOs based on EF objects?

  6. Can I use DTO to update data back to database?

Sorry for the multiple questions. Please help me out here! I'm searching for a pattern that lead me to an easy to maintain, concern separated code. Thanks!

UPDATE

Following TomTom suggestions, i read a lot about POCOs, EF and Linq to Entities. After that i re-write my whole app. I divide my code on 5 projects:

1)DataLayer

2)ModelLayer

3)BusinessLyer

4)WCF Service Layer

5)WPF Client

For comunications on layers 1 to 5 I use POCO classes mapped to Entity Framework using POCO T4 Template. To comunicate layer 4 and 5 over WCF I want to use a custom class to hide some field from client (I don't want to expose the whole POCO class) so I re-write filter function using linq to entities and projection to IEnumerable:

public IEnumerable<PedidosList> Pedidos_Listar(string sComprobante, Clientes MyCliente = null, DateTime? dDesde = null, DateTime? dHasta = null, bool bCumplidos = false)
            {           
                using (var context = new OhmioEntities())
                {
                    IEnumerable<PedidosList> query =
                        from Pedidos in context.Pedidos
                        join Clientes in context.Clientes on Pedidos.ID_Cliente equals Clientes.ID_Cliente
                        where Pedidos.ID_Comprobante == sComprobante                    
                        select new PedidosList {ID_Pedido = Pedidos.ID_Pedido, Fecha=Pedidos.Fecha, Aprobado=Pedidos.Aprobado, Bruto=Pedidos.Bruto, Cliente=Clientes.RazonFantasia, 
                            FechaEntrega=Pedidos.FechaEntrega, Neto=Pedidos.Neto, Numero=Pedidos.Numero, Observaciones=Pedidos.Observaciones, Entregado=Pedidos.Entregado, ID_Cliente=Pedidos.ID_Cliente };

                    if (MyCliente != null) query = query.Where(i => i.ID_Cliente == MyCliente.ID_Cliente);
                    if (MyCliente != null) query = query.Where(i => i.ID_Cliente == MyCliente.ID_Cliente);
                    if (dDesde != null && dHasta != null) query = query.Where(i => i.Fecha >= dDesde && i.Fecha <= dHasta);
                    if (bCumplidos == false) query = query.Where(i => i.Entregado == false);                
                    return query.ToList();
                }
            }

Is this the best way to do this? Can i make the projection AFTER the filter? Thanks!

like image 897
ericpap Avatar asked Mar 18 '14 15:03

ericpap


1 Answers

The problem with it is that don't suppose to expose my data model directly, right? So here i'm returning dettached entity of class vw_Pedidos. Is this a bad practice?

Yes.

I have read a lot about DTO's and POCO's object, wich should i use instead of the above code?

Both. Given that a DTO pretty much normally is a POCO. Maybe you try to understand what the words MEAN. POCO is a "Plain Old C# Object" (not required to inherit from a base class) and that is totally normal for a POCO.

If I use DTO or POCO to transfer over WCF, do i have to manualy create a DTO for each Database Object? Do I have manualy create all the properties (Fields) for each DB object?

No. Ever heard of T4? That can be scripted. That said, these normally aren't database but API objects - the WCF service is a (public) front end of the server program.

If i add a new field to a DB object and need to show it on client, do i have to update my EF model, add the new field manualy to DTO or POCO object? This sounds like a mantenance nigthmare!

It absolutely is one. If you do things without thinking. Seriously, once you have hammered out the basic model, this is not something that happens every 15 minutes. Also it includes a significant work on the front end and logic anyway - I mean, if the property is useless then hey, why put it in? If it is not useless it is a lot of work anyway. And the models can be updated, not need to regenerate.

Heck, I normally work with non-trivial databases. In this case a change is not "regenerate a model", it also includes "write a sql update script for versioning" and "test the update performance" (adding a field to a multi-billion row table with default value and generating indices can take some time). And it still is no nightmare because this does not happen every 5 minutes. MOST stuff is not "add fields", it is "Program".

Can i use DTO to update data back to database?

What else?

Please help me out here! I'm searching for a pattern that lead me to an easy to mantain, concern separated code

You will not find one. This is a matter of making the right compromise. You DO seem to overestimate the amount of changes that happen in a project though.

You do more well to assume that the WCF service is a trust boundary and public API - much like a website, just for consumption by a computer. The objects you expose there must not absolutely conform to the database internals. It is better to generate them separately and possibly change them to be more in line with what is needed for the public API - than to expose back-end implementation details to an API.

I have to say, though, your work is made a lot more complicated by the archaic and absolutely contra-productive way you access the database - start using a proper sql generator and object mapper, or you'll have a maintenance nightmare right there, regardless how you do the rest. Putting together SQL strings like that is a SERIOUS "no, never". And look up how to expose OData via WCF. That gives you query functionality via URLs.

like image 163
TomTom Avatar answered Sep 28 '22 16:09

TomTom