Warning: file_get_contents(/data/phpspider/zhask/data//catemap/8/linq/3.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# C Linq to Entities方法将属性投影到字符串_C#_Linq_Linq To Entities_Projection - Fatal编程技术网

C# C Linq to Entities方法将属性投影到字符串

C# C Linq to Entities方法将属性投影到字符串,c#,linq,linq-to-entities,projection,C#,Linq,Linq To Entities,Projection,我试图重构一行到处都在使用的代码。我们正在使用EF6.1,希望以字符串形式查找电话和电子邮件 public SiteLabelDto[] GetVendorSites(int vendorId) { return Context.Sites.Where(s => !s.IsDeleted && s.Vendor.Id == vendorId) .Select(s => new SiteLabelDto

我试图重构一行到处都在使用的代码。我们正在使用EF6.1,希望以字符串形式查找电话和电子邮件

    public SiteLabelDto[] GetVendorSites(int vendorId)
    {
        return Context.Sites.Where(s => !s.IsDeleted && s.Vendor.Id == vendorId)
            .Select(s => new SiteLabelDto
            {
                Id = s.Id,
                Name = s.Name,                    
                Address = s.Address.StreetAddress + ", " + s.Address.Locality + ", " + s.Address.Region + ", " + s.Address.Postcode,
                Country = s.Address.Country.Name,
                Email = s.ContactPoints.FirstOrDefault(x => x.Type == ContactPointType.Email) != null ? s.ContactPoints.FirstOrDefault(x => x.Type == ContactPointType.Email).Value : "",
                Phone = s.ContactPoints.FirstOrDefault(x => x.Type == ContactPointType.Phone) != null ? s.ContactPoints.FirstOrDefault(x => x.Type == ContactPointType.Phone).Value : "",                    
            }).ToArray();
    }
上面的代码获取联系人列表,并尝试为每种类型找到最适合的联系人

public class ContactPointEntity
{
    public int Id { get; set; }
    public string Value { get; set; }
    public ContactPointType Type { get; set; }
    public bool IsDefault { get; set; }
}
该方法将被扩展以尝试在first中首先返回IsDefault one

我的目标是尝试并能够将其放入一个方法或扩展中,这样我就可以说s.GetcontactPointContactPointType.Email或s.contactPoints.GetPointsContactPointType.Email并返回字符串值,如果字符串不可能出现,则返回contactpoint类


我读得越多,我想我需要构建一些表达式树,但还不确定如何构建。

使用Linq to Entite的扩展方法有点棘手,因为不是所有的提供者都能理解并转换为相应的后端调用。一个相对安全的赌注是获取IQueryable并返回它可以解析的IQueryable:

public static IQueryable<SiteDTO> MapToSiteDTO(this IQueryable<Site> query)
{
    return query.Select(s => new SiteLabelDto
                            {
                                Id = s.Id,
                                Name = s.Name,                    
                                Address = s.Address.StreetAddress + ", " + s.Address.Locality + ", " + s.Address.Region + ", " + s.Address.Postcode,
                                Country = s.Address.Country.Name,
                                Email = s.ContactPoints.FirstOrDefault(x => x.Type == ContactPointType.Email) != null ? s.ContactPoints.FirstOrDefault(x => x.Type == ContactPointType.Email).Value : "",
                                Phone = s.ContactPoints.FirstOrDefault(x => x.Type == ContactPointType.Phone) != null ? s.ContactPoints.FirstOrDefault(x => x.Type == ContactPointType.Phone).Value : "",                    
                            });
}

您需要构建一个表达式树

首先,由于需要引入IsDefault条件,表达式可能如下所示:

s.ContactPoints
 .Where(x => x.Type == ContactPointType.Email && x.IsDefault)
 .Select(x => x.Value)
 .DefaultIfEmpty(string.Empty)
 .FirstOrDefault()
然后,将接触点选择器转换为表达式

private static Expression<Func<Site, string>> GetContactPoint(ParameterExpression siteParam, ParameterExpression cpeParam, ContactPointType type)
{
    // Where.
    var typeCondition = Expression.Equal(Expression.Property(cpeParam, "Type"), Expression.Constant(type));
    var defaultCondition = Expression.Equal(Expression.Property(cpeParam, "IsDefault"), Expression.Constant(true));
    var condition = Expression.AndAlso(typeCondition, defaultCondition);
    var predicateExp = Expression.Lambda<Func<ContactPointEntity, bool>>(condition, cpeParam);
    var whereExp = Expression.Call(typeof(Enumerable), "Where", new[] { typeof(ContactPointEntity) }, Expression.Property(siteParam, "ContactPoints"), predicateExp);

    // Select.
    var valueExp = Expression.Lambda<Func<ContactPointEntity, string>>(Expression.Property(cpeParam, "Value"), cpeParam);
    var selectExp = Expression.Call(typeof(Enumerable), "Select", new[] { typeof(ContactPointEntity), typeof(string) }, whereExp, valueExp);

    // DefaultIfEmpty.
    var defaultIfEmptyExp = Expression.Call(typeof(Enumerable), "DefaultIfEmpty", new[] { typeof(string) }, selectExp, Expression.Constant(string.Empty));

    // FirstOrDefault.
    var firstOrDefaultExp = Expression.Call(typeof(Enumerable), "FirstOrDefault", new[] { typeof(string) }, defaultIfEmptyExp);

    var selector = Expression.Lambda<Func<Site, string>>(firstOrDefaultExp, siteParam);
    return selector;
}
附言:

可能上面的一些代码需要重构,这只是给出了如何重构的基本思路

更新

您还可以创建一个包装器类来包含EF实例和所有接触点

public class ContactPointExt<T>
{
    public T Instance { get; set; }
    public string Email { get; set; }
    public string Phone { get; set; }
}
ContactPointExt的结果可以通过另一个选择重新投影到SiteLabelDto

从OP编辑

我们创建了一个包装器方法来简化重用,将其放在这里只是为了向其他人展示:

    /// <summary>
    /// Wraps up a each of a query's objects in a ContactPointExt&lt;T&gt; object, providing the default contact point of each type.
    /// The original query object is accessed via the "Instance" property on each result.
    /// Assumes that the query object type has a property called ContactPoints - if different, supply the property name as the first argument.
    /// </summary>
    /// <typeparam name="T"></typeparam>
    /// <param name="query"></param>
    /// <param name="contactPointsPropertyName"></param>
    /// <returns></returns>
    public static IQueryable<ContactPointExt<T>> WithContactPointProcessing<T>(this IQueryable<T> query, string contactPointsPropertyName = "ContactPoints") where T : class
    {
        var siteParam = Expression.Parameter(typeof(T), "s");
        var cpeParam = Expression.Parameter(typeof(ContactPointEntity), "cpe");
        var selector = ContactPointHelpers.GetContactPoints<T>(siteParam, cpeParam, contactPointsPropertyName);
        return query.Select(selector);
    }

Hi Mrchief,这一切都在编译,但我得到了:INQ to Entities无法识别方法'System.String GetContactPointSystem.Collections.Generic.IEnumerable`1[Opus.Repository.Models.ContactPointEntity],Opus.Repository.Models.ContactPointType',并且该方法无法转换为存储表达式。另外,我认为有一个打字错误,最多,您正在检查src是否为null。我想你的意思是。。我没有纠正,只是怕我不理解。谢谢你支持我的回答。如果您正试图消除映射代码的重复,那么这种方法就可以了。或者,如果您正在寻找一种通用方法来消除此处的ContactPointType查找以及其他查询,那么您可能必须对此进行调整。谢谢主任先生,我的目标是控制ContactPoint在域中不同位置和许多不同POCO中的出现方式,而不仅仅是一种类类型。嗨,James,我的目标是控制接触点在域和许多不同POCO中的不同位置出现的方式,而不是像上面描述的那样显式使用它,因为需要多次重写该代码。Regard我很喜欢你的答案,但我需要在接触点上这样做,这样它就可以被重用,而不必把站点作为一个表达式树,因为这意味着我们需要把很多repo层作为表达式树来做。但答案很接近。我不确定是否可以重新调整getcontactpoint表达式方法,以便只在contactpoints上使用,因为此时如何将select表达式传递给它们。我原以为如果我使用s.contactpoints.selectexp,它只会对单个联系人有效,而不会对整个列表有效。@Jon,请参阅我的更新,我创建了另一个扩展,以便可以重用联系人并将其重新投影到另一个类谢谢。那太好了,工作起来很有魅力。。我已经编辑了你的答案,向你展示了我们写的一个助手,如果有人遇到这个问题,我们会把它再总结一下。谢谢again@Jon,请随意改进它,如果您发现类似的问题,您可以编写一个普通的linq查询,然后使用查看生成的表达式,您只需对表达式进行反向工程即可。很好的答案,关于表达式树可视化器的说明也很有帮助。
private static Expression<Func<Site, SiteLabelDto>> GetSite(ParameterExpression siteParam, ParameterExpression cpeParam)
{
    var newExp = Expression.New(typeof(SiteLabelDto));
    var initExp = Expression.MemberInit(
        newExp,
        Expression.Bind(typeof(SiteLabelDto).GetProperty("Id"), Expression.Lambda<Func<Site, int>>(Expression.Property(siteParam, "Id"), siteParam).Body),
        Expression.Bind(typeof(SiteLabelDto).GetProperty("Name"), Expression.Lambda<Func<Site, string>>(Expression.Property(siteParam, "Name"), siteParam).Body),
        /* other basic information */
        Expression.Bind(typeof(SiteLabelDto).GetProperty("Email"), GetContactPoint(siteParam, cpeParam, ContactPointType.Email).Body),
        Expression.Bind(typeof(SiteLabelDto).GetProperty("Phone"), GetContactPoint(siteParam, cpeParam, ContactPointType.Phone).Body)
        /* other types */
    );
    var selector = Expression.Lambda<Func<Site, SiteLabelDto>>(initExp, siteParam);
    return selector;
}
var siteParam = Expression.Parameter(typeof(Site), "s");
var cpeParam = Expression.Parameter(typeof(ContactPointEntity), "cpe");
var selector = GetSite(siteParam, cpeParam);
return Context.Sites
    .Where(s => !s.IsDeleted && s.Vendor.Id == vendorId)
    .Select(selector)
    .ToArray();
public class ContactPointExt<T>
{
    public T Instance { get; set; }
    public string Email { get; set; }
    public string Phone { get; set; }
}
private static Expression<Func<Site, ContactPointExt<T>>> GetContactPoints<T>(ParameterExpression siteParam, ParameterExpression cpeParam)
{
    var type = typeof(ContactPointExt<T>);
    var newExp = Expression.New(type);
    var initExp = Expression.MemberInit(
        newExp,
        Expression.Bind(type.GetProperty("Instance"), siteParam),
        Expression.Bind(type.GetProperty("Email"), GetContactPoint(siteParam, cpeParam, ContactPointType.Email).Body),
        Expression.Bind(type.GetProperty("Phone"), GetContactPoint(siteParam, cpeParam, ContactPointType.Phone).Body)
    );
    var selector = Expression.Lambda<Func<Site, ContactPointExt<T>>>(initExp, siteParam);
    return selector;
}
var siteParam = Expression.Parameter(typeof(Site), "s");
var cpeParam = Expression.Parameter(typeof(ContactPointEntity), "cpe");
var selector = GetContactPoints<Site>(siteParam, cpeParam);
return Context.Sites
    .Where(s => !s.IsDeleted && s.Vendor.Id == vendorId)
    .Select(selector)
    .Select(s => new SiteLabelDto
    {
        Id = s.Instance.Id,
        Name = s.Instance.Name,
        Email = s.Email,
        Phone = s.Phone
    })
    .ToArray();
    /// <summary>
    /// Wraps up a each of a query's objects in a ContactPointExt&lt;T&gt; object, providing the default contact point of each type.
    /// The original query object is accessed via the "Instance" property on each result.
    /// Assumes that the query object type has a property called ContactPoints - if different, supply the property name as the first argument.
    /// </summary>
    /// <typeparam name="T"></typeparam>
    /// <param name="query"></param>
    /// <param name="contactPointsPropertyName"></param>
    /// <returns></returns>
    public static IQueryable<ContactPointExt<T>> WithContactPointProcessing<T>(this IQueryable<T> query, string contactPointsPropertyName = "ContactPoints") where T : class
    {
        var siteParam = Expression.Parameter(typeof(T), "s");
        var cpeParam = Expression.Parameter(typeof(ContactPointEntity), "cpe");
        var selector = ContactPointHelpers.GetContactPoints<T>(siteParam, cpeParam, contactPointsPropertyName);
        return query.Select(selector);
    }