当用户在我的Web应用程序中注册时,他们必须输入三项内容:用户名,公司名称和密码。现在,我接下来要使用的信息完全取决于我如何实现此信息。理想情况下,我希望用户具有两个配置文件:用户和公司配置文件。 Sensenet已经带有内置的用户配置文件,并且正在查看内容类型定义,这就是它的定义方式。
<?xml version="1.0" encoding="utf-8"?>
<ContentType name="UserProfile" parentType="Workspace" handler="SenseNet.ContentRepository.UserProfile" xmlns="http://schemas.sensenet.com/SenseNet/ContentRepository/ContentTypeDefinition">
<DisplayName>$Ctd-UserProfile,DisplayName</DisplayName>
<Description>$Ctd-UserProfile,Description</Description>
<Icon>UserProfile</Icon>
<AllowedChildTypes>
Blog,DocumentLibrary,EventList,MemoList,LinkList,TaskList,ImageLibrary,Posts,CustomList
</AllowedChildTypes>
<Fields>
<Field name="IsWallContainer" type="Boolean">
<Configuration>
<VisibleBrowse>Advanced</VisibleBrowse>
<VisibleEdit>Advanced</VisibleEdit>
<VisibleNew>Show</VisibleNew>
<DefaultValue>true</DefaultValue>
</Configuration>
</Field>
<Field name="IsCritical" type="Boolean">
<Configuration>
<VisibleBrowse>Hide</VisibleBrowse>
<VisibleEdit>Hide</VisibleEdit>
<VisibleNew>Hide</VisibleNew>
</Configuration>
</Field>
<Field name="Manager" type="Reference">
<Configuration>
<VisibleBrowse>Hide</VisibleBrowse>
<VisibleEdit>Hide</VisibleEdit>
<VisibleNew>Hide</VisibleNew>
</Configuration>
</Field>
<Field name="Deadline" type="DateTime">
<Configuration>
<VisibleBrowse>Hide</VisibleBrowse>
<VisibleEdit>Hide</VisibleEdit>
<VisibleNew>Hide</VisibleNew>
</Configuration>
</Field>
<Field name="IsActive" type="Boolean">
<Configuration>
<VisibleBrowse>Hide</VisibleBrowse>
<VisibleEdit>Hide</VisibleEdit>
<VisibleNew>Hide</VisibleNew>
</Configuration>
</Field>
<Field name="User" type="Reference">
<DisplayName>$Ctd-UserProfile,User-DisplayName</DisplayName>
<Configuration>
<AllowMultiple>false</AllowMultiple>
<AllowedTypes>
<Type>User</Type>
</AllowedTypes>
<SelectionRoot>
<Path>/Root/IMS</Path>
</SelectionRoot>
</Configuration>
</Field>
</Fields>
</ContentType>
引起我注意的是引用字段,该字段引用附加到用户配置文件的用户。因此,以这种知识为基础,也许最好用与此参考字段相同的参考字段创建一个单独的内容类型定义,以便将其绑定到同一用户。我可以走的另一个方向是扩展User Profile内容类型定义xml文件,以将公司信息嵌入其中,而不是为其提供单独的定义文件,但是问题是,现在我将更改用户个人资料可能包含太多信息。
最重要的是,没有其他用户可以绑定到同一家公司,因为它必须是唯一的。考虑所有这些,您认为哪种方法是执行此操作的最佳方法?
因此,我在为用户创建公司资料方面取得了一些进展。我已经为公司资料创建了此内容处理程序。
using SenseNet.ContentRepository.Schema;
using SenseNet.ContentRepository.Storage;
using SenseNet.ContentRepository.Workspaces;
namespace DerAssistantService.ContentHandlers
{
[ContentHandler]
public class CompanyProfile : Workspace
{
public CompanyProfile(Node parent) : this(parent, null) { }
public CompanyProfile(Node parent, string nodeTypeName) : base(parent, nodeTypeName) { }
protected CompanyProfile(NodeToken nt) : base(nt) { }
public override string Name
{
get { return base.Name;}
set { base.Name = value;}
}
[RepositoryProperty("Address")]
public string Address
{
get { return GetProperty<string>("Address"); }
set { this["Address"] = value; }
}
[RepositoryProperty("City")]
public string City
{
get { return GetProperty<string>("City"); }
set { this["City"] = value; }
}
[RepositoryProperty("State")]
public string State
{
get { return GetProperty<string>("State"); }
set { this["State"] = value; }
}
public override object GetProperty(string name)
{
switch (name)
{
case "Address":
return Address;
case "City":
return City;
case "State":
return State;
default:
return base.GetProperty(name);
}
}
public override void SetProperty(string name, object value)
{
switch (name)
{
case "Address":
Address = (string)value;
break;
case "City":
City = (string)value;
break;
case "State":
State = (string)value;
break;
default:
base.SetProperty(name, value);
break;
}
}
}
}
他们注册时,将在“公司”域下创建公司资料。
using System;
using System.Linq;
using SenseNet.ApplicationModel;
using SenseNet.ContentRepository;
using SenseNet.ContentRepository.Storage;
using SenseNet.ContentRepository.Storage.Data;
using SenseNet.ContentRepository.Storage.Security;
namespace DerAssistantService.Actions
{
public static class UserActions
{
[ODataAction]
public static Content RegisterUser(Content content, string email, string companyname, string password)
{
if (string.IsNullOrEmpty(email))
throw new ArgumentNullException(nameof(email));
if (string.IsNullOrEmpty(companyname))
throw new ArgumentNullException(nameof(companyname));
if (string.IsNullOrEmpty(password))
throw new ArgumentNullException(nameof(password));
var username = email.Split('@').First();
var isUserCreated = Node.LoadNode("Root/IMS/Public/" + username);
var isCompanyProfileCreated = Node.LoadNode("Root/Profiles/Company" + companyname);
if (isUserCreated != null)
{
throw new NodeAlreadyExistsException("There already exists a user with this name.");
}
if (isCompanyProfileCreated != null)
{
throw new NodeAlreadyExistsException("There already exists a company with this name.");
}
using (new SystemAccount())
{
var user = Content.CreateNew("User", content.ContentHandler, username);
user["FullName"] = username;
user["Email"] = email;
user["LoginName"] = email;
user["Enabled"] = true;
user["Password"] = password;
user.Save();
var parent = Node.LoadNode("Root/Profiles/Company");
var companyProfile = Content.CreateNew("CompanyProfile", parent, companyname);
companyProfile["Name"] = companyname;
companyProfile.Save();
var identifiedUsers = Node.Load<Group>("/Root/IMS/BuiltIn/Portal/IdentifiedUsers");
identifiedUsers.AddMember(user.ContentHandler as IUser);
identifiedUsers.Save();
return user;
}
}
}
}
,我为此创建了内容类型定义。
<ContentType name="CompanyProfile" parentType="Workspace" handler="DerAssistantService.ContentHandlers.CompanyProfile" xmlns="http://schemas.sensenet.com/SenseNet/ContentRepository/ContentTypeDefinition">
<DisplayName>CompanyProfile</DisplayName>
<Description>This content contains basic information on a company</Description>
<Icon>Company</Icon>
<Fields>
<Field name="Name" type="ShortText">
<DisplayName>Company</DisplayName>
<Description>The name of the company</Description>
<Configuration>
<VisibleBrowse>Show</VisibleBrowse>
<VisibleEdit>Show</VisibleEdit>
<VisibleNew>Show</VisibleNew>
<Compulsory>true</Compulsory>
</Configuration>
</Field>
<Field name="Address" type="ShortText">
<DisplayName>Address</DisplayName>
<Description>The location of the company</Description>
<Configuration>
<VisibleBrowse>Show</VisibleBrowse>
<VisibleEdit>Show</VisibleEdit>
<VisibleNew>Show</VisibleNew>
<Compulsory>true</Compulsory>
</Configuration>
</Field>
<Field name="City" type="ShortText">
<DisplayName>City</DisplayName>
<Description>The city where the company is located at</Description>
<Configuration>
<VisibleBrowse>Show</VisibleBrowse>
<VisibleEdit>Show</VisibleEdit>
<VisibleNew>Show</VisibleNew>
<Compulsory>true</Compulsory>
</Configuration>
</Field>
<Field name="State" type="ShortText">
<DisplayName>State</DisplayName>
<Description>The state the company resides in</Description>
<Configuration>
<VisibleBrowse>Show</VisibleBrowse>
<VisibleEdit>Show</VisibleEdit>
<VisibleNew>Show</VisibleNew>
<Compulsory>true</Compulsory>
</Configuration>
</Field>
</Fields>
</ContentType>
问题所在,我无法轻松加载公司资料。两者之间没有连接,因为两者都位于单独的域中,彼此之间没有信息。我该如何调和?
经过一些实验,我终于有了一个可行的解决方案。
这是我的公司资料现在的样子。
<?xml version="1.0" encoding="utf-8"?>
<ContentType name="CompanyProfile" parentType="UserProfile" handler="DerAssistantService.ContentHandlers.CompanyProfile" xmlns="http://schemas.sensenet.com/SenseNet/ContentRepository/ContentTypeDefinition">
<DisplayName>CompanyProfile</DisplayName>
<Description>This profile contains contains a single reference to a company that has registered itself in the web app.</Description>
<Icon>UserProfile</Icon>
<AllowedChildTypes>DocumentLibrary,EventList,MemoList,LinkList,TaskList,ImageLibrary,CustomList</AllowedChildTypes>
<Fields>
<Field name="Company" type="Reference">
<DisplayName>Company</DisplayName>
<Configuration>
<AllowMultiple>false</AllowMultiple>
<AllowedTypes>
<Type>Company</Type>
</AllowedTypes>
<SelectionRoot>
<Path>/Root/IMS</Path>
</SelectionRoot>
</Configuration>
</Field>
</Fields>
</ContentType>
现在,公司资料扩展了用户资料,因此我仍然具有对用户对象的引用,并具有公司对象的新引用属性。
现在我有一个公司的单独的内容类型定义文件。
<?xml version="1.0" encoding="utf-8"?>
<ContentType name="Company" parentType="Workspace" handler="DerAssistantService.ContentHandlers.Company" xmlns="http://schemas.sensenet.com/SenseNet/ContentRepository/ContentTypeDefinition">
<DisplayName>Company</DisplayName>
<Description>This content contains basic information on a particular company</Description>
<Icon>Company</Icon>
<AllowedChildTypes>Image</AllowedChildTypes>
<Fields>
<Field name="Address" type="ShortText">
<DisplayName>Address</DisplayName>
<Description>The location of the company</Description>
<Configuration>
<VisibleBrowse>Show</VisibleBrowse>
<VisibleEdit>Show</VisibleEdit>
<VisibleNew>Show</VisibleNew>
<Compulsory>true</Compulsory>
</Configuration>
</Field>
<Field name="City" type="ShortText">
<DisplayName>City</DisplayName>
<Description>The city where the company resides in</Description>
<Configuration>
<VisibleBrowse>Show</VisibleBrowse>
<VisibleEdit>Show</VisibleEdit>
<VisibleNew>Show</VisibleNew>
<Compulsory>true</Compulsory>
</Configuration>
</Field>
<Field name="State" type="ShortText">
<DisplayName>State</DisplayName>
<Description>The state the company resides in</Description>
<Configuration>
<VisibleBrowse>Show</VisibleBrowse>
<VisibleEdit>Show</VisibleEdit>
<VisibleNew>Show</VisibleNew>
<Compulsory>true</Compulsory>
</Configuration>
</Field>
</Fields>
</ContentType>
这是我的CompanyProfile内容处理程序的外观
using SenseNet.ContentRepository;
using SenseNet.ContentRepository.Schema;
using SenseNet.ContentRepository.Storage;
namespace DerAssistantService.ContentHandlers
{
[ContentHandler]
public class CompanyProfile : UserProfile
{
public CompanyProfile(Node parent) : this(parent, null) { }
public CompanyProfile(Node parent, string nodeTypeName) : base(parent, nodeTypeName) { }
protected CompanyProfile(NodeToken token) : base(token) { }
[RepositoryProperty("Company", RepositoryDataType.Reference)]
public Company Company
{
get { return GetReference<Company>("Company"); }
set { SetReference("Company", value); }
}
public override object GetProperty(string name)
{
switch (name)
{
case "Company":
return Company;
default:
return base.GetProperty(name);
}
}
public override void SetProperty(string name, object value)
{
switch (name)
{
case "Company":
Company = (Company)value;
break;
default:
base.SetProperty(name, value);
break;
}
}
}
}
以及我公司类的内容处理程序的外观。
using SenseNet.ContentRepository.Schema;
using SenseNet.ContentRepository.Storage;
using SenseNet.ContentRepository.Workspaces;
namespace DerAssistantService.ContentHandlers
{
[ContentHandler]
public class Company : Workspace
{
public Company(Node parent) : this(parent, null) { }
public Company(Node parent, string nodeTypeName) : base(parent, nodeTypeName) { }
protected Company(NodeToken token) : base(token) { }
[RepositoryProperty("Address", RepositoryDataType.String)]
public string Address
{
get { return GetProperty<string>("Address"); }
set { this["Address"] = value; }
}
[RepositoryProperty("City", RepositoryDataType.String)]
public string City
{
get { return GetProperty<string>("City"); }
set { this["City"] = value; }
}
[RepositoryProperty("State", RepositoryDataType.String)]
public string State
{
get { return GetProperty<string>("State"); }
set { this["State"] = value; }
}
public override object GetProperty(string name)
{
switch (name)
{
case "Address":
return Address;
case "City":
return City;
case "State":
return State;
default:
return base.GetProperty(name);
}
}
public override void SetProperty(string name, object value)
{
switch (name)
{
case "Address":
Address = (string)value;
break;
case "City":
City = (string)value;
break;
case "State":
State = (string)value;
break;
default:
base.SetProperty(name, value);
break;
}
}
}
}
我在RegisterUser方法中分离了太多的逻辑。
我的RegisterUser函数现在看起来像这样。
[ODataAction]
public static Content RegisterUser(Content content, string email, string password)
{
using (new SystemAccount())
{
var username = email.Split('@').First();
var user = CreateUser("Public", email, password, username, true);
var identifiedUsers = Node.Load<Group>("/Root/IMS/BuiltIn/Portal/IdentifiedUsers");
identifiedUsers.AddMember(user.ContentHandler as IUser);
identifiedUsers.Save();
return user;
}
}
private static Content CreateUser(string domainName, string username, string password, string fullname, bool enabled, Dictionary<string, object> properties = null)
{
var domainPath = RepositoryPath.Combine(RepositoryStructure.ImsFolderPath, domainName);
var domain = Node.LoadNode(domainPath);
var user = Content.CreateNew("User", domain, username);
user["Name"] = username;
user["Password"] = password;
user["FullName"] = fullname;
user["Enabled"] = enabled;
if (properties != null)
{
foreach (var key in properties.Keys)
{
user[key] = properties[key];
}
}
user.Save();
return user;
}
}
我创建了一个单独的功能,负责创建用户后注册公司,这仅仅是因为我需要创建用户对象,以便它可以创建公司资料。
[ODataAction]
public static Content RegisterCompany(Content content, string companyName, string userEmail)
{
using (new SystemAccount())
{
CompanyProfile companyProfile = Node.LoadNode("/Root/Profiles/Public/" + userEmail) as CompanyProfile;
Company company = CreateCompany(companyName, companyProfile);
var companyContent = Content.Create(company);
companyContent.Save();
companyProfile.Company = company;
companyProfile.Save();
return companyContent;
}
}
private static Company CreateCompany(string companyName, CompanyProfile companyProfile)
{
var parent = Node.LoadNode("/Root/IMS/Company");
Company company = new Company(parent);
company.Name = companyName;
company.Address = "N/A";
company.City = "N/A";
company.State = "N/A";
company.VersionCreatedBy = companyProfile.User;
company.VersionModifiedBy = companyProfile.User;
company.CreatedBy = companyProfile.User;
company.ModifiedBy = companyProfile.User;
company.Owner = companyProfile.User;
return company;
}
}
这样,如果我要按该顺序向RegisterUser和RegisterCompany进行请求,则公司资料现在将具有对创建的公司对象的引用。请告诉我是否还有其他方法可以重组它。
答案 0 :(得分:1)
我认为您需要反转参考方向。例如,用户在一家公司工作,该公司可以有一个个人资料。因此,用户可以使用单个参考字段来定位公司或公司资料。这种机制可在两个方向上轻松访问:如果您拥有用户实例user.Company
或user.CompanyProfile
,则返回目标对象。向后方向可以通过简单的查询来访问,例如:Content.All.OfType<User>().Where(c => c.Company == company).FirstOrDefault()
。