【问题标题】:Is there a way to access a private property in a where clause?有没有办法在 where 子句中访问私有属性?
【发布时间】:2015-06-14 09:09:47
【问题描述】:

我正在尝试将我的域模型直接映射到 EF。为此,我在模型中引入了一个私有属性,如下所示:

private ICollection<Tag> TagsInternal { get; set; }

public Article(Guid id, ... , IEnumerable<Tag> tags) : base(id)
{
   ...
   this.TagsInternal = new List<Tag>(tags.Where(i => i != null));
}

public IEnumerable<Tag> Tags { get { return this.TagsInternal.AsEnumerable(); } }

为了让 EF 访问“支持属性”,我添加了一些扩展方法:

public static class FluentApiExtensions
{
   public static ManyNavigationPropertyConfiguration<TEntityType, TTargetEntityType> 
      HasMany<TEntityType, TTargetEntityType>(this EntityTypeConfiguration<TEntityType> mapper,
      string propertyName)
      where TEntityType : class
      where TTargetEntityType : class
   {
      var lambda = GetLambdaExpression<TEntityType>(propertyName);

      return mapper
         .HasMany((Expression<Func<TEntityType, ICollection<TTargetEntityType>>>)lambda);
   }

   public static ManyToManyNavigationPropertyConfiguration<TEntityType, TTargetEntityType> 
      WithMany<TEntityType, TTargetEntityType>(this ManyNavigationPropertyConfiguration<TEntityType, TTargetEntityType> mapper,
      string fieldName)
      where TEntityType : class
      where TTargetEntityType : class
   {
      var lambda = GetLambdaExpression<TTargetEntityType>(fieldName);

      return mapper
         .WithMany((Expression<Func<TTargetEntityType, ICollection<TEntityType>>>)lambda);
   }

   private static LambdaExpression GetLambdaExpression<T>(string propertyName)
   {
      var type = typeof (T);
      var parameterExpression = Expression.Parameter(type, "type");
      var expression = (Expression)parameterExpression;

      var propertyInfo = type
         .GetProperty(propertyName, BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance);

      if (propertyInfo == null)
         throw new ArgumentException(string.Format("There is no property named '{0}' on type '{1}'.",
            propertyName, type.Name));

      var propertyExpression = Expression.Property(expression, propertyInfo);

      return Expression.Lambda(propertyExpression, parameterExpression);
   }
}

然后我可以选择这样的导航属性:

public Maybe<Article> GetArticle(Guid articleId)
{
   articleId.MustNotBeNull();

   var article = this.unitOfWork.Context.Articles
      .Include("TagsInternal")
      .FirstOrDefault(a => a.Id == articleId);

   return article == null
      ? new Maybe<Article>()
      : new Maybe<Article>(article);
}

但是,例如,当尝试执行在 where 子句中包含“标签”的查询时,整个事情就会崩溃

var test = this.unitOfWork.Context.Articles
   .Where(a => a.Tags.Count() > 0);

消息是

LINQ to Entities 不支持指定的类型成员“标签”。仅支持初始化程序、实体成员和实体导航属性。

有什么办法可以解决这个问题吗?我正在认真考虑仅将 EF 排除在模型之外并进行映射,或者按照 Vaughn Vernon 的建议沿着状态对象路径进行。

【问题讨论】:

    标签: c# linq entity-framework domain-driven-design


    【解决方案1】:

    您说您将域模型映射到 EF。因此,考虑到这一点,您将希望使您的 EF 类尽可能简单。它们仅存在以保留您的域模型。这意味着:

    • 不要将方法放在 EF 类上。
    • 不要使用默认构造函数以外的任何东西。
    • 仅使用公共方法。
    • 手动将您的域类映射到 EF 类或使用类似 AutoMapper 的东西。

    此外,根据您上面的代码,您似乎拥有自己的 UnitOfWork 实现。这是不必要的,因为DbContext 工作单元。 (使用您的 DI 容器来管理 DbContext 会话。如果这是一个 Web 应用程序,您可能希望它们具有每个请求的生命周期。)

    我也不确定您为什么要返回上面的Maybe&lt;Article&gt;。如果没有找到,就返回null。在您的控制器中,如果GetArticle() 返回null,则从控制器返回HttpNotFound()

    如果执行上述操作,那应该会消除混乱并删除您上面的许多映射代码。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2011-04-15
      • 2012-03-04
      • 2022-08-23
      • 2022-08-02
      • 1970-01-01
      • 1970-01-01
      • 2016-10-18
      • 2010-09-07
      相关资源
      最近更新 更多