0
\$\begingroup\$

The following code reads from Microsoft EntityFramework 6.0 domain model and returns the single view_patient object. The view_patient object is defined in the backend database and uploaded to the .edmx model of EF.

I am new to EF, but it seems there should be a better way--(better means less typing)-- of going from model to object without the intermediary anonymous data transfer object. As I have a couple hundred of these procedures to write (all within a WCF service), is there a better way of doing this? Or perhaps a "generic" method that will allow going directly from Link-to-entity to output of objects?

A note towards naming convention: The classes are coming directly from Npgsql 3.0.5 EntityFramework interface -- which in turn is being read directly from PostgreSQL 9.5 database. I can not seem to get either EF or Npgsql to return PascalCase from the PostgreSQL database which is case-insensitive and, without adding quotes to all names, returns only lowercase names for tables, stored procedures, and columns.

Note #2: When not going through the intermediate DTO, I get the following error:

The entity or complex type 'chaosModel.view_patient' cannot be constructed in a LINQ to Entities query.

(Using .NET 4.5)

  // testing the setup
    public view_patient Test(string cpatient)
    {
        using (var ctx = new chaosEntities())
        {

            var q = (from patient in ctx.patients
                     join chart in ctx.charts on patient.chart_recid equals chart.recid
                     where patient.cpatient == cpatient
                     select new
                     {
                         birthdate = patient.birthdate,
                         chart_number = chart.chart_number,
                         chart_recid = chart.recid,
                         city = patient.city,
                         cpatient = patient.cpatient,
                         donotsee = chart.donotsee,
                         firstname = patient.firstname,
                         groupid = chart.groupid,
                         lastname = patient.lastname,
                         mailbox = patient.mailbox,
                         mi = patient.mi,
                         patient_recid = patient.recid,
                         phone = patient.phone,
                         selfpay = chart.selfpay,
                         cashonly = chart.cashonly,
                         sex = patient.sex,
                         ssn = patient.ssn,
                         state = patient.state,
                         street = patient.street,
                         zipcode = patient.zipcode
                     });

            var r = (from c in q.AsEnumerable()
                     select new view_patient
                     {
                         birthdate = c.birthdate.ToShortDateString(),
                         chart_number = c.chart_number,
                         chart_recid = c.chart_recid,
                         city = c.city,
                         cpatient = c.cpatient,
                         donotsee = c.donotsee,
                         firstname = c.firstname,
                         groupid = c.groupid,
                         lastname = c.lastname,
                         mailbox = c.mailbox,
                         mi = c.mi,
                         patient_recid = c.patient_recid,
                         phone = c.phone,
                         selfpay = c.selfpay,
                         cashonly = c.cashonly,
                         sex = c.sex,
                         ssn = c.ssn,
                         state = c.state,
                         street = c.street,
                         zipcode = c.zipcode
                     }).SingleOrDefault();

            return r;
        }
    }
\$\endgroup\$

closed as off-topic by Jamal Feb 17 '16 at 16:29

This question appears to be off-topic. The users who voted to close gave this specific reason:

  • "Questions containing broken code or asking for advice about code not yet written are off-topic, as the code is not ready for review. After the question has been edited to contain working code, we will consider reopening it." – Jamal
If this question can be reworded to fit the rules in the help center, please edit the question.

  • \$\begingroup\$ Please remember to add language tags to your posts as well. \$\endgroup\$ – Jamal Feb 17 '16 at 6:17
  • 1
    \$\begingroup\$ You seem to actually want some form of "automapping" (iterating with reflection over object fields/properties and matching them). There seems to be Automapper Nuget package and here is Tutorial for it. If I remember correctly, it have some tricks to perform better than raw reflections (which are always slow) - someday I will migrate my EF code to it.... someday ;) \$\endgroup\$ – PTwr Feb 17 '16 at 7:50
3
\$\begingroup\$

Besides the naming that should more C# friendly (PascalCase), the application should have a clear separation between data models and view models.

Data models are usually generated automatically in database first scenarios or POCOs classes defined by the programmer (code-first approach) and should be separated from view models or data returned by the service layer which usually consists in aggregated information.

Ok, for your particular case, data model is Patient:

public partial class Patient
{
    public DateTime BirthDate { get; set; }
    public String City { get; set; }
    public String Cpatient { get; set; }

    // rest of properties come here
}

and your view model can be called PatientViewModel:

public partial class PatientViewModel
{
    public DateTime BirthDate { get; set; }
    public DateTime BirthDateStr 
    { 
        get { return BirthDate .ToShortDateString(); }
    }

    public String ChartNumber { get; set; }
    public ChartRecid { get; set; }
    public String City { get; set; }
    public String Cpatient { get; set; }
}

So, as you can see, the view model hold the properties required by the view, which are aggregated from multiple data models.

Getting data model extra data

One method I found convenient (although not exactly orthodox, since it pollutes data models) is to extend the data model with properties that provide extra needed data, like this:

// this is another part of the partial class
public partial class Patient
{
    public String ChartNumber 
    { 
       get { return Chart.chart_number; }
       set { Chart.chart_number = value; }
    }

    public ChartRecid 
    { 
       get { return Chart.ChartRecid; }
       set { Chart.ChartRecid = value; }
    }

    // other "computed" properties come here
}

An alternative to this approach is to have custom map these properties when transferring data model -> view model and vice-versa (so, this also supports data persistence, not just data fetch).

Actual mapping

As already suggested, AutoMapper was specially designed for this kind of chores. Its usage is simple as:

setup the mapping:

(this is deprecated in the last version, as described here (and also mentioned by the compiler, if last NuGet package is referenced))

Mapper.CreateMap<Patient, PatientViewModel>();
Mapper.CreateMap<PatientViewModel>();

using it:

var patientDataModels = ctx.patients.
   .Include(p => p.Chart)               // eager load chart data
   .Where(p => cpatient == cpatient)
   .ToList();                           

var patientVms = new List<PatientViewModel>();
// Mapper should also handle collection mapping automatically, if type mapping is set up
Mapper.Map(patientDataModels, patientVms);

// if data model is not extended, you have to manually map Chart type properties

Eager load is recommended to avoid separate queries generated when Chart object properties are required (less SQL verbosity, significantly faster for large collections).

AutoMapper will automatically map properties having the same (and type, if type is different and no implicit conversion can be done, it will throw an exception) and not ignored (IgnoreMap attribute).

\$\endgroup\$
  • \$\begingroup\$ I will try it out. The idea of manually creating the classes in code on a database first project where there are hundreds of tables is not particularly interesting to me. Thanks. \$\endgroup\$ – Alan Wayne Feb 17 '16 at 16:30
  • \$\begingroup\$ No, in a database first approach, data models are automatically generated using edmx editor (until EF 6, in EF 7 I think there is/will not be one). Of course, view models have to be written. \$\endgroup\$ – Alexei Feb 17 '16 at 17:00
  • \$\begingroup\$ I'm new to all this. Please explain "No, in a database first approach...". Thanks. \$\endgroup\$ – Alan Wayne Feb 17 '16 at 18:39
  • 1
    \$\begingroup\$ @AlanWayne - for existing databases, Entity Framework database first is used to generate database context (e.g. chaosEntities) and classes for each selected table (e.g. patient). More details (step by step, actually) are provided here. So, all data models like patient are automatically generated. Data model extensions (partial classes) and view models are manually written, however this effort pays when mapping logic is greatly simplified. \$\endgroup\$ – Alexei Feb 17 '16 at 21:09

Not the answer you're looking for? Browse other questions tagged or ask your own question.