如何在Umbraco中创建虚拟节点

如何在Umbraco中创建虚拟节点,umbraco,umbraco7,Umbraco,Umbraco7,形势 我在Umbraco 7.3中创建了一个新的部分,可以在其中管理目录(创建、编辑和删除产品)。创建产品时,我将其所有信息存储在Umbraco数据库的表中。请注意,我没有在树中创建节点,我只是直接使用数据库 问题 例如,当我在该目录中创建“产品A”时,我会自动生成一个自定义URL“/products/Product-A”,并将其存储在数据库的表中。但是,该URL不存在,因为我没有物理节点: 找不到页面 没有与url“/products/product-A”匹配的umbraco文档 由于我没有物

形势

我在Umbraco 7.3中创建了一个新的部分,可以在其中管理目录(创建、编辑和删除产品)。创建产品时,我将其所有信息存储在Umbraco数据库的表中。请注意,我没有在树中创建节点,我只是直接使用数据库

问题

例如,当我在该目录中创建“产品A”时,我会自动生成一个自定义URL“/products/Product-A”,并将其存储在数据库的表中。但是,该URL不存在,因为我没有物理节点:

找不到页面

没有与url“/products/product-A”匹配的umbraco文档


由于我没有物理节点,如何“创建”虚拟节点并为该URL分配模板?

听起来您不需要由Umbraco处理产品URL

您需要在
RouteConfig.cs
文件中添加自定义路由,例如

routes.MapRoute(
  name: "Products",
  url: "Products/{id}",
  defaults: new { controller = "Products", action = "Product", id = UrlParameter.Optional }
);
然后创建一个
ProductsController

public class ProductsController : Controller
{
    public ActionResult Product(int id)
    {
        // Retrieve product from database
        // Return view
    }
如果您需要访问
UmbracoContext
,这里有一个很好的示例:

根据要点,是否需要创建VirtualProductNode?能否返回Umbraco页面(节点引用),然后使用该页面(模板/控制器)处理产品数据

protected override IPublishedContent FindContent(RequestContext requestContext, UmbracoContext umbracoContext)
{
    // Get the real Umbraco IPublishedContent instance as a reference point
    var nodeReference = new UmbracoHelper(umbracoContext).TypedContent(3286);  

     contentRequest.PublishedContent = nodeReference;

     return contentRequest.PublishedContent != null;
}
我们做了一些类似于列出存储在媒体部分的pdf文档页面的事情。pdf文档组织在映射到虚拟url结构的文件夹中。内容部分中没有页面

(我们不希望编辑上传所有pdf文档,然后进入内容部分,创建页面并再次选择pdf,这是不必要的额外工作)

有一个页面定义该节,其模板将根据url显示正确的PDF。该模板使用url在“媒体”部分中查找相应的文件夹,并列出pdf文件

public class ProductsContentFinder : IContentFinder
{
    public bool TryFindContent(PublishedContentRequest contentRequest)
    {
        if (contentRequest != null)
        {
            // url: /store/products/product-name
            var path = contentRequest.Uri.GetAbsolutePathDecoded();
            var parts = path.Split(new[] { '/' }, System.StringSplitOptions.RemoveEmptyEntries);
            if (parts.Length > 1)
            {
                var level1 = string.Concat('/', string.Join("/", parts.Take(2)), '/');
                var node = contentRequest.RoutingContext.UmbracoContext.ContentCache.GetByRoute(level1);
                 //find product page
                 var products = node.AncestorOrSelf("Products");

                 if (products != null)
                 {
                     contentRequest.PublishedContent = products;
                 }
             }
         }

         return contentRequest.PublishedContent != null;
     }
 }

我最终会明白的。谢谢你的回答

1.为我们的虚拟页面创建一个类 第一件事是创建一个类,即
ProductVirtualPage
,它继承自
publishedContentRapped
。最后一个类为包装和扩展另一个
IPublishedContent
IPublishedContent
实现提供了一个抽象基类。我们将使用属性产品从Razor视图中获取
产品
,并进行渲染

public class ProductVirtualPage : PublishedContentWrapped
{   
  private readonly Product _product;

  public ProductVirtualPage(IPublishedContent content, Product product ) : base(content)
  {
     if (product.Name == null) throw new ArgumentNullException("productName");
     _product = product;
  }

   public Product Product
   {
       get
       {
           return _product;
       }
   }
 }
2.基于
umbracoVirtualNodeHandler创建我们的处理程序
在树中,我们需要创建一个引用节点,在我的例子中,它的id是3286。调用
FindContent()
方法时,我们将把它传递给
ProductVirtualPage(noderReference,product)
方法

public class ProductVirtualNodeRouteHandler : UmbracoVirtualNodeRouteHandler
{
    private ProductService _productService;

    protected override IPublishedContent FindContent(RequestContext requestContext, UmbracoContext umbracoContext)
    {
        // Get the real Umbraco IPublishedContent instance as a reference point
        var nodeReference = new UmbracoHelper(umbracoContext).TypedContent(3286);

        //Get the product Id from the URL (http://example.com/Products/product/57)
        var productId = umbracoContext.HttpContext.Request.Url.Segments[3];

        // Create an instance for the ProductService
        _productService = new ProductService(new ProductRepository(), new SiteRepository());

        // Get the product from the database
        var product = _productService.GetById(Convert.ToInt32(productId));

        // Get the virtual product
        return  new ProductVirtualPage(nodeReference, product);
    }
}
3.注册我们的定制路线 我们需要使用
MapUmbracoRoute
添加自定义路由,并将其绑定到
ApplicationStarted
方法,该方法提供
ApplicationEventHandler

public class ContentServiceEventsController : ApplicationEventHandler
{

    protected override void ApplicationStarted(UmbracoApplicationBase umbracoApplication,
        ApplicationContext applicationContext)
    {
        // Add a route for the Products
        RouteTable.Routes.MapUmbracoRoute(
            "ProductPage",
            "Products/Product/{id}/",
            new
            {
                controller = "Product",
                action = "GetProduct"
            },
            new ProductVirtualNodeRouteHandler());
    }
}
4.创建控制器 控制器将只返回视图,并传递我们的
RenderModel模型

public class ProductController : RenderMvcController
{
    public ActionResult GetProduct(RenderModel model, int id)
    {
        return View("Product", model);
    }
}
5.景色
@{
Layout=“~/Views/Shared/_MasterLayout.cshtml”;
var productModel=Model.Content.Product;
var product=产品模型作为产品;
}
产品信息
---------------
Id:@product.Id
名称:@product.Name

首先,非常感谢您抽出时间为我指明了正确的方向。现在的问题是,我的产品视图(需要传递
产品
模型)继承了共享布局视图(需要接收
渲染模型
模型),这导致了冲突。请查看以下要点,您可以在注释中看到更详细的解释:我无法实现代码中出现的
FindContent
方法。基于方法签名的返回类型为
ippublishedcontent
,但是在代码中返回bool。感谢共享@Filipe,您知道您是否可以劫持umbraco中的路由,从而使您的产品显示为umbraco节点的子节点吗?您是否可以访问父节点和路径属性?你能在
ProductVirtualPage
类的某个地方设置这些吗?
@{
  Layout = "~/Views/Shared/_MasterLayout.cshtml";
  var productModel = Model.Content.Product;
  var product = productModel as Product; 
}
<pre>
  Product Info
  ---------------

  Id: @product.Id
  Name: @product.Name
</pre>