What ID values are constant to all Sitecore installations? - constants

When making reusable layouts/sublayouts in Sitecore, I often want to access a specific item by ID. This poses a challenge as I'm not certain which items will have the same ID across all Sitecore installations.
The Sitecore root ID (/sitecore) is constant at {11111111-1111-1111-1111-111111111111}, what other IDs can be stored as a constant without fear of needing to update for each project?

I would guess most of the structural items have the same ids in different installations.
Sitecore has Sitecore.ItemIds class which has references to some of the main items.
Here is the list of items it contains
public static ID AnonymousUser;
public static ID BranchesRoot;
public static ID ConditionalRenderingsGlobalRules;
public static ID ContentRoot;
public static ID DefaultRibbon;
public static ID DevicesRoot;
public static ID EveryoneRoleID;
public static ID LanguageRoot;
public static ID LayoutRoot;
public static ID Layouts;
public static ID MediaLibraryRoot;
public static ID Null;
public static ID PlaceholderSettingsRoot;
public static ID Policies;
public static ID RootID;
public static ID Shell;
public static ID ShellAll;
public static ID ShellDefault;
public static ID SystemRoot;
public static ID TemplateRoot;
public static ID Undefined;
[Obsolete("This ID has been deprecated.")]
public static ID VirtualStructures;
public static ID WorkflowRoot;
If you don't find what you need here I think the best solution is to depend on paths rather than Ids. Paths are easier to read and debug.

Related

Rest API architecture special cases

Using the Web API in .net 5 and conforming an api in rest gives me some challenges about how much to split up the methods in different controllers and the naming conventions.
I have read that if I have users in my system and doing a rest architecture, my controller would be named UserController and method for getting a user would be:
[HttpGet("{id}")]
public string Get(int id)
{
return "value";
}
For getting a list of users it would be:
[HttpGet]
public IEnumerable<string> Get()
{
return new string[] { "value1", "value2" };
}
Updating would be:
[HttpPut("{id}")]
public void Put(int id, [FromBody] string value)
{
}
Delete would have a [HttpDelete], and so on. But what about the special cases?
What if I wanted GetUserByUsername? Would it then be in the same UserController and just be the following, or would it break the REST pattern?:
[HttpGet("{username}")]
public Task GetByUsername(string username)
{
}
What if I needed a call to the api get some data to populate the "createuser page", lets say I need the roles that the user could be created as and some other information and would like to do it in one call. Could I then just create a "InitCreateUser" in the UserController and not break the REST pattern?
[HttpPost]
public Task InitCreateUser()
{
}
What if I needed Login and Logout methods, would it be AutenticationController and just have the two methods: (It's just so far from the other pattern when its not called just Get() Post() and so on)
[HttpPost]
public Task Login(LoginRequest request)
{
}
[HttpPost]
public Task Logout(LogoutRequest request)
{
}
Yes you would put it in the same controller as it is still dealing with the user, and it is good habit to keep functions involving a specific entity in the same location.
But that obviously has the issue of having different endpoints such as Id and Username. A simple way to do this is to indicate which you want to use:
[HttpGet("Id/{id}")]
public string Get([FromRoute] int id) {...}
-> api/User/Id/293
[HttpGet("Username/{username}")]
public string Get([FromRoute] string username) {...}
-> api/User/Username/Tom
Another way is to have a base request and have other functions be offshoots of it
[HttpGet("{id}")]
public string Get([FromRoute] int id) {...}
-> api/User/293
[HttpGet("Username/{username}")]
public string Get([FromRoute] string username) {...}
-> api/User/Username/Tom
[HttpGet("Customer/{custNo}")]
public string Get([FromRoute] string custNo) {...}
-> api/User/Customer/5DtU22D
But lets say you have the Id of the user and you wish to do other functions like get relevant data from the user to display, but not all of it. Or You want to check all the relating data towards a specific funciton,
then you can do something like this:
[HttpGet("{id}/Permissions")]
public PermissionModel GetUserPermission([FromRoute] int id) {...}
[HttpPut("{id}/Permissions")]
public bool UpdateUserPermission([FromRoute] int id, [FromBody] PermissionModel permission) {...}
Or even further derived functionalities
[HttpGet("{id}/Account")]
public string GetUserAccount([FromRoute] int id) {...}
[HttpGet("{id}/Account/Funds")]
public double GetUserAccountTotal([FromRoute] int id) {...}
if you are accessing list properties, for example the user has many accounts for example, you can add a secondary key:
[HttpGet("{id}/Accounts")]
public IEnumerable<string> GetUserAccount([FromRoute] int id) {...}
[HttpGet("{id}/Accounts/{accountId}/Funds")]
public double GetUserAccountTotal([FromRoute] int id, [FromRoute] int accountId) {...}

EF Core Hierarchy TPH map two entities to one

I have a base project, which has some basic entities. Let's use BaseUser as one. In the child project, it references base project and User class inherits from that BaseUser. The only difference is the User class has List<Blogs>, so no additional properties, no change in mapping.
When I query Users, it does not find any because they are created as BaseUser and the query has discriminator value of "User". I don't want to query "BaseUser" because I want the relational property of List<Blogs>.
Is there any way to tell EF to basically treat these classes as one? Is there a better way to handle the split? (obviously base project has no concept of blogs, so cannot move the List to the base)
Some sample classes as requested
/* Base Project (nuget package created) */
public class BaseUser {
public int UserId { get; set; }
public int Name { get; set; }
}
public class BaseContext : DbContext {
public DbSet<BaseUser> BaseUsers {get;set;}
}
public class BaseDataInstaller {
BaseContext _ctx;
public BaseDataInstaller( BaseContext ctx ){
_ctx = ctx;
}
public void Install(){
_ctx.BaseUsers.Add( new BaseUser { Name="Demo User 1" } );
_ctx.BaseUsers.Add( new BaseUser { Name="Demo User 2" } );
_ctx.SaveChanges();
}
}
/* Child Project (consumes nuget package)*/
public class User : BaseUser {
List<Blogs> Blogs { get; set; }
}
public class ProjectContext : BaseContext {
public DbSet<User> Users { get; set; }
}
public class SomeService {
ProjectContext _ctx;
public BaseDataInstaller(ProjectContext ctx){
_ctx = ctx;
}
//Finds 0 users
public void PrintUsers(){
var users = _ctx.Users.ToList();
users.ForEach( u=> Console.WriteLine(u.Name) );
}
//Finds Users
public void PrintBaseUsers(){
var users = _ctx.BaseUsers.ToList();
users.ForEach( u=> Console.WriteLine(u.Name) );
}
}
Comparing the SQL generated, there is a discriminator added
where Discriminator = 'BaseUser' or where Discriminator = 'User'
There are no properties which are different between the two, just the relationship with the blogs.
So is there a way to either make both have the same Discriminator value or another way to solve this?
UPDATE 1
The discriminator only appears if the DbContext knows about BOTH entities. if it only knows about the one, it is happy to map onto the table. Even if the child inherits from the base, it still doesn't need a discriminator. So I think the challenge is to re-work the base so the context doesn't know about the base. This does feel like a workaround though. Maybe the structure should change:
instead of User : BaseUser use a property
User
- int ChildUserId {get; set;}
- BaseUser BaseUser {get; set;}
- SomeObject SomeNavProperty etc
it will mean a new table for each inherited project, but would allow the project to add it's own specific data too...

Add Columns/Properties to AspNetUserLogins/Logins in IdentityDbContext

Is it possible to add columns to the AspNetUserLogins table, or subclass the IdentityUserLogin class, such that the Identity Framework will use that class properly?
This is an answer but I'm sure it's not going to end up the best one:
It can be done, but it's ugly.
First, you'll want to make a class of all the generics you're about to use, just to make your life easier. Those are:
[Table("AspNetUserRoles")]
public class StandardUserRole : IdentityUserRole<string>
[Table("AspNetRoles")]
public class StandardRole : IdentityRole<string, StandardUserRole>
[Table("AspNetUserLogins")]
public class LoginIdentity : IdentityUserLogin
(The above superclasses can be found in Microsoft.AspNet.Identity.EntityFramework).
This is going to make the following generic definitions shorter, and harder to get into a place where they won't compile due to clerical errors.
While you're here may as well add these to the DbContext, which normally does not leave them available to you:
public DbSet<LoginIdentity> LoginIdentities { get; set; }
public DbSet<StandardUserRole> UserRoles { get; set; }
Now, here comes the crazy:
public class Db :
// Replace this with a custom implementation
//IdentityDbContext<Visitor>,
IdentityDbContext<Visitor, StandardRole, string, LoginIdentity,
StandardUserRole, IdentityUserClaim>,
And, Visitor is going to need its own adjustment to match this declaration:
public class Visitor : IdentityUser<string, LoginIdentity, StandardUserRole,
IdentityUserClaim>
That satisfies the Models (which btw, are best to have in their own Project for Migrations performance reasons). But, you've still got all the Identity/OWIN stuff to deal with.
By default you're provided with an ApplicationUserManager that involves a UserStore. It normally inherits from UserManager, but that's going to be too restrictive now - you need to slightly expand it:
public class VisitorManager : UserManager<Visitor, string>
{
public VisitorManager(IUserStore<Visitor, string> store)
: base(store)
{
}
public static VisitorManager Create(
IdentityFactoryOptions<VisitorManager> options,
IOwinContext context)
{
var manager = new VisitorManager(new UserStore<Visitor,
StandardRole, string, LoginIdentity, StandardUserRole,
IdentityUserClaim>(context.Get<Db>()));
I warned you about crazy. SignInManager:
public class SignInManager : SignInManager<Visitor, string>
{
public SignInManager(VisitorManager userManager,
IAuthenticationManager authenticationManager)
: base(userManager, authenticationManager)
{
}
public override Task<ClaimsIdentity> CreateUserIdentityAsync(
Visitor user)
{
return user.GenerateUserIdentityAsync((VisitorManager)UserManager);
}
public static SignInManager Create(
IdentityFactoryOptions<SignInManager> options, IOwinContext context)
{
return new SignInManager(context.GetUserManager<VisitorManager>(),
context.Authentication);
}
}
That should get you through most of the dirty work. Not easy. But, having done that, you've got a working implementation where you can add extra fields to the Logins table! You can now extend the OWIN Auth stuff to provide events, and listen for the creation of new Logins. You can then respond to those by adding that extra info.
In our case, the goal was to have multiple Logins from multiple OpenId/OAuth Providers (Google, Facebook, etc) across multiple email addresses, on a single User/Visitor account. The framework does support that, but, it doesn't make a record of what Email is associated with what Login row, which is important when merging more Logins with a given account.
[Table("AspNetUserLogins")]
public class LoginIdentity : IdentityUserLogin
{
/// <summary>
/// The email address associated with this identity at this provider
/// </summary>
[MaxLength(300)]
public string Email { get; set; }
}
There's more you'll need to do to get the whole thing working, but it should be relatively obvious from the above starting point - with one exception, which I'll point out here.
By migrating from UserManager<TVisitor> to UserManager<TVisitor, string>, you quietly lose the ID-generation functionality built-in to the former. You'll need to emulate it yourself. As another gotcha, along the way you'll most likely implement Visitor as IUser<string>. Doing so will prevent you from setting the Id property, because it's read-only (no setter). You can avoid that with a second interface:
public interface IVisitor
{
string Id { get; set; }
string Uid { get; set; }
string UserName { get; set; }
string Email { get; set; }
string FirstName { get; set; }
string LastName { get; set; }
ICollection<StandardUserRole> Roles { get; }
ICollection<LoginIdentity> Logins { get; }
}
With that in place you can set Id safely (even in an abstracted class):
public override Task<IdentityResult> CreateAsync(Visitor user)
{
var guid = Guid.NewGuid();
string id = guid.ToString();
((IVisitor)user).Id = id;
return base.CreateAsync(user);
}
Remember to do same for CreateAsync(Visitor user, string password). Otherwise created users explode with DbEntityValidationException complaining Id is a required field.

Azure Mobile App Service - force load of sub item in REST API

I have a mobile app service, that represents Employee and Company.
There's also a many to many table to represent when an employee works for a company (and can work for more than one).
So the basic structure is
public class Employee : EntityData
{
public string Id{get;set;} etc
public List<EmployeeCompanyRelationship> EmployeeCompanyRelationships{get;set;}
}
public class Company : EntityData
{
public string Id{get;set;} etc
}
public class EmployeeCompanyRelationship : EntityData
{
public Employee Employee{get;set;}
public Company Company{get;set;}
public bool IsCurrentEmployee{get;set;}
}
However when I then GET on Employee's controller the EmployeeCompanyRelationships is not populated. If I expose EmployeeCompanyRelationship with its own controller, I get something like the following
[{"deleted":false,"updatedAt":"2016-05-21T23:04:49.407Z","createdAt":"2016-05-21T23:04:49.391Z","version":"AAAAAAAACNc=","id":"09fa0daf-ba36-4146-ba9c-c0836fda4275:126ff9b8-b90e-4c82-9e3e-4331f7126cce","isCurrentEmployee":true
Is there a way I can force either Employee or EmployeeCompanyRelationship to include their related entities in serialization?
Short version: no
Longer version: See my blog post on the subject: https://shellmonger.com/2016/05/27/30-days-of-zumo-v2-azure-mobile-apps-day-26-relationship-advice/

Entity Framework Code First ReadOnly Collections

As a new to Entity Framework, and already having developed almost all classes with the ReadOnly collections, my question is:
Is there a way to use ReadOnly collections with Code First?
OR
Should ReadOnly collections be used with Code First?
Entity Framework supports Read Only Collections throught backing fields
There are conventions to folow: field must started with '_'. See field _posts in code bellow.
public class Blog
{
public int BlogId { get; set; }
private string _url;
public string Url
{
get { return _url; }
set { _url = value; }
}
//backing field
private List<Post> _posts;
public IReadOnlyList<Post> Posts => _posts.AsReadOnly();
}
public class Post{
public int Id {get;set;}
public string Title {get;set;}
public string Content {get;set;}
}
No there is not way to use read only collections with EF because even during materialization of entities read from database EF must fill that collection with entities or assign writable collection to your navigation property.