I'm using Glass V4. I have a set up of MVC Web Area Project.
I have installed the Glass Mapper in the Main Project (WebProject).
I'm trying to do the Glass Casting in my Area Project.
public class ContactController : SitecoreController
{
private readonly ISitecoreContext _context;
private IGlassHtml _glassHtml;
public ContactController()
: this(new SitecoreContext())
{
}
public ContactController(ISitecoreContext context)
{
_context = context;
_glassHtml = new GlassHtml(context);
}
// GET: Contact
public ActionResult ContactUs()
{
var db = Sitecore.Context.Database;
var datasource = db.GetItem(RenderingContext.Current.Rendering.DataSource);
var ViewModel = new Models.ContactUs();
ViewModel.Headerstring = datasource.Fields["Headerstring"].Value;
ViewModel.Substring = datasource.Fields["Substring"].Value;
ViewModel.Description = ((MultilistField)datasource.Fields["Description"]).GetItems().Select(s => s.Fields["Line"].Value).ToList<string>();
return View(ViewModel);
}
public ActionResult ContactUsGlass()
{
var model = _context.GetCurrentItem<ContactUsGlassModel>();
return View(model);
}
}
I'm able to get the value with the First Action Method but not with the second.
Model:
public class ContactUs
{
public string Headerstring { get; set; }
public string Substring { get; set; }
public List<string> Description { get; set; }
}
Glass Model:
public class ContactUsGlassModel
{
public virtual string Headerstring { get; set; }
public virtual string Substring { get; set; }
}
I understand I don't need to register my Namespace in Glass V4.
You should not use _context.GetCurrentItem method. Use _context.GetItem instead:
public ActionResult ContactUsGlass()
{
var model = context.GetItem<ContactUsGlassModel>(RenderingContext.Current.Rendering.DataSource);
return View(model);
}
You don't want to get model from your Sitecore.Context.Item (which is used in GetCurrentItem method. You want to get your model from the DataSource of the current rendering.
What #Marek has answered is the right way of pulling the rendering item into model. GetCurrentItem by default gives the page item being served by Sitecore. If the fields that your model needs are fields of your page item then GetCurrentItem can also fill your model. If Datasource nesting is enabled, then if the datasource is not set on the rendering, Sitecore returns the page item again.
You can inherit from GlassController and then use GetLayoutItem() to get the datasorced item. If it's null then you need to publish the template in sitecore and make sure you mappings are correct if you are not using TDS :)
Related
I can't verify and test my database by in memory providers.
for example I set these properties to required :
public abstract class Log
{
#region Properties
public Guid Id { get; set; }
[Required]
public string ClientIp { get; set; }
[Required]
public string Application { get; set; }
[Required]
public string Host { get; set; }
[Required]
public string Path { get; set; }
[Required]
public string Method { get; set; }
[Required]
public string User { get; set; }
[Required]
public string Date { get; set; }
#endregion
}
and this is my DBContext :
public class ApplicationDbContext : IdentityDbContext<ApplicationUsers, Role, Guid>, IUnitOfWork
{
private readonly IConfigurationRoot _configuration;
public ApplicationDbContext(IConfigurationRoot configuration)
{
_configuration = configuration;
}
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
var useInMemoryDatabase = _configuration[key: "UseInMemoryDatabase"].Equals(value: "true",
comparisonType: StringComparison.OrdinalIgnoreCase);
if (useInMemoryDatabase)
optionsBuilder.UseInMemoryDatabase();
else
optionsBuilder.UseSqlServer(
connectionString: _configuration[key: "ConnectionStrings:ApplicationDbContextConnection"]
, sqlServerOptionsAction: serverDbContextOptionsBuilder =>
{
var minutes = (int) TimeSpan.FromMinutes(3).TotalSeconds;
serverDbContextOptionsBuilder.CommandTimeout(commandTimeout: minutes);
});
}
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
modelBuilder.Entity<Log>()
.HasKey(c => c.Id);
modelBuilder.Entity<Log>()
.HasDiscriminator<int>(name: "Type")
.HasValue<LogRequest>(value: Convert.ToInt32(value: LogLevel.Information))
.HasValue<LogError>(value: Convert.ToInt32(value: LogLevel.Error));
}
And this is my unit test :
[TestClass]
public class LogRepositoryTest
{
private readonly IServiceProvider _serviceProvider;
public LogRepositoryTest()
{
var services = new ServiceCollection();
services.AddScoped<IUnitOfWork, ApplicationDbContext>();
services.AddScoped<ILogRepository, LogRepository>();
services.AddSingleton(provider => new ConfigurationBuilder()
.AddInMemoryCollection(initialData: new[]
{
new KeyValuePair<string, string>(key: "UseInMemoryDatabase", value: "true"),
})
.Build());
services.AddEntityFrameworkInMemoryDatabase().AddDbContext<ApplicationDbContext>(ServiceLifetime.Scoped);
_serviceProvider = services.BuildServiceProvider();
}
[TestMethod]
public async Task Verify_SaveRequestLog()
{
using (var serviceScope = _serviceProvider.GetRequiredService<IServiceScopeFactory>().CreateScope())
{
using (var context = serviceScope.ServiceProvider.GetRequiredService<IUnitOfWork>())
{
context.Set<Log>().Add(new LogRequest());
var result =await context.SaveAllChangesAsync();
Assert.AreEqual(1, result);
}
}
}
But the unit test method always return 1 and passes, meanwhile the empty object of LogRequest must not save anything to database!
How can I determine not null properties for unit test ? In fact how can I enforce unit test to reflect to validation policies ?
Update:
Based on this linke :
Entity Framework Core Issues
that I asked, I got this respond:
EF Core doesn't do any validation of entities beyond what is needed
for internal consistency. Validation is something that could be done
in EF, but experience shows that it is not something that is useful to
many developers because it usually cannot replace either client-side
validation or database validation and there are also other places
where validation can be done more effectively.
Going beyond EF to the database, the in-memory database does not
currently validate nullability (i.e. requiredness) when saving
property values. I will leave this issue open so that we can discuss
as a team whether this is something we should add.
Also, if the intent is test with an in-memory database as an
approximation for a relational database, then you might want to
consider using SQLite in in-memory mode. See
https://learn.microsoft.com/en-us/ef/core/miscellaneous/testing/index
for more information.
Based on this linke :
Entity Framework Core Issues
that I asked, I got my answer :
class MyContext : DbContext
{
public override int SaveChanges()
{
var entities = from e in ChangeTracker.Entries()
where e.State == EntityState.Added
|| e.State == EntityState.Modified
select e.Entity;
foreach (var entity in entities)
{
var validationContext = new ValidationContext(entity);
Validator.ValidateObject(entity, validationContext);
}
return base.SaveChanges();
}
}
Hi I'm currently using Sitecore 8.1 update 1 with MVC5.2.3 and Glass Mapper. I'm having some issues with the Glass Mapper link field. I have created a template which is derived from Standard Rendering Parameters template in which I have used Sitecore General Link field. Also I have created Model for that:
My model:
[SitecoreType(TemplateId = "{912B074D-F8BA-4AA7-9276-016515A1ACE8}")]
public class RelatedArticleParams
{
[SitecoreId]
public virtual Guid Id { get; set; }
public virtual string HeaderText { get; set; }
[SitecoreField(FieldType = SitecoreFieldType.GeneralLink)]
public Link Link { get; set; }
}
My View:
#{
var parameters = GetRenderingParameters<RelatedArticleParams>();
}
#parameters.Text
Everything is fine if I add the link from presentation details at Sitecore backend. But when I click on this component at Sitecore Page Experience Editor and insert link to Rendering Parameters, then it will give An error occurred red line indication at top of the page. I cant insert link from Page Editor mode.
Please help me in this issue whether it is Glass Mapper bug or I am making any mistake ??
Thanks.
Will appreciate your suggestions.
Another option is to do it in code.
public class MyViewModel
{
public HtmlString MyLink { get; set; }
}
public class MyController : Controller
{
private readonly IGlassHtml _glassHtmlHelper;
public void MyController()
{
_glassHtmlHelper = new GlassHtml(new SitecoreContext());
}
public ViewResult MyControllerAction()
{
var viewModel = new MyViewModel();
//Get your item
viewModel.MyLink = new HtmlString(_glassHtmlHelper.RenderLink<RelatedArticleParams>(contentItem, x => x.Link, isEditable: true));
return View(viewModel);
}
}
Then, in your markup, all you have to do is:
#model MyViewModel
<div>
#Model.MyLink
</div>
You should use
#Editable(Property name of glass mapper) //using the Model property.
Reference
use
#RenderLink(x => x.Link)
or
#using (BeginRenderLink(x => x.GeneralLink, isEditable: true))
{
#RenderImage(x => x.Image)
}
http://glass.lu/Mapper/Sc/Tutorials/Tutorial22
I have created a bunch of custom templates to store items (such as Industries, Subindustries, etc.) in Sitecore. I now want to go about loading these into my Sitecore MVC model.
The lists are located in sitecore > Content > Lists. For example inside the Lists folder there is a folder called Country. I want to get back all the items within the Country folder and populate them as unordered list in my view.
UPDATE: I implemented the Glass.Mapper.Sc method suggested below. It is fully operational now.
This is what my working model looks like now:
using Glass.Mapper.Sc.Configuration;
using Glass.Mapper.Sc.Configuration.Attributes;
using Sitecore.Data.Items;
using Sitecore.Mvc.Presentation;
using System;
using System.Collections.Generic;
namespace Sitecore.Web.Models
{
public class Registration: IRenderingModel
{
public Rendering Rendering { get; set; }
public Item Item { get; set; }
public Item PageItem { get; set; }
public IEnumerable<CountryChildItem> CountryList { get; set; }
[SitecoreType(AutoMap = true)]
public class CountryItem
{
public virtual IEnumerable<CountryChildItem> Children { get; set; }
}
[SitecoreType(AutoMap = true)]
public class CountryChildItem
{
[SitecoreId]
public virtual Guid Id { get; set; }
[SitecoreInfo(SitecoreInfoType.Path)]
public virtual string Path { get; set; }
[SitecoreField]
public virtual string DisplayName { get; set; }
[SitecoreField]
public virtual string Abbreviation { get; set; }
}
public void Initialize(Rendering rendering)
{
Rendering = rendering;
Item = rendering.Item;
PageItem = PageContext.Current.Item;
}
}
}
and this is what my working contoller looks like:
using Glass.Mapper.Sc;
using Sitecore.Web.Models;
using System.Web.Mvc;
namespace Sitecore.Web.Controllers
{
public class RegistrationController : Controller
{
Registration registrationModel = new Registration();
public ActionResult Index()
{
ISitecoreContext sitecoreContext = new SitecoreContext();
ISitecoreService service = new SitecoreService(sitecoreContext.Database);
Registration.CountryItem countryItem = service.GetItem<Registration.CountryItem>("/sitecore/content/Lists/Country");
registrationModel.CountryList = countryItem.Children;
return View(registrationModel);
}
}
}
and a snippet of my working view:
<ul class="select-menu-options dropdown-menu">
#foreach (var country in Model.CountryList)
{
<li>#country.DisplayName</li>
}
</ul>
If I were in your position I'd look into Glassmapper for Sitecore.
It's a fairly lightweight ORM for Sitecore.
http://www.glass.lu/Mapper/Sc
I'd also suggest moving the lists located in
sitecore > Templates > User Defined > Lists > Content
to some where under either
sitecore > Content
or
sitecore > System
(whichever makes more sence)
UPDATE:
Try adding this above your class:
[SitecoreType(AutoMap = true)]
public class CountryItem
{
//...
}
If you change your CountryItem and other model classes to inherit from SearchResultItem like that:
[PredefinedQuery("TemplateID", ComparisonType.Equal, "{ID-OF-CountryItem-TEMPLATE}", typeof(ID))]
public class CountryItem : Sitecore.ContentSearch.SearchTypes.SearchResultItem
{
[IndexField("_displayname")]
public virtual string DisplayName { get; set; }
[IndexField("abbreviation")]
public string Abbreviation { get; set; }
}
You should be able to use Sitecore indexes to retrieve all the countries and other lists like that:
private static string IndexName
{
get
{
return string.Format("sitecore_{0}_index", (Context.ContentDatabase ?? Context.Database).Name);
}
}
private static string Language { get { return Context.Language.Name; } }
public IEnumerable<CountryItem> GetCountries()
{
using (var context = ContentSearchManager.GetIndex(IndexName).CreateSearchContext())
{
IQueryable<CountryItem> queryable = context.GetQueryable<CountryItem>();
queryable = queryable.Where(i => i.Language == Language);
queryable = queryable.Where(i => i.LatestVersion);
// ... maybe excluding standard values or some other filters
var searchResults = queryable.GetResults();
return queryable.ToList();
}
}
Please be aware that this is just an example. You need to test it and most probably adapt to your solution.
And as Dar Brett mentioned, you should not keep any data items under the Templates node.
So I'm working with Umbraco 6.12 and having great difficulty been able to test a RenderMvcController.
I have implemented IApplicationEventHandler in my Global.ascx and Ninject is working fine and as expected when running the application - all good.
However, unit testing these controllers is a different matter. I found this, and have added the latest reply:
http://issues.umbraco.org/issue/U4-1717
I now have this lovely hack in my SetUp:
Umbraco.Web.UmbracoContext.EnsureContext(new HttpContextWrapper(new HttpContext(new HttpRequest("", "http://www.myserver.com", ""), new HttpResponse(null))), ApplicationContext.Current);
Which has got around the original UmbracoContext cannot be null, but is now throwing:
Current has not been initialized on Umbraco.Web.PublishedCache.PublishedCachesResolver. You must initialize Current before trying to read it.
The published caches resolver also seems to be hidden behind internal and protected stuff, which I can't use reflection to hack at as I can't init anything to pass into SetProperty reflection.
It's really frustrating, I'm loving v6, and using uMapper is very nice. I can inject a repo, service, command or query at will into the controllers and life is good - I just can't cover the controllers!
Any help on this would be greatly appreciated.
Thanks.
To unit test a Umbraco RenderMvcController, you need to grab the source code from github, compile the solution yourself, and get the Umbraco.Tests.dll and reference it on your test project.
In addition to that, you need to reference the SQLCE4Umbraco.dll which is distributed with the Umbraco packages, and Rhino.Mocks.dll which is internally for mocking.
To help you with this, I have compiled put the Umbraco.Tests.dll for Umbraco 6.1.5 and put it together with the Rhino.Mocks.dll and put it on this zip file.
Finally, derive your test from BaseRoutingTest, override the DatabaseTestBehavior to
NoDatabasePerFixture, and get the UmbracoContext and HttpBaseContext by calling the GetRoutingContext method, as in the code below:
using System;
using Moq;
using NUnit.Framework;
using System.Globalization;
using System.Web.Mvc;
using System.Web.Routing;
using Umbraco.Core.Models;
using Umbraco.Tests.TestHelpers;
using Umbraco.Web;
using Umbraco.Web.Models;
using Umbraco.Web.Mvc;
namespace UnitTests.Controllers
{
public class Entry
{
public int Id { get; set; }
public string Url { get; set; }
public string Title { get; set; }
public string Summary { get; set; }
public string Content { get; set; }
public string Author { get; set; }
public string[] Tags { get; set; }
public DateTime Date { get; set; }
}
public interface IBlogService
{
Entry GetBlogEntry(int id);
}
public class BlogEntryController : RenderMvcController
{
private readonly IBlogService _blogService;
public BlogEntryController(IBlogService blogService, UmbracoContext ctx)
: base(ctx)
{
_blogService = blogService;
}
public BlogEntryController(IBlogService blogService)
: this(blogService, UmbracoContext.Current)
{
}
public override ActionResult Index(RenderModel model)
{
var entry = _blogService.GetBlogEntry(model.Content.Id);
// Test will fail if we return CurrentTemplate(model) as is expecting
// the action from ControllerContext.RouteData.Values["action"]
return View("BlogEntry", entry);
}
}
[TestFixture]
public class RenderMvcControllerTests : BaseRoutingTest
{
protected override DatabaseBehavior DatabaseTestBehavior
{
get { return DatabaseBehavior.NoDatabasePerFixture; }
}
[Test]
public void CanGetIndex()
{
const int id = 1234;
var content = new Mock<IPublishedContent>();
content.Setup(c => c.Id).Returns(id);
var model = new RenderModel(content.Object, CultureInfo.InvariantCulture);
var blogService = new Mock<IBlogService>();
var entry = new Entry { Id = id };
blogService.Setup(s => s.GetBlogEntry(id)).Returns(entry);
var controller = GetBlogEntryController(blogService.Object);
var result = (ViewResult)controller.Index(model);
blogService.Verify(s => s.GetBlogEntry(id), Times.Once());
Assert.IsNotNull(result);
Assert.IsAssignableFrom<Entry>(result.Model);
}
private BlogEntryController GetBlogEntryController(IBlogService blogService)
{
var routingContext = GetRoutingContext("/test");
var umbracoContext = routingContext.UmbracoContext;
var contextBase = umbracoContext.HttpContext;
var controller = new BlogEntryController(blogService, umbracoContext);
controller.ControllerContext = new ControllerContext(contextBase, new RouteData(), controller);
controller.Url = new UrlHelper(new RequestContext(contextBase, new RouteData()), new RouteCollection());
return controller;
}
}
}
This code has only been tested in Umbraco 6.1.5.
According to the core team, you should include the Umbraco.Tests library and inherit your test from BaseUmbracoApplicationTest. That will setup a valid UmbracoApplication and UmbracoContext.
https://groups.google.com/forum/?fromgroups=#!topic/umbraco-dev/vEjdzjqmtsU
I have raised this on the Umbraco forums and there are several replies which may help you.
See here:
http://our.umbraco.org/forum/developers/api-questions/37255-How-can-I-unit-test-a-class-inheriting-from-SurfaceController
Essentially, you can .. just ... but requires some reflection because some of the key classes and interfaces are internal. As Luke's last post points out, this is because the functionality is currently a bit of a moving target.
I am trying to use the SimpleRepository feature in Subsonic3 - first of all, I must say a big thanks to RobC - Subsonic really rocks, and I can't wait to see additional updates to the SimpleRepository. I am a big fan of the migration approach (developer/class driven rather than starting with the DB).
I have had a look at the post here:
Parent and Child object in SimpleRepository
but I am still a bit confused.
If I have got these classes defined:
public class Permit {
public int PermitID {get; set;}
public string Number { get; set; }
public DateTime? DateIssued { get; set; }
public Product product { get; set; }
}
public class Product
{
public int ProductID { get; set; }
public string Value { get; set; }
}
and then I want to save the data for a Permit, what should I be doing? Should I have defined a ProductID in the Permit class, and then programatically link them up? Or should the below code work?
var repo = new SimpleRepository("ECPermit", SimpleRepositoryOptions.RunMigrations);
var permit = new Permit();
var product = new Product();
permit.Number = "apermit";
permit.DateAdded = DateTime.Now;
product.Value = "this is a product";
repo.Add(permit);
permit.product = product;
repo.Add(product);
This is creating the Permit and Product table, but no links between them. What am I doing wrong?
Thanks
What you need to be aware of here is that the relationships are created by populating foreign keyvalues. So what you're doing in your example is creating a permit and saving it then setting the ProductID of the permit (but not saving this information), then saving the product. If you reorder your code as follows the ProductID will be set correctly:
var repo = new SimpleRepository("ECPermit", SimpleRepositoryOptions.RunMigrations);
var permit = new Permit();
var product = new Product();
product.Value = "this is a product";
repo.Add(product);
permit.Number = "apermit";
permit.DateAdded = DateTime.Now;
permit.ProductId = product.Id;
repo.Add(permit);