I have the following entity in my solution:
public class Company : FullAuditedEntity<int>
{
[Required]
[MinLength(2)]
[MaxLength(48)]
public virtual string CompanyName { get; set; }
[Required]
[MinLength(2)]
[MaxLength(48)]
public virtual string CompanyLegalName { get; set; }
[MaxLength(48)]
public virtual string CompanyTaxId { get; set; }
//some others properties removed to keep this simple
[Required]
public virtual bool ActiveYesNo { get; set; }
}
My application design keeps ALL addresses for company and any other entity in one master Address table, defined below.
public class Address : FullAuditedEntity<Int64>
{
[Required]
[MaxLength(10)]
public virtual string AddressType { get; set; }
[Required]
[MinLength(5)]
[MaxLength(100)]
public virtual string AddressLine1 { get; set; }
[Required]
[MinLength(3)]
[MaxLength(100)]
public virtual string City { get; set; }
[Required]
public virtual Country Country { get; set; }
[Required]
[MaxLength(10)]
public virtual string PostalCode { get; set; }
[Required]
public virtual bool DefaultYN { get; set; }
}
So in order to maintain the multiple addresses for the company entity, I have the table below.
public class CompanyAddress : Entity
{
public virtual Company Company { get; set; }
public virtual Address Address { get; set; }
}
Then I added this property into the company table.
public virtual CompanyAddress Addresses { get; set; }
After that, when I run the "Add-Migration" command I get this error:
Unable to determine the principal end of an association between the types 'CompanyAddress' and 'Company'. The principal end of this association must be explicitly configured using either the relationship fluent API or data annotations
Then after a google search for the error, I found this post: [http://stackoverflow.com/questions/6531671/what-does-principal-end-of-an-association-means-in-11-relationship-in-entity-fr])
After reading that, I applied the below change to companyAddress table:
public class CompanyAddress : Entity
{
[Required]
public virtual Company Company { get; set; }
public virtual Address Address { get; set; }
}
This allowed me to complete the "Add-Migration" command without any errors.
Here is the code for the migration:
public override void Up()
{
DropIndex("dbo.app_CompanyAddress", new[] { "Company_Id" });
DropColumn("dbo.app_CompanyAddress", "Id");
RenameColumn(table: "dbo.app_CompanyAddress", name: "Company_Id", newName: "Id");
DropPrimaryKey("dbo.app_CompanyAddress");
AlterColumn("dbo.app_CompanyAddress", "Id", c => c.Int(nullable: false));
AlterColumn("dbo.app_CompanyAddress", "Id", c => c.Int(nullable: false));
AddPrimaryKey("dbo.app_CompanyAddress", "Id");
CreateIndex("dbo.app_CompanyAddress", "Id");
}
public override void Down()
{
DropIndex("dbo.app_CompanyAddress", new[] { "Id" });
DropPrimaryKey("dbo.app_CompanyAddress");
AlterColumn("dbo.app_CompanyAddress", "Id", c => c.Int());
AlterColumn("dbo.app_CompanyAddress", "Id", c => c.Int(nullable: false, identity: true));
AddPrimaryKey("dbo.app_CompanyAddress", "Id");
RenameColumn(table: "dbo.app_CompanyAddress", name: "Id", newName: "Company_Id");
AddColumn("dbo.app_CompanyAddress", "Id", c => c.Int(nullable: false, identity: true));
CreateIndex("dbo.app_CompanyAddress", "Company_Id");
}
However, when I apply the "Update-Database" command, I get the following error:
The object 'PK_dbo.app_CompanyAddress' is dependent on column 'Id'.
ALTER TABLE DROP COLUMN Id failed because one or more objects access this column.
Can you please tell me what I am doing wrong or what I am missing in order to setup this relationship properly? Thanks!
Hi @ismcagdas...Thank you that worked!
I am very interested in doing the same thing. My application displays create date + create user name and modified date + modified user name on every entity UI page.
For the index/list view I was able to use the below code, from an ABP example. This joins to the user table and puts the name into my custom "createusername" column in my Dto.
private IQueryable<CompanyUserAndAllFK> CompanyListingQuery(GetCompanyInput input)
{
var query = from cmp in _companyRepository.GetAllCompanies()
join listval in _listvaluesRepository.GetAll() on cmp.CurrencyCode equals listval.ListValue
join user in _userRepository.GetAll() on cmp.CreatorUserId equals user.Id into userJoin
from joinedUser in userJoin.DefaultIfEmpty()
select new CompanyUserAndAllFK { Company = cmp, User = joinedUser, ListValues = listval };
query = query
.WhereIf(!input.Filter.IsNullOrWhiteSpace(), comp => comp.Company.CompanyName.Contains(input.Filter) || comp.Company.CompanyLegalName.Contains(input.Filter) );
return query;
}
I hope this helps?
I was able to find one solution. I added line 1 below just before the "MapTo" and that is allowing me to get the "Id" for Note.
input.Note.Id = (int)cmp.Note.Id;
input.MapTo(cmp);
await _companyRepository.UpdateAsync(cmp);
Please let me know if there is an alternate to this solution. I would prefer the view model send it back to the app service. Thanks!
In my Edit modal I have these hidden fields
<input type="hidden" name="Id" value="@Model.Company.Id" />
<input type="hidden" name="CompanyName" value="@Model.Company.CompanyName" />
<input type="hidden" name="Note.Id" value="@Model.Company.Note.Id" />
Here is the model that is returned to my UpdateCompany method: [attachment=0:167vzn0s]Screenshot (16).png[/attachment:167vzn0s] As you can see above the Note.Id is always coming back as NULL to the UpdateCompany method in CompanyAppService.
When I change the name of this field, in the HTML, to "Id", it overlays the "Company Id" column so that does not work.
<input type="hidden" name="Note.Id" value="@Model.Company.Note.Id" />
How can I get the page to return both ID numbers back to my app service method?
I have sent you email.
Here is the original code in my Sidebar.cshtml
//Using statements here
@{
var calculateMenuUrl = new Func<string, string>((url) =>
{
if (string.IsNullOrEmpty(url))
{
return ApplicationPath;
}
if (UrlChecker.IsRooted(url))
{
return url;
}
return ApplicationPath + url;
});
}
<div class="page-sidebar navbar-collapse collapse">
<ul class="page-sidebar-menu" data-keep-expanded="false" data-auto-scroll="true" data-slide-speed="200">
@for (var i = 0; i < Model.Menu.Items.Count; i++)
{
var menuItem = Model.Menu.Items[i];
<li class="@(i==0 ? "start" : "") @(menuItem.IsMenuActive(Model.CurrentPageName) ? "active" : "")">
@if (menuItem.Items.IsNullOrEmpty())
{
<a href="@calculateMenuUrl(menuItem.Url)">
<i class="@menuItem.Icon"></i>
<span class="title">@menuItem.DisplayName</span>
</a>
}
else
{
<a href="javascript:;" class="auto">
<i class="@menuItem.Icon"></i>
<span class="title">@menuItem.DisplayName</span>
<span class="arrow"></span>
</a>
<ul class="sub-menu">
@foreach (var childMenuItem in menuItem.Items)
{
<li class="@(childMenuItem.IsMenuActive(Model.CurrentPageName) ? "active" : "")">
@if (childMenuItem.Items.IsNullOrEmpty())
{
<a href="@calculateMenuUrl(childMenuItem.Url)">
<span><i class="sub-menu-icon @childMenuItem.Icon"></i> @childMenuItem.DisplayName</span>
</a>
}
else
{
<a href="javascript:;" class="auto">
<i class="@childMenuItem.Icon"></i>
<span class="title">@childMenuItem.DisplayName</span>
<span class="arrow"></span>
</a>
<ul class="sub-menu">
@foreach (var secondLevelChildMenuItem in childMenuItem.Items)
{
<li class="@(secondLevelChildMenuItem.IsMenuActive(Model.CurrentPageName) ? "active" : "")">
<a href="@calculateMenuUrl(secondLevelChildMenuItem.Url)">
<span><i class="sub-menu-icon @secondLevelChildMenuItem.Icon"></i> @secondLevelChildMenuItem.DisplayName</span>
</a>
</li>
}
</ul>
}
</li>
}
</ul>
}
</li>
}
</ul>
</div>
I changedthese lines: <a href="javascript:;" class="auto"> to <a href="javascript:;" class="auto nav-link nav-toggle">
And it still did not work or fix the menu issue?
Also when i check my developer tools in chrome browser there are no JS related errors, before or after click on menu item.
Ok I removed all references to my entities in my DTO and it resolved the error, thanks!
The design pattern used in ABP is something new for me and in the few short weeks that I have been trying to convert my application into ABP design pattern I have learned quite a bit. :)
If you can please answer a couple of questions to further my understanding of the ABP pattern. I'm starting to understand it but how all the parts work and why they are needed is not quite clear.
Can you tell me how entities, DTOs and view models used in the ABP solution are intended to be used and what is the purpose of each? If you could just provide a brief description on each one and why they are all used in this manner? Thanks!
<cite>ismcagdas: </cite> That is good :).
Do you have any javascript error your browser's developer console ?
And one more thing. I think you need to use
<script src="../assets/layouts/layout/scripts/layout.min.js" type="text/javascript"></script>
instead of
<script src="../assets/layouts/layout4/scripts/layout.min.js" type="text/javascript"></script>
Can you also check that ?
What file are you referring to there? Can you be more specific?
[AutoMapTo(typeof(NoteHeader))]
public class EditNoteHeaderDto : EntityDto
{
public NoteHeader Note { get; set; }
}
Added noteDTO into companyEditDto:
[Required]
public bool ActiveYesNo { get; set; }
public EditNoteHeaderDto Note { get; set; }
public DateTime CreationTime { get; set; }
Still getting the same error:
<span style="color:#FF0000">AutoMapper.AutoMapperMappingException: Missing type map configuration or unsupported mapping.
Mapping types:
List1 -> EditCompanyDto System.Collections.Generic.List
1[[EXLNT.NursingOps17.NursingOps.Dto.EditCompanyDto, EXLNT.NursingOps17.Application, Version=1.13.0.0, Culture=neutral, PublicKeyToken=null]] -> EXLNT.NursingOps17.NursingOps.Dto.EditCompanyDto
</span>