Fixes performance for Get(id) on ContentTypeRepository = much faster now, now to work on getall/query

This commit is contained in:
Shannon
2014-07-04 10:54:12 +10:00
parent 9bae8173fc
commit 7fbda1dc7d
4 changed files with 205 additions and 42 deletions
@@ -8,6 +8,7 @@ using Umbraco.Core.Models.Rdbms;
using Umbraco.Core.Persistence.Caching;
using Umbraco.Core.Persistence.Factories;
using Umbraco.Core.Persistence.Querying;
using Umbraco.Core.Persistence.Relators;
using Umbraco.Core.Persistence.SqlSyntax;
using Umbraco.Core.Persistence.UnitOfWork;
@@ -47,47 +48,30 @@ namespace Umbraco.Core.Persistence.Repositories
//NOTE: SQL call #1
var dto = Database.Fetch<DocumentTypeDto, ContentTypeDto, NodeDto>(contentTypeSql).FirstOrDefault();
IEnumerable<Tuple<int, string, string>> assocatedTemplates;
var contentType = ContentTypeQueries.MapContentType(id, Database, out assocatedTemplates);
//var dto = Database.Fetch<DocumentTypeDto, ContentTypeDto, NodeDto, ContentTypeAllowedContentTypeDto, DocumentTypeDto>(
// new AllowedContentTypeRelator().Map,
// contentTypeSql);
if (dto == null)
if (contentType == null)
return null;
var factory = new ContentTypeFactory(NodeObjectTypeId);
var contentType = factory.BuildEntity(dto);
//NOTE: SQL call #2
contentType.AllowedContentTypes = GetAllowedContentTypeIds(id);
PropertyGroupCollection propGroups;
PropertyTypeCollection propTypes;
ContentTypeQueries.PopulateGroupsAndProperties(id, Database, out propTypes, out propGroups);
ContentTypeQueries.MapGroupsAndProperties(id, Database, out propTypes, out propGroups);
contentType.PropertyGroups = propGroups;
((ContentType) contentType).PropertyTypes = propTypes;
((ContentType)contentType).PropertyTypes = propTypes;
////NOTE: SQL call #3
//contentType.PropertyGroups = GetPropertyGroupCollection(id, contentType.CreateDate, contentType.UpdateDate);
//NOTE: SQL call #3++
//SEE: http://issues.umbraco.org/issue/U4-5174 to fix this
////NOTE: SQL call #4
//((ContentType)contentType).PropertyTypes = GetPropertyTypeCollection(id, contentType.CreateDate, contentType.UpdateDate);
var templates = _templateRepository.GetAll(assocatedTemplates.Select(x => x.Item1).ToArray());
contentType.AllowedTemplates = templates.ToArray();
//NOTE: SQL call #5
//NOTE: SQL call #5++
//TODO: Fix this part up, surely we can include it in the first sql call
var templates = Database.Fetch<DocumentTypeDto>("WHERE contentTypeNodeId = @Id", new { Id = id });
if(templates.Any())
{
//NOTE: SQL call #6++
contentType.AllowedTemplates = templates.Select(template => _templateRepository.Get(template.TemplateNodeId)).ToList();
}
//NOTE: SQL call #7
var list = Database.Fetch<ContentType2ContentTypeDto>("WHERE childContentTypeId = @Id", new { Id = id});
var list = Database.Fetch<ContentType2ContentTypeDto>("WHERE childContentTypeId = @Id", new { Id = id });
foreach (var contentTypeDto in list)
{
bool result = contentType.AddContentType(Get(contentTypeDto.ParentId));
@@ -150,15 +134,14 @@ namespace Umbraco.Core.Persistence.Repositories
protected override Sql GetBaseQuery(bool isCount)
{
var sql = new Sql();
sql.Select(isCount ? "COUNT(*)" : "*")
.From<DocumentTypeDto>()
.RightJoin<ContentTypeDto>()
.On<ContentTypeDto, DocumentTypeDto>(left => left.NodeId, right => right.ContentTypeNodeId)
.InnerJoin<NodeDto>()
.On<ContentTypeDto, NodeDto>(left => left.NodeId, right => right.NodeId)
//.LeftJoin<ContentTypeAllowedContentTypeDto>()
//.On<ContentTypeAllowedContentTypeDto, ContentTypeDto>(left => left.AllowedId, right => right.NodeId)
.Where<NodeDto>(x => x.NodeObjectType == NodeObjectTypeId);
.Where<NodeDto>(x => x.NodeObjectType == new Guid(Constants.ObjectTypes.DocumentType));
return sql;
}
@@ -303,7 +286,129 @@ namespace Umbraco.Core.Persistence.Repositories
internal static class ContentTypeQueries
{
public static void PopulateGroupsAndProperties(int contentTypeId, Database db, out PropertyTypeCollection propertyTypeCollection, out PropertyGroupCollection propertyGroupCollection)
public static IContentType MapContentType(int contentTypeId, Database db, out IEnumerable<Tuple<int, string, string>> associatedTemplates)
{
var sql = @"SELECT cmsDocumentType.IsDefault as dtIsDefault, cmsDocumentType.templateNodeId as dtTemplateId,
cmsContentType.pk as ctPk, cmsContentType.alias as ctAlias, cmsContentType.allowAtRoot as ctAllowAtRoot, cmsContentType.description as ctDesc,
cmsContentType.icon as ctIcon, cmsContentType.isContainer as ctIsContainer, cmsContentType.nodeId as ctId, cmsContentType.thumbnail as ctThumb,
AllowedTypes.allowedId as ctaAllowedId, AllowedTypes.SortOrder as ctaSortOrder, AllowedTypes.alias as ctaAlias,
umbracoNode.createDate as nCreateDate, umbracoNode.[level] as nLevel, umbracoNode.nodeObjectType as nObjectType, umbracoNode.nodeUser as nUser,
umbracoNode.parentID as nParentId, umbracoNode.[path] as nPath, umbracoNode.sortOrder as nSortOrder, umbracoNode.[text] as nName, umbracoNode.trashed as nTrashed,
umbracoNode.uniqueID as nUniqueId,
Template.alias as tAlias, Template.nodeId as tId,Template.text as tText
FROM cmsDocumentType
RIGHT JOIN cmsContentType
ON cmsContentType.nodeId = cmsDocumentType.contentTypeNodeId
INNER JOIN umbracoNode
ON cmsContentType.nodeId = umbracoNode.id
LEFT JOIN (
SELECT cmsContentTypeAllowedContentType.Id, cmsContentTypeAllowedContentType.AllowedId, cmsContentType.alias, cmsContentTypeAllowedContentType.SortOrder
FROM cmsContentTypeAllowedContentType
INNER JOIN cmsContentType
ON cmsContentTypeAllowedContentType.AllowedId = cmsContentType.nodeId
) AllowedTypes
ON AllowedTypes.Id = cmsContentType.nodeId
LEFT JOIN (
SELECT * FROM cmsTemplate
INNER JOIN umbracoNode
ON cmsTemplate.nodeId = umbracoNode.id
) as Template
ON Template.nodeId = cmsDocumentType.templateNodeId
WHERE (umbracoNode.nodeObjectType = @nodeObjectType)
AND (umbracoNode.id = @contentTypeId)
ORDER BY cmsDocumentType.IsDefault DESC";
var result = db.Fetch<dynamic>(sql, new { nodeObjectType = new Guid(Constants.ObjectTypes.DocumentType), contentTypeId = contentTypeId });
//first we want to get the main content type data this is 1 : 1 with umbraco node data
var ct = result
.Select(x => new { x.ctPk, x.ctId, x.ctAlias, x.ctAllowAtRoot, x.ctDesc, x.ctIcon, x.ctIsContainer, x.ctThumb, x.nName, x.nCreateDate, x.nLevel, x.nObjectType, x.nUser, x.nParentId, x.nPath, x.nSortOrder, x.nTrashed, x.nUniqueId })
.DistinctBy(x => (int)x.ctId)
.FirstOrDefault();
if (ct == null)
{
associatedTemplates = null;
return null;
}
//get the unique list of associated templates
var defaultTemplates = result
//use a tuple so that distinct checks both values
.Select(x => new Tuple<bool?, int?>(x.dtIsDefault, x.dtTemplateId ))
.Where(x => x.Item1.HasValue && x.Item2.HasValue)
.Distinct()
.ToArray();
//if there isn't one set to default explicitly, we'll pick the first one
var defaultTemplate = defaultTemplates.FirstOrDefault(x => x.Item1.Value)
?? defaultTemplates.FirstOrDefault();
var dtDto = new DocumentTypeDto
{
//create the content type dto
ContentTypeDto = new ContentTypeDto
{
Alias = ct.ctAlias,
AllowAtRoot = ct.ctAllowAtRoot,
Description = ct.ctDesc,
Icon = ct.ctIcon,
IsContainer = ct.ctIsContainer,
NodeId = ct.ctId,
PrimaryKey = ct.ctPk,
Thumbnail = ct.ctThumb,
//map the underlying node dto
NodeDto = new NodeDto
{
CreateDate = ct.nCreateDate,
Level = (short)ct.nLevel,
NodeId = ct.ctId,
NodeObjectType = ct.nObjectType,
ParentId = ct.nParentId,
Path = ct.nPath,
SortOrder = ct.nSortOrder,
Text = ct.nName,
Trashed = ct.nTrashed,
UniqueId = ct.nUniqueId,
UserId = ct.nUser
}
},
ContentTypeNodeId = ct.ctId,
IsDefault = defaultTemplate != null,
TemplateNodeId = defaultTemplate != null ? defaultTemplate.Item2.Value : 0
};
// We will return a subset of the associated template - alias, id, name
// TODO: See: http://issues.umbraco.org/issue/U4-5174
//next, we'll get the template subset
associatedTemplates = result
.Select(x => new Tuple<int?, string, string>(x.tId, x.tAlias, x.tText))
.Where(x => x.Item1.HasValue)
.Select(x => new Tuple<int, string, string>(x.Item1.Value, x.Item2, x.Item3))
.Distinct()
.ToArray();
//now create the content type object
var factory = new ContentTypeFactory(new Guid(Constants.ObjectTypes.DocumentType));
var contentType = factory.BuildEntity(dtDto);
//map the allowed content types
contentType.AllowedContentTypes = result
//use tuple so we can use distinct on all vals
.Select(x => new Tuple<int?, int?, string>(x.ctaAllowedId, x.ctaSortOrder, x.ctaAlias))
.Where(x => x.Item1.HasValue && x.Item2.HasValue && x.Item3 != null)
.Distinct()
.Select(x => new ContentTypeSort(new Lazy<int>(() => x.Item1.Value), x.Item2.Value, x.Item3))
.ToList();
return contentType;
}
public static void MapGroupsAndProperties(int contentTypeId, Database db, out PropertyTypeCollection propertyTypeCollection, out PropertyGroupCollection propertyGroupCollection)
{
// first part Gets all property groups including property type data even when no property type exists on the group
// second part Gets all property types including ones that are not on a group
@@ -323,7 +428,7 @@ namespace Umbraco.Core.Persistence.Repositories
INNER JOIN cmsDataType as DT
ON PT.dataTypeId = DT.nodeId
) as PT
ON PG.[id] = PT.ptGroupId
ON PT.ptGroupId = PG.id
WHERE (PG.contenttypeNodeId = @contentTypeId)
UNION
@@ -19,6 +19,48 @@ namespace Umbraco.Tests.Persistence.Querying
[TestFixture]
public class ContentTypeSqlMappingTests : BaseDatabaseFactoryTest
{
[Test]
public void Can_Map_Content_Type_Templates_And_Allowed_Types()
{
using (var transaction = DatabaseContext.Database.GetTransaction())
{
DatabaseContext.Database.Execute(new Sql(string.Format("SET IDENTITY_INSERT {0} ON ", SqlSyntaxContext.SqlSyntaxProvider.GetQuotedTableName("umbracoNode"))));
DatabaseContext.Database.Insert("umbracoNode", "id", false, new NodeDto { NodeId = 55554, Trashed = false, ParentId = -1, UserId = 0, Level = 1, Path = "-1,55554", SortOrder = 1, UniqueId = new Guid("87D1EAB6-AB27-4852-B3DF-DE8DBA4A1AA0"), Text = "Template 1", NodeObjectType = new Guid(Constants.ObjectTypes.Template), CreateDate = DateTime.Now });
DatabaseContext.Database.Insert("umbracoNode", "id", false, new NodeDto { NodeId = 55555, Trashed = false, ParentId = -1, UserId = 0, Level = 1, Path = "-1,55555", SortOrder = 1, UniqueId = new Guid("3390BDF4-C974-4211-AA95-3812A8CE7C46"), Text = "Template 2", NodeObjectType = new Guid(Constants.ObjectTypes.Template), CreateDate = DateTime.Now });
DatabaseContext.Database.Insert("umbracoNode", "id", false, new NodeDto { NodeId = 99997, Trashed = false, ParentId = -1, UserId = 0, Level = 0, Path = "-1,99997", SortOrder = 0, UniqueId = new Guid("BB3241D5-6842-4EFA-A82A-5F56885CF528"), Text = "Test Content Type 1", NodeObjectType = new Guid(Constants.ObjectTypes.DocumentType), CreateDate = DateTime.Now });
DatabaseContext.Database.Insert("umbracoNode", "id", false, new NodeDto { NodeId = 99998, Trashed = false, ParentId = -1, UserId = 0, Level = 0, Path = "-1,99998", SortOrder = 0, UniqueId = new Guid("EEA66B06-302E-49BA-A8B2-EDF07248BC59"), Text = "Test Content Type 2", NodeObjectType = new Guid(Constants.ObjectTypes.DocumentType), CreateDate = DateTime.Now });
DatabaseContext.Database.Insert("umbracoNode", "id", false, new NodeDto { NodeId = 99999, Trashed = false, ParentId = -1, UserId = 0, Level = 0, Path = "-1,99999", SortOrder = 0, UniqueId = new Guid("C45CC083-BB27-4C1C-B448-6F703CC9B799"), Text = "Test Content Type 2", NodeObjectType = new Guid(Constants.ObjectTypes.DocumentType), CreateDate = DateTime.Now });
DatabaseContext.Database.Execute(new Sql(string.Format("SET IDENTITY_INSERT {0} OFF ", SqlSyntaxContext.SqlSyntaxProvider.GetQuotedTableName("umbracoNode"))));
DatabaseContext.Database.Execute(new Sql(string.Format("SET IDENTITY_INSERT {0} ON ", SqlSyntaxContext.SqlSyntaxProvider.GetQuotedTableName("cmsTemplate"))));
DatabaseContext.Database.Insert("cmsTemplate", "pk", false, new TemplateDto { NodeId = 55554, Alias = "testTemplate1", Design = "", Master = null, PrimaryKey = 22221});
DatabaseContext.Database.Insert("cmsTemplate", "pk", false, new TemplateDto { NodeId = 55555, Alias = "testTemplate2", Design = "", Master = null, PrimaryKey = 22222 });
DatabaseContext.Database.Execute(new Sql(string.Format("SET IDENTITY_INSERT {0} OFF ", SqlSyntaxContext.SqlSyntaxProvider.GetQuotedTableName("cmsTemplate"))));
DatabaseContext.Database.Execute(new Sql(string.Format("SET IDENTITY_INSERT {0} ON ", SqlSyntaxContext.SqlSyntaxProvider.GetQuotedTableName("cmsContentType"))));
DatabaseContext.Database.Insert("cmsContentType", "pk", false, new ContentTypeDto { PrimaryKey = 88887, NodeId = 99997, Alias = "TestContentType1", Icon = "icon-folder", Thumbnail = "folder.png", IsContainer = false, AllowAtRoot = true });
DatabaseContext.Database.Insert("cmsContentType", "pk", false, new ContentTypeDto { PrimaryKey = 88888, NodeId = 99998, Alias = "TestContentType2", Icon = "icon-folder", Thumbnail = "folder.png", IsContainer = false, AllowAtRoot = true });
DatabaseContext.Database.Insert("cmsContentType", "pk", false, new ContentTypeDto { PrimaryKey = 88889, NodeId = 99999, Alias = "TestContentType3", Icon = "icon-folder", Thumbnail = "folder.png", IsContainer = false, AllowAtRoot = true });
DatabaseContext.Database.Execute(new Sql(string.Format("SET IDENTITY_INSERT {0} OFF ", SqlSyntaxContext.SqlSyntaxProvider.GetQuotedTableName("cmsContentType"))));
DatabaseContext.Database.Insert(new DocumentTypeDto { ContentTypeNodeId = 99997, IsDefault = true, TemplateNodeId = 55555 });
DatabaseContext.Database.Insert(new DocumentTypeDto { ContentTypeNodeId = 99997, IsDefault = false, TemplateNodeId = 55554 });
DatabaseContext.Database.Insert(new ContentTypeAllowedContentTypeDto { AllowedId = 99998, Id = 99997, SortOrder = 1 });
DatabaseContext.Database.Insert(new ContentTypeAllowedContentTypeDto { AllowedId = 99999, Id = 99997, SortOrder = 2});
transaction.Complete();
}
IEnumerable<Tuple<int, string, string>> assocatedTemplates;
var contentType = ContentTypeRepository.ContentTypeQueries.MapContentType(99997, DatabaseContext.Database, out assocatedTemplates);
Assert.IsNotNull(contentType);
Assert.AreEqual(2, contentType.AllowedContentTypes.Count());
Assert.AreEqual(2, assocatedTemplates.Count());
}
[Test]
public void Can_Map_All_Property_Groups_And_Types()
{
@@ -26,17 +68,17 @@ namespace Umbraco.Tests.Persistence.Querying
{
DatabaseContext.Database.Execute(new Sql(string.Format("SET IDENTITY_INSERT {0} ON ", SqlSyntaxContext.SqlSyntaxProvider.GetQuotedTableName("umbracoNode"))));
DatabaseContext.Database.Insert("umbracoNode", "id", false, new NodeDto { NodeId = 55555, Trashed = false, ParentId = -1, UserId = 0, Level = 1, Path = "-1,55555", SortOrder = 1, UniqueId = new Guid("3390BDF4-C974-4211-AA95-3812A8CE7C46"), Text = "Test Data Type", NodeObjectType = new Guid(Constants.ObjectTypes.DataType), CreateDate = DateTime.Now });
DatabaseContext.Database.Insert("umbracoNode", "id", false, new NodeDto { NodeId = 99999, Trashed = false, ParentId = -1, UserId = 0, Level = 0, Path = "-1,2222", SortOrder = 0, UniqueId = new Guid("129241F0-D24E-4FC3-92D1-BC2D48B7C431"), Text = "Test Content Type", NodeObjectType = new Guid(Constants.ObjectTypes.DocumentType), CreateDate = DateTime.Now });
DatabaseContext.Database.Insert("umbracoNode", "id", false, new NodeDto { NodeId = 99999, Trashed = false, ParentId = -1, UserId = 0, Level = 0, Path = "-1,99999", SortOrder = 0, UniqueId = new Guid("129241F0-D24E-4FC3-92D1-BC2D48B7C431"), Text = "Test Content Type", NodeObjectType = new Guid(Constants.ObjectTypes.DocumentType), CreateDate = DateTime.Now });
DatabaseContext.Database.Execute(new Sql(string.Format("SET IDENTITY_INSERT {0} OFF ", SqlSyntaxContext.SqlSyntaxProvider.GetQuotedTableName("umbracoNode"))));
DatabaseContext.Database.Execute(new Sql(string.Format("SET IDENTITY_INSERT {0} ON ", SqlSyntaxContext.SqlSyntaxProvider.GetQuotedTableName("cmsDataType"))));
DatabaseContext.Database.Insert("cmsDataType", "pk", false, new DataTypeDto { PrimaryKey = 44444, DataTypeId = 55555, PropertyEditorAlias = Constants.PropertyEditors.TextboxAlias, DbType = "Nvarchar" });
DatabaseContext.Database.Execute(new Sql(string.Format("SET IDENTITY_INSERT {0} OFF ", SqlSyntaxContext.SqlSyntaxProvider.GetQuotedTableName("cmsDataType"))));
DatabaseContext.Database.Execute(new Sql(string.Format("SET IDENTITY_INSERT {0} ON ", SqlSyntaxContext.SqlSyntaxProvider.GetQuotedTableName("cmsContentType"))));
DatabaseContext.Database.Insert("cmsContentType", "pk", false, new ContentTypeDto { PrimaryKey = 88888, NodeId = 99999, Alias = "TestContentType", Icon = "icon-folder", Thumbnail = "folder.png", IsContainer = false, AllowAtRoot = true });
DatabaseContext.Database.Execute(new Sql(string.Format("SET IDENTITY_INSERT {0} OFF ", SqlSyntaxContext.SqlSyntaxProvider.GetQuotedTableName("cmsContentType"))));
DatabaseContext.Database.Execute(new Sql(string.Format("SET IDENTITY_INSERT {0} ON ", SqlSyntaxContext.SqlSyntaxProvider.GetQuotedTableName("cmsPropertyTypeGroup"))));
DatabaseContext.Database.Insert("cmsPropertyTypeGroup", "id", false, new PropertyTypeGroupDto { Id = 77776, ContentTypeNodeId = 99999, Text = "Group1", SortOrder = 1 });
DatabaseContext.Database.Insert("cmsPropertyTypeGroup", "id", false, new PropertyTypeGroupDto { Id = 77777, ContentTypeNodeId = 99999, Text = "Group2", SortOrder = 2 });
@@ -59,7 +101,7 @@ namespace Umbraco.Tests.Persistence.Querying
PropertyTypeCollection propTypeCollection;
PropertyGroupCollection propGroupCollection;
ContentTypeRepository.ContentTypeQueries.PopulateGroupsAndProperties(99999, DatabaseContext.Database, out propTypeCollection, out propGroupCollection);
ContentTypeRepository.ContentTypeQueries.MapGroupsAndProperties(99999, DatabaseContext.Database, out propTypeCollection, out propGroupCollection);
Assert.AreEqual(4, propGroupCollection.Count);
Assert.AreEqual(2, propGroupCollection["Group1"].PropertyTypes.Count);
@@ -72,7 +114,7 @@ namespace Umbraco.Tests.Persistence.Querying
Assert.IsTrue(propGroupCollection["Group3"].PropertyTypes.Contains("property6"));
Assert.IsTrue(propGroupCollection["Group3"].PropertyTypes.Contains("property7"));
Assert.AreEqual(0, propGroupCollection["Group4"].PropertyTypes.Count);
Assert.AreEqual(1, propTypeCollection.Count);
Assert.IsTrue(propTypeCollection.Contains("property5"));
@@ -40,10 +40,26 @@ namespace Umbraco.Tests.Services
//NOTE: Doing this the old 1 by 1 way and based on the results of the ContentServicePerformanceTest.Retrieving_All_Content_In_Site
// the old way takes 143795ms, the new above way takes: 14249ms that is a 90% savings of processing and sql calls!
var contentType1 = MockedContentTypes.CreateTextpageContentType("test1", "test1");
var contentType1 = MockedContentTypes.CreateTextpageContentType("test1", "test1");
var contentType2 = MockedContentTypes.CreateTextpageContentType("test2", "test2");
var contentType3 = MockedContentTypes.CreateTextpageContentType("test3", "test3");
var contentType3 = MockedContentTypes.CreateTextpageContentType("test3", "test3");
ServiceContext.ContentTypeService.Save(new[] {contentType1, contentType2, contentType3});
contentType1.AllowedContentTypes = new[]
{
new ContentTypeSort(new Lazy<int>(() => contentType2.Id), 0, contentType2.Alias),
new ContentTypeSort(new Lazy<int>(() => contentType3.Id), 1, contentType3.Alias)
};
contentType2.AllowedContentTypes = new[]
{
new ContentTypeSort(new Lazy<int>(() => contentType1.Id), 0, contentType1.Alias),
new ContentTypeSort(new Lazy<int>(() => contentType3.Id), 1, contentType3.Alias)
};
contentType3.AllowedContentTypes = new[]
{
new ContentTypeSort(new Lazy<int>(() => contentType1.Id), 0, contentType1.Alias),
new ContentTypeSort(new Lazy<int>(() => contentType2.Id), 1, contentType2.Alias)
};
ServiceContext.ContentTypeService.Save(new[] { contentType1, contentType2, contentType3 });
var roots = MockedContent.CreateTextpageContent(contentType1, -1, 10);
ServiceContext.ContentService.Save(roots);
+1 -1
View File
@@ -298,7 +298,7 @@
<Compile Include="Mvc\MergeParentContextViewDataAttributeTests.cs" />
<Compile Include="Mvc\ViewDataDictionaryExtensionTests.cs" />
<Compile Include="Persistence\PetaPocoExtensionsTest.cs" />
<Compile Include="Persistence\Querying\PropertyTypeRelatorTests.cs" />
<Compile Include="Persistence\Querying\ContentTypeSqlMappingTests.cs" />
<Compile Include="Persistence\Repositories\MacroRepositoryTest.cs" />
<Compile Include="Persistence\Repositories\MemberRepositoryTest.cs" />
<Compile Include="Persistence\Repositories\MemberTypeRepositoryTest.cs" />