【问题标题】:How to incorporate ASP.NET Web API ODATA into ActiveRecord/NHibernate如何将 ASP.NET Web API ODATA 合并到 ActiveRecord/NHibernate
【发布时间】:2013-05-08 23:27:07
【问题描述】:

在 ASP.NET Web API 中,它允许您在 url 字符串中编写 ODATA 查询,以指定要从方法返回的数据。然而,我很难理解的部分是 ODATA 用于过滤 C# 对象的 IQueryable 集合,而不是数据库表本身。

这是不切实际的,因为您确实希望在数据库级别进行过滤,因为从数据库返回所有对象,将它们加载到 C# IQueryable 列表中,然后让 ODATA 过滤该列表是很糟糕的。

下面是代码,它使用 NHibernate 和 Castle Active Record 进行数据访问:

public IQueryable<Message> GetAll()
{
    return from m in MessageData.FindAllQueryable()
           select ConvertToView(m);
}

public static IQueryable<Message> FindAllQueryable()
{
    var criteria = DetachedCriteria.For<Message>()
        .CreateAlias("MessageRecipients", "mr")
        .AddOrder(new Order("Id", false));
    return ActiveRecordMediator<Message>.FindAll(criteria).AsQueryable();
}

这段代码的最终结果将是它从数据库中返回所有消息。如何允许 ODATA 对数据库本身执行过滤器?否则 ODATA 的整个概念对于现实世界的情况是完全不切实际的。

【问题讨论】:

标签: nhibernate castle-activerecord asp.net-mvc-4 asp.net-web-api


【解决方案1】:

请记住,您处理的是 IQueryable,它不是对象的物理列表。这是一个具有潜在要执行并产生实体列表的查询。这些查询也可以链接在一起:

var query = customRepository.Where(x => x.CustomerName == "John"); //no results generated
var query2 = query.Where(x => x.Salary > 100000); // still no results generated
var results = query2.ToList();  // now results are generated

您应该能够从 NHibernate 返回一个 Session.Query 的实例。然后,OData 功能将根据 URL 链接附加条件,然后枚举结果。

【讨论】:

  • 我知道在处理 iqueryable 时它何时访问数据库,但是在这种情况下(使用 asp.net web api、odata 和活动记录)使用您建议它调用的方法首先数据库并返回表中的所有记录,然后 odata 对 c# 列表执行并对其进行过滤。我猜想知道如何在 nhibernate/活动记录中执行 odata 命令还不够聪明,因此是 nhibernate.odata 项目。不过还是谢谢你的建议。
【解决方案2】:

这就是我最终让它工作的方式。非常感谢 Andreas 将我带到 NHibernate.OData。

在我的控制器操作中,我从 url 获取 odata 并将其传递给我的数据访问函数:

public IQueryable<Message> GetAll(int authUserId, int userId, DateTime? startDate, DateTime? endDate)
        {
            LogWriter.Write(String.Format("Getting all messages for user {0}", userId));

            //get messages and convert to view.
            return from m in MessageData.FindAll(userId, startDate, endDate, GetOData())
                    select new Message(m);
        }

    protected string GetOData()
            {
                var odata = this.Request.RequestUri.Query;
                odata = odata.Substring(odata.IndexOf("$"), odata.Length - odata.IndexOf("$"));
                odata = odata.Replace("%20", " ");
                return odata;
            }

在数据访问方法内部,我们得到NHibernate session,调用session.ODataQuery:

 public static IQueryable<Message> FindAll(int userId, DateTime? startDate, DateTime? endDate, string odata)
            {
                ICriteria query = GetSession().ODataQuery<Message>(odata);
                var detachedCriteria = new ConvertedDetachedCriteria(query)
                    .CreateAlias("MessageRecipients", "mr")
                    .Add(Restrictions.Or(
                        Restrictions.Eq("SenderUserId", userId),
                        Restrictions.Eq("mr.Key.RecipientId", userId)
                    ));
                return FindAllQueryable(detachedCriteria);
            }

    public static ISession GetSession()
            {
                var factory = ActiveRecordMediator.GetSessionFactoryHolder().GetSessionFactories()[0];
                return factory.OpenSession();
            }

public static IQueryable<T> FindAllQueryable(DetachedCriteria criteria)
        {
            return ActiveRecordMediator<T>.FindAll(criteria).AsQueryable();
        }

此外,需要一个简单的 ConvertedDetachedCriteria 类来从 ICriteria 转换为 DetachedCriteria。

public class ConvertedDetachedCriteria : DetachedCriteria
    {
        public ConvertedDetachedCriteria(ICriteria criteria)
            : base((CriteriaImpl)criteria, criteria)
        {
            var impl = (CriteriaImpl)criteria;
            impl.Session = null;
        }
    }

希望这对其他人有所帮助。我现在可以针对我的 asp.net web api 方法编写 odata 查询,并让它们在 db 级别进行过滤,这比过滤 iqueryable c# 对象更有用!

【讨论】:

  • 嗨@贾斯汀,我有点困惑。你可以帮帮我吗?在我的情况下,我使用ODataController 和 webapi,在this link 上发布了这个问题。谢谢!
【解决方案3】:

添加到@Justin 的答案,如果没有 ActiveRecord,可能只会返回类似的东西

public IQueryable<Message> Get()
{
    ICriteria query = _unitOfWork.CurrentSession.ODataQuery<Message>(GetOData());
    return query.Future<Location>().AsQueryable<Location>();
}

// Taken from @Justin's answer
protected string GetOData()
{
    var odata = this.Request.RequestUri.Query;
    odata = odata.Substring(odata.IndexOf("$"), odata.Length - odata.IndexOf("$"));
    odata = odata.Replace("%20", " ");
    return odata;
}

这应该让你接受测试!

【讨论】:

    猜你喜欢
    • 2013-03-07
    • 1970-01-01
    • 2015-08-24
    • 2013-09-12
    • 2016-03-17
    • 2014-05-12
    • 2012-10-28
    • 2012-06-20
    • 2012-03-11
    相关资源
    最近更新 更多