VincentZHANG
VincentZHANG

Reputation: 787

How to convert the objects in a query result to custom type

I think the same question must have been asked, but I just don't know how to describe it properly.

I am using Entity Framework, and I have a table called vendor, consisting of many columns:

[vendor] 
 - [Id] [INT]
 - [Name] [NVARCHAR]
 - [ApiUrl] [NVARCHAR]
 - ...

I am writing a Web Api to expose the function which users can get vendor records by. However, I want to provide only three columns.

So I created a model class, as below:

public class OM_Vendor
{
    public int Id { set; get; }
    public string Name { set; get; }
    public string ApiUrl { set; get; }
}

After building the model classes with EF, the context object of EF has a property named vendors now. Then, in an API controller, I coded like this:

var vendors = this.objEntity.vendors
.Where<OM_Vendor>(v => v.Status == "1")
.OrderBy(v => v.Id)
.Skip(intSkip)
.Take(intLimit)
.ToList();

It doesn't work, neither does this:

    var vendors = this.objEntity.vendors
.Where(v => v.Status == "1")
.OrderBy(v => v.Id)
.Skip(intSkip)
.Take(intLimit)
.ToList<OM_Vendor>();

Is there a way to convert the objects returned by the methods like above to my custom type?

Thanks a lot.


In addition, I know that I can get it to work in this way:

var vendors = new List<OM_Vendor>();
vendors = objCont.ExecuteStoreQuery<OM_Vendor>("SELECT * FROM vendor").ToList();

And this way works too:

var vendors = this.objCont.vendors
                .Where(v => v.Status == "1")
                .OrderBy(v => v.Id)
                .Select(
                    row => new
                        {
                            Id= row.Id,
                            Name = row.Name,
                            ApiUrl = row.ApiUrl
                        }
                )
                .Skip(intSkip)
                .Take(intLimit)
                .ToList();

Update:

According to @Andrew's answer, I updated my code as below:

public class OM_Vendor
{
    public int Id { set; get; }
    public string Name { set; get; }
    public string ApiUrl { set; get; }

    public static implicit operator OM_Vendor(vendor vendor){
        return new OM_Vendor
        {
            Id = vendor.Id,
            Name = vendor.Name,
            ApiUrl = vendor.ApiUrl
        };
    }
}

And in my controller class:

List<OM_Vendor> vendors = this.objCont.vendors
                                        .Where(v => v.Status == "1")
                                        .OrderBy(v => v.Id)
                                        .Skip(intSkip)
                                        .Take(intLimit)
                                        .ToList();

I got error saying:

Cannot implicitly convert type 'System.Collections.Generic.List' to 'System.Collections.Generic.List'

What did I miss? As for the explicit cast, I will have to convert each instance explicitly in a loop?

Upvotes: 0

Views: 9034

Answers (2)

Claies
Claies

Reputation: 22323

You basically have 3 methods to achieve what you are trying to accomplish.

  1. Use the .Select() linq extension method to create a projection (as is demonstrated by your last example.) Generally a good option, as this will create a SQL expression which only returns the fields which are needed by your Data Transfer Object.
  2. Create a custom cast operator, e.g. public static implicit Operator OM_Vendor(Vendor vendor), which takes in a full Vendor object and assigns it to an instance of OM_Vendor. The main drawback here is that you are essentially retrieving all fields through Entity Framework, only to Flatten the entity and discard many of these fields during the implicit cast.
  3. Use a custom library such as Automapper to automate the process of converting the objects. Suffers from most of the same issues as 2., but having it handled by a library can be helpful if you have many classes you are Flattening.

A more complete code example to demonstrate:

public class OM_Vendor {
    ...
    public static implicit Operator OM_Vendor(Vendor vendor){
        return new OM_Vendor {
            Id = vendor.Id;
            Name = vendor.Name;
            ApiUrl = vendor.ApiUrl;
    }
}

OM_Vendor om_Vendor = this.objEntity.vendors.Where(
...

List<OM_Vendor> vendors = this.objEntity.vendors.Where(
...

Either of these statements should execute correctly, using the implicit Operator as a Constructor when assigning a Vendor instance to an OM_Vendor. However, it cannot be overstated, this is Flattening the object at the code level, and will result in a larger than necessary SQL query.

This actually should be an explicit operator, since data loss occurs during conversion. Changing implicit Operator to explicit Operator will cause this conversion to require the cast, i.e. OM_Vendor omVendor = (OM_Vendor)vendor;. This makes it much more clear that a conversion is occurring, but makes the code slightly more verbose.

Upvotes: 2

Glumac
Glumac

Reputation: 31

Have you tried projecting with:

.Select(row => new OM_Vendor{ Id=row.Id, Name=row.Name, ApiUrl=row.ApiUrl})

Upvotes: 0

Related Questions