Using Sitecore 10.2 and Glass Mapper 5.8.180
After upgrade from Sitecore 8.2 and switching to above mentioned version of Glass Mapper our code stopped loading collection references. I am aware that in Glass Mapper now the lazy loading is turned on by default and I want to keep this setting.
Problem is with follows:
Having
[SitecoreType(TemplateId = "{...}", AutoMap = true)]
public interface IStatementContainer
{
IEnumerable<Statement> Statements { get; set; }
}
public class StatementContainer : IStatementContainer
{
public virtual IEnumerable<Statement> Statements { get; set; }
}
The following code
_mvcContext.SitecoreService.GetItem<StatementContainer>(datasource)
does not work properly. The Statements are empty.
For following code
_mvcContext.SitecoreService.GetItem<IStatementContainer>(datasource)
the Statements are being loaded properly.
Could you explain me the difference in behavior?
Our legacy code contains a lot of usages of concrete types rather than interfaces, changing all of them would be difficult.
Related
I am working on an MVC project and I added a model to my Model's Folder.
[ProjectName].web.Models.ProductAndClient
That same folder already has another model called 'UserAccount'
When I go to my controller, I can instantiate and use the model normally; I didn't have any issues accessing or seeing the model from the controller.
However, when I go to my view and try to use the model with Razor, it will only show the
[ProjectName].web.Models.UserAccount
option. It will not pull up ProductAndClient. I have other classes in the bll that I can access, as well. Is is just this one class that the View will not see.
I already tried the web config solution in this Stack Overflow solution. It didn't work. Again, the view can already see the model folder, it just wont see the one file.
I have also tried building, cleaning, and rebuilding the solution. I have tried shutting down and restarting Visual Studio. I have tried shutting down and restarting my computer. I have tried deleting and re-creating the class. And I have tried accessing the class from other views. And I triple checked everything says 'public'. None of them work.
As far as the exact 'error', when I type the
#model [projectName].web.Models.ProductAndClient
The 'ProductAndClient' part has a red squiggly under it. And it says that it does not exist in the namespace. I have used this syntax on several other pages in this project and other projects, so It must just be some random thing I did to make this not work.
namespace [projectName].web.Models
{
public class ProductAndClient
{
public ClientInv Client { get; set; } //used as a model for the UI
public List<ClientInv> Clients { get; set; } //collected info
public List<ProductCommon> Products { get; set; } //used to compare description and prices
public List<SelectListItem> ProductNames { get; set; } //used for drop down
}
}
using [projectName].web.Models;
namespace [projectName].web.Controllers
{
public class InvoiceController : Controller
{
public ActionResult Index()
{
//Variables
ProductCommon productCommon = new ProductCommon();
List<string> productNames_String = new List<string>();
ProductAndClient client = new ProductAndClient();
//Other code that does stuff goes here
client.Client = new ClientInv();
client.ProductNames = productNames;
client.Products = products;
return View(client);
}
#model [projectName].web.Models.ProductAndClient
If closing/opening the file doesn't help, try adding a using.
#using [projectName].web.Models
#model ProductAndClient
Or add your model namespace to your web.config
<pages>
<namespaces>
<add namespace="[projectName].web.Models" />
</namespaces>
</pages>
Lets say I have a class called FooController in which I have a property called Bar of type IBar (interface). I need to initialize Bar via MEF. However I need MEF to create only one instance of IBar type for the duration of the application (despite multiple calls to initialize it due to multiple requests) and make it available to all requests concurrently. Note that IBar implementations can be assumed thread safe.
i.e.
public interface IBar
{
string Method();
}
[Export(typeof(IBar))]
public class MyBar: IBar
{
public string dateTimeCreated;
public MyBar()
{
System.Threading.Thread.Sleep(1000);
dateTimeCreated = DateTime.Now.ToLongTimeString() + " ";
}
public string Method()
{
return dateTimeCreated;
}
}
public class FooController : ApiController
{
[Import(typeof(IBar), RequiredCreationPolicy = CreationPolicy.Shared)]
public IBar Bar { get; set; }
public FooController()
{
//Assume CompositionContainer.ComposeParts call here
}
public string Get()
{
return Bar.Method();
}
}
The problem is each time I call Get() on FooController, the returned time value changes. This means the MyBar object is being reinstantiated for each call. I basically need it to return the same value meaning I need to tell MEF to create only one instance of IBar in my application despite multiple requests.
Thanks in advance.
You need to specify the PartCreationPolicy attribute on your MyBar export. Like this:
[Export(typeof(IBar))]
[PartCreationPolicy(CreationPolicy.Shared)]
public class MyBar : IBar
{
// ...
}
That also means that you don't need to specify the creation policy on your import:
[Import]
public IBar Bar { get; set; }
The ASP.NET MVC integration of MEF interprets CreationPolicy.Any and CreationPolicy.Shared as single instance per HTTP request. You need to apply the ApplicationShared attribute to the part to share it between HTTP requests.
Update:
The ApplicationSharedAttribute can be found in the System.ComponentModel.Composition.Web.Mvc assembly. Unfortunately this is not distributed with Framework 4.5. It can be found at the Using MEF with ASP.NET MVC 3 Sample Code example in the lib folder. The drawback is that you will have to reference the composition assemblies found it that sample and not the latest ones.
If you do not want to do that then start with this very simple approach:
Add a CompositionContainer in your MvcApplication class as a public property.
On the MvcApplication constructor create the container and add some catalogs.
On the controller get the application from the HttpContext and use one of the GetExport/GetExportedValue/GetExportedValues methods of the CompositionContainer. No need to call ComposeParts on the container.
There are a lot of other approaches that are more elaborate but this should get you started.
Simple test project working from examples in Julia Lerman's EF Code First book. Not a single example works TPH, TPT or TPC when using the Map method, but fine without. Every other aspect of EF (I have a part developed application) appears to work okay. I'm using VS 2010/.NET 4 with all latest updates and I even resorted to repairing the VS installation today.
The following TPH example throws an InvalidOperationException - "Map was called more than once for type 'Child' and at least one of the calls didn't specify the target table name."
using System.Linq;
using System.Data.Entity;
namespace ConsoleApplication1
{
public class Program
{
static void Main(string[] args)
{
Database.SetInitializer(new DropCreateDatabaseIfModelChanges<Context>());
var children = new Context().Set<Child>().ToList();
}
}
public class Parent
{
public int Id { get; set; }
public string Name { get; set; }
}
public class Child : Parent
{
}
public class Context : DbContext
{
DbSet<Parent> Parents { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<Parent>()
.Map(m =>
{
m.Requires("EntityType").HasValue("Parent");
m.ToTable("Families");
})
.Map<Child>(m => m.Requires("EntityType").HasValue("Child"));
}
}
}
Another example, TPT this time, throws "The type 'Child' has already been mapped to table 'Children'. Specify all mapping aspects of a table in a single Map call."
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<Parent>()
.Map(m => m.ToTable("Families"))
.Map<Child>(m => m.ToTable("Children"));
}
The final example, TPC, throws similar "The type 'Child' has already been mapped to table 'Children'. Specify all mapping aspects of a table in a single Map call."
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<Parent>()
.Map(m => m.ToTable("Families"))
.Map<Child>(m =>
{
m.ToTable("Children");
m.MapInheritedProperties();
});
}
I've tried any number of other examples too with similar results. Either I'm missing something very basic or I suspect I have an installation/configuration problem. My PC also has VS2008 installed and a few months ago I did a lot of re-configuring SQL Server as I had 4 different versions installed. I'm now using 2008 R2 Express.
Has anybody seen similar or have any idea how I can trace/debug what's going on in EF?
Found the answer here...
Entity Framework 4.3 - TPH mapping and migration error
... This is a known issue with 4.3 and 4.3.1. (We found it too late to put the fix in 4.3.1.)
... In a nutshell, you used to be able to make chained map calls on a single EntityConfiguration in 4.1. and 4.2
Thankfully, the suggested alternative way of calling Map (ie not chaining) fixed all my issues.
In my project, I have the following PageCache entity, which is being stored in RavenDB:
public class PageCache
{
private readonly IHtmlDocumentHelper htmlDocumentHelper;
public string Id { get; set; }
public string Url { get; set; }
public PageCache(IHtmlDocumentHelper htmlDocumentHelper, string url)
{
this.htmlDocumentHelper = htmlDocumentHelper;
this.Url = url;
}
}
I am using Castle Windsor to inject the IHtmlDocumentHelper implementation at runtime. This member is used in methods defined inside the PageCache class, which I stripped from the above snippet for the sake of simplicity.
When I create a PageCache object using the constructor, everything works fine. But elsewhere in my code, I load PageCache objects back from RavenDB:
public PageCache GetByUrl(string url)
{
using (var session = documentStore.OpenSession())
{
return session.Query<PageCache>()
.Where(x => x.Url == url)
.FirstOrDefault();
}
}
My issue is that the objects I get back from RavenDB don't have the htmlDocumentHelper member set, rendering the PageCache methods that depend on it unuseable.
In other words: when I load objects back from documents stored in RavenDB, it won't use my constructor to build the objects, thus not initializing the private members through constructor injection.
Am I doing something wrong here? How would you solve such an issue?
I ended up using the solution proposed by Ayende below. The circular dependency issue I mentioned in the comments only appeared when I registered the DocumentStore in Windsor with UsingFactoryMethod(). The issue strangely disappeared when I used Windsor's DependsOn() and OnCreate() to configure and initialize the DocumentStore directly inside the Register().
My container is now being initialized as follows:
WindsorContainer container = new WindsorContainer();
container.Register(
// Register other classes, such as repositories and services.
// Stripped for the sake of clarity.
// ...
// Register the CustomJsonConverter:
Component.For<CustomJsonConverter>().ImplementedBy<CustomJsonConverter>(),
// The following approach resulted in an exception related to the circular
// dependencies issue:
Component.For<IDocumentStore>().UsingFactoryMethod(() =>
Application.InitializeDatabase(container.Resolve<CustomJsonConverter>()))
// Oddly enough, the following approach worked just fine:
Component.For<IDocumentStore>().ImplementedBy<DocumentStore>()
.DependsOn(new { Url = #"http://localhost:8080" })
.OnCreate(new Action<IDocumentStore>(store =>
store.Conventions.CustomizeJsonSerializer = serializer =>
serializer.Converters.Add(container.Resolve<CustomJsonConverter>())))
.OnCreate(new Action<IDocumentStore>(store =>
store.Initialize()))
.OnDestroy(new Action<IDocumentStore>(store =>
store.Dispose()))
);
Although it seems to be working fine, I feel odd having to call container.Resolve<CustomJsonConverter>() from inside the container.Register() method.
Is this a legal approach to register the dependencies?
Christian,
We can't use your ctor, we don't know what to put in there.
Instead, you can use this technique to tell RavenDB how to create your objects:
http://james.newtonking.com/projects/json/help/CustomCreationConverter.html
Then you can wire this in using documentStore.Conventison.CustomizeSerializer
Ok, I wrote this question up earlier today but I decided to delete it because I thought the question wasn't worded very well. I decided to wait until I had more time to compose it at home :).
I am just getting started with IOC/DI. I have done some research on which framework to use and decided to give StructureMap a spin. The following is the first tutorial I used:
http://dimecasts.net/Casts/CastDetails/39 by Derik Whittaker.
Anyways, I got everything working like a dream with EVERYTHING is hosted in the same project.
Here is my sample code:
[PluginFamily("SMTest",IsSingleton=true)]
public interface IVehicle
{
byte TopSpeed {set;get;}
byte MPG { set; get; }
}
[Pluggable("SMTest")]
public class Car : IVehicle
{
private byte mTopSpeed;
private byte mMPG;
#region IVehicle Members
byte IVehicle.TopSpeed
{
get
{
return mTopSpeed;
}
set
{
mTopSpeed = value;
}
}
public interface IConsumer
{
IVehicle Car { get; set; }
}
[Pluggable("SMTest")]
public class Consumer : StructureMapBasic.IConsumer
{
private IVehicle mCar;
public Consumer(IVehicle lcar)
{
Car = lcar;
}
public IVehicle Car { set; get; }
byte IVehicle.MPG
{
get
{
return mMPG;
}
set
{
mMPG = value;
}
}
#endregion
}
So anyways if i create the project above into a command line program and do the following:
var consumer = ObjectFactory.GetInstance<IConsumer>();
It works perfectly. No problems at all. When i create a seperate project in the solution and then change the project above to a DLL. I get the following error:
Test method StructureMapBasic.ConsumerTest.ConsumerConstructorTest
threw exception: StructureMap.StructureMapException: StructureMap
Exception Code: 202 No Default Instance defined for PluginFamily
StructureMapBasic.IConsumer, StructureMapBasic, Version=1.0.0.0,
Culture=neutral, PublicKeyToken=null.
At first i thought maybe the StructureMap.Config file didn't get into the new projects bin folder but that wasn't the case. It was there. Everythign compiles just fine this problem happens at runtime. I'm sure the solution is very easy but for the life of me i can't figure out whats going wrong. Any help would be MUCH appreciated.
Thanks,
Ncage
I took me all friggn day to figure this out. At first i thought i was just being an idiot and i was missing something stupid. Well i was not. I thought it was related to different projects but it was not. I created a new console application that consumed my StructureMapped DLL (If just coined a term ;) ). Anyways, after trying to spend all day on this problem i FINALLY found a post that described the problem. its a freakn bug in MSTest (my project i was having problems with was created in MSTest). Xunit here i come. Here is a post that describes the issue from the same guy who created the tutorial video:
http://devlicio.us/blogs/derik_whittaker/archive/2008/07/23/mstest-why-i-hate-you-you-cause-me-too-much-friction.aspx