微软Odata API通过ViewModel进行PATCH操作存在问题

3

目标

我的目标是在实体产品中发送一些额外的(未定义的)属性。例如,在AngularJs列表视图中,我需要根据从会话获取的当前用户数据和productId计算的权限来显示某些产品作为链接(可访问),而其他产品则不能访问。

导致我出现这个问题的原因

现在,Odata不允许我在发送IQueryable结果时添加额外的属性,如下所示。

public IQueryable<Product> GET()
{
  return db.Products.AsQueryable<Product>();
}

仅仅因为返回类型是Product,添加额外的属性将会把它变成别的东西,如果我像这样尝试:

var result = db.Products.Select(p => new Product
{
  ID = p.ID,
  Accessible = (code for permission checks),
  [other properties...]
}

解决方案

我采用了一种解决方案,创建了一个新类型的OdataController,即OdataProduct,它具有我需要发送的已定义属性,以使列表权限生效。(您有没有其他方法使列表权限生效,除了我提出的这种解决方案?)

public class OdataProduct
{
    public Product product { get; set; }
    public OdataProduct(Product product)
    {
        this.product = product;
    }
    //
    public void setPermissions(User user, string controller)
    {
        if (user == null) return;
        isAllowed = [permission check code];
    }
    public bool isAllowed { get; set; }
}

我试图从Product继承OdataProduct,但在接收POST请求以保存到数据库时,将OdataProduct向下转型为Product时遇到了问题。Db.Products.Add();

现在,有了这个ViewModel和这种类型的控制器,我已成功发送了基于当前用户会话的权限感知结果,用于列表中所有产品。

 public class OdataProductController : ODataController
{
    private OfferAssistantDbContext db = new OfferAssistantDbContext();

    // GET api/OdataProduct
    public IQueryable<OdataProduct> GET()
    {
        var result = db.Products.AsQueryable<Product>();
        var OResult = new List<OdataProduct>();
        var currentUser = (User)HttpContext.Current.Session["LoggedUser"];
        OdataProduct OProduct;
        foreach (var item in result)
        {
            OProduct = new OdataProduct(item);
            OProduct.setPermissions(currentUser, "Product");
            OResult.Add(OProduct);
        }

        return OResult.AsQueryable<OdataProduct>();
   }
   //other methods of the controller below...
}

我想要解决的问题

  1. 当我向OdataProduct控制器发送PATCH请求时,我得到一个Delta对象,而不是Product。如果我在Payload中发送一个Product,并相应地修改PATCH方法的Odata参数以接收一个Product而不是OdataProduct,那么它将被接收为null,而在默认情况下,我无法运行这个命令对真正的Product实体进行而不是ViewModel。以下是:

    var dbProduct = db.Products.Find(key); oDataProduct.Patch((dbProduct); //OdataProduct is not of dbProduct type 有什么解决办法吗?

  2. 我面临的另一个问题是设置上面的OdataProduct的权限。

    OProduct.setPermissions(currentUser, "Product"); //进入这个方法后,异常行是 this.Childs = product.DependantProducts.Where(dp => dp.ParentID == product.ID).Count();

它说DataReader已经打开。虽然这不是主要问题,但请在此处提供一些信息。

1个回答

2
第一个问题可以这样解决,首先定义一个View Model ODataProduct,其中包含模型所需的属性以及IsAllowed:
public class ODataProduct
{
    public int ID { get; set; }
    public string Title { get; set; }
    public bool IsAllowed { get; set; }
}

然后创建一个新的实体集,其元素类型为 ODataProduct。

private static IEdmModel GetModel()
{
    ODataModelBuilder modelBuilder = new ODataConventionModelBuilder();
    var odataProductsEntitySet = modelBuilder.EntitySet<ODataProduct>("ODataProducts");
    return modelBuilder.GetEdmModel();
}

最后一步是更新ODataProductController来执行GET、POST、PATCH等操作。
public class ODataProductsController : ODataController
{
    private ProductsContext db = new ProductsContext();

    public IHttpActionResult Get()
    {
        var Products = db.Products.Select(m => new ODataProduct()
        {
            ID = m.ID,
            Title=m.Title,
            // Other properties
            IsAllowed = true,
        });
        return Ok( Products);
    }

    public ODataProduct Patch(int key, Delta<ODataProduct> odataProduct)
    {
        var dbProduct = db.Products.Single(m => m.ID == key);
        foreach (string propertyName in odataProduct.GetChangedPropertyNames())
        {
            if ("IsAllowed" == propertyName)
            {
                continue;
            }
            object propertyValue;
            if (odataProduct.TryGetPropertyValue(propertyName, out propertyValue))
            {
                var propertyInfo = typeof(Product).GetProperty(propertyName);
                propertyInfo.SetValue(dbProduct, propertyValue);
            }
        }
        db.SaveChanges();
        ODataProduct odataProductReturn = new ODataProduct()
        {
            ID = dbProduct.ID,
            Title=dbProduct.Title,
            // other properties
        };
        odataProductReturn.IsAllowed = true;// or false according to your business logic
        return odataProductReturn;
    }
}

所以你的想法是在现有的产品控制器上应用PATCH,并通过新的OdataProduct控制器提供权限感知列表。对吗? - ahmadalibaloch
不,我的意思是在ODataProductsController中执行GET/PATCH/PUT/POST操作。 - Tan Jinfu
如何使用Product更改ODataProduct视图模型。这是我在问题中提出的主要问题!!!!!! - ahmadalibaloch
谢谢您的回答,但我已经通过向我的原始产品实体添加一个未映射到数据库的属性(添加了[NotMapped]属性)来解决了这个问题。现在我不需要为此目的创建OdataProduct实体。请记住,您需要在modelBuilder中设置未映射的属性,如下所示:builder.EntitySet<Product>("Product").EntityType.Property(c => c.IsAccessible); 还需要初始化它以使其与LINQ to Queryable一起使用,如下所示 var result = db.Products.ToList().Where(p => p.IsAccessible == p.IsAccessible); 在您的控制器->GET中。 - ahmadalibaloch

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接