Map to view on custom field name - one-to-one relationship - view

I am unable to get NHibernate to map the AccountCode column of the Beneficiary table AccountCode column in this one-to-one relationship (each Account has a single Beneficiary, each Beneficiary has a single Account).
Classes:
public class Account
{
...
public virtual string Name { get; protected set; }
public virtual string Code { get; protected set; }
}
public class Beneficiary
{
...
public virtual int Id { get; set; }
public virtual string Name { get; protected set; }
public virtual Account Account { get; protected set; }
public virtual BeneficiaryGroup Group { get; protected set; }
}
SQL:
CREATE VIEW dbo.Account AS
SELECT DISTINCT RTRIM(LTRIM(ACCNT_NAME)) AS Name,
RTRIM(LTRIM(ACCNT_CODE)) AS Code
FROM myremoteserver.schema.tablename
WHERE ACCNT_TYPE NOT IN ('B', 'P')
CREATE TABLE dbo.Beneficiary
(
Id INT IDENTITY(1,1) NOT NULL,
BeneficiaryGroupId INT NOT NULL CONSTRAINT FK_Beneficiaries_BeneficiaryGroup FOREIGN KEY REFERENCES dbo.BeneficiaryGroup (Id),
Name VARCHAR(100) NOT NULL,
AccountCode VARCHAR(100) NOT NULL,
CONSTRAINT PK_Beneficiary PRIMARY KEY (Id)
)
When trying to use HasMany and different variants, NHibernate tries to join on the Beneficiary.Id column.
I had tried different variations of Map, References, Join (which tells me that the join already exists) and HasMany (which fails, as the relationship is indeed one-to-one).
How can I get NHibernate to map these two classes correctly to their columns?
When trying the different fluent mappings, in my IAutoMappingOverride<Beneficiary>, the following happens:
mapping.HasOne(b => b.Account);
mapping.HasOne(b => b.Account).PropertyRef(sa => sa.Code);
mapping.HasOne(b => b.Account).PropertyRef(sa => sa.Code).ForeignKey("none");
The generated SQL uses the Beneficiary.Id field instead of the Beneficiary.AccountCode. (before you ask, I am using "none" since Account is a view, it can't have a key).
mapping.Join("AccountCode", x => x.References(y => y.Account));
mapping.Join("Account", b => b.KeyColumn("AccountCode"));
Result in Tried to add join to table 'Account' when already added..
And:
mapping.Map(b => b.Account, "AccountCode");
mapping.Map(b => b.Account).ReadOnly().Column("AccountCode");
Result in:
Could not determine type for: .Account, , Version=1.0.0.0, Culture=neutral, PublicKeyToken=null, for columns:
NHibernate.Mapping.Column(AccountCode)
mapping.References(b => b.Account).Column("Code");
Results in Invalid column name 'Code'..
And:
mapping.References(b => b.Account).Column("AccountCode");
mapping.References(b => b.Account).Column("AccountCode").Access.Property();
Overrides all my IReferenceConvention overrides (mapping some classes to have a <class name>Code key column).
When trying HasMany:
mapping.HasMany<Account>(b => b.Account).KeyColumn("AccountCode");
Custom type does not implement UserCollectionType: .Account

// in BeneficiaryMapping
mapping.References(b => b.Account)
.Column("AccountCode" /* of Beneficiary table*/)
.PropertyRef(a => a.Code); // use the Column of Code as the joincolumn in Account table

This approach is similar to Firo's but in a simpler mapping. It uses References like he suggest because that's how NH allows you select a property other than the entity primary key property to reference another type. The PropertyRef is not necessary since NH "knows" its going to use the AccountCode value as the the "key" into the Account view.
Here is how to map Account:
public class AccountMap : ClassMap<Account>
{
public AccountMap()
{
// Code as a psuedo primary key in the view:
Id(acc => acc.Code)
.GeneratedBy.Assigned();
Map(acc => acc.Name);
// Add other mappings here...
// Ensure NH doesn't try to update the lookup view:
ReadOnly();
}
}
Here is how Beneficiary looks mapped:
public class BeneficiaryMap : ClassMap<Beneficiary>
{
public BeneficiaryMap()
{
Id(b => b.Id)
.GeneratedBy.Identity()
.UnsavedValue(0);
Map(b => b.Name);
// Other mapped properties...
References<BeneficiaryGroup>(b => b.Group, "BeneficiaryGroupId");
References<Account>(b => b.Account, "AccountCode");
}
}

Related

are we able to Include(x=>x.Entity) then Select(x=> new Entity{ A=x.A })

I'm trying to call my CompanyCatalog table with its FileRepo table. There is a One to One relationship between them and i wanna apply when i Include(x=>x.FileRepo.Select(a=> new{ FileName=a.FileNAme} )) or any query like that.
Let me show to you my query in the bellow :
return TradeTurkDBContext.CompanyCatalog.Include(x=>x.FileRepo
.Select(x=> new FileRepo(FileName=x.FileName))).AsNoTracking().ToList();
I'm trying to do something like that. I'm asking is it possible or not ? if it's possible then how ?
So you have a table of CompanyCatalogs and a table of FileRepos. Every CompanyCatalog has exactly one FileRepo (one-to-one), namely the one that the foreign key refers to.
If you've followed the entity framework conventions, you will have classes similar to the following:
class CompanyCatalog
{
public int Id {get; set;}
public string Name {get; set;}
... // other properties
// every CompanyCatalog has one FileRepo, the one that the foreign key refers to
public int FileRepoId {get; set;}
public virtual FileRepo FileRepo {get; set;}
}
class FileRepo
{
public int Id {get; set;}
public string Name {get; set;}
... // other properties
// every FileRepo is the FileRepo of exactly one CompanyCatalog
// namely the one that the foreign key refers to
public int CompanyCatalogId {get; set;}
public virtual CompanyCatalog CompanyCatalog {get; set;}
}
This is enough for entity framework to detect your tables, the columns in the tables and the relations between the tables. If you had a one-to-many, you would have had a virtual ICollectioni<...> on the "one side". Only if you deviate from the conventions, for instance because you want other table names, or other column names, you need attributes or fluent API.
In entity framework the columns are represented by non-virtual properties. The virtual properties represent the relations between the tables (one-to-many, many-to-many, etc)
Foreign keys are columns in a table, hence they are not virtual. FileRepo is no column in the CompanyCatalogs table, hence it is declared virtual.
You want several properties of CompanyCatalogs, each with several properties of their FileRepos. You use Include for this. This is not necessary. A simple Select will do.
var companyCatalogs = dbContext.CompanyCatalogs
.Where(catalog => ...) // only if you don't want all CompanyCatalogs
.Select(companyCatalog => new
{
// Select only the CompanyCatalog properties that you plan to use:
Id = companyCatalog.Id,
Name = companyCatalog.Name,
...
// Select the FileRepo of this CompanyCatalog as one sub object
FileRepo = new
{
Date = companyCatalog.FileRepo.Date,
Title = companyCatalog.FileRepo.Title,
...
},
// if you want you can select the FileRepo properties one by one:
FileRepoDate = companyCatalog.FileRepo.Date,
FileRepoTitle = companyCatalog.FileRepo.Title,
});
Entity Framework knows your relations, and because you used the virtual properties of the class, it knows it has to perform a (Group-)Join.

Querying many to many table in EF Core/LINQ [duplicate]

This question already has answers here:
Many-to-many query in Entity Framework 7
(4 answers)
Closed 2 years ago.
I have three tables: Posts, Tags and PostTags (link table between Post and Tag). How can I write a query to get all Posts by a TagId?
DB structure:
public class Post {
public string Id {get;set;}
public string Content {get;set;}
public List<PostTag> PostTags {get;set;}
}
public class Tag {
public string Id {get;set;}
public string Name {get;set;}
public List<PostTag> PostTags {get;set;}
}
public class PostTag
{
public string PostId { get; set; }
public Post Post { get; set; }
public string TagId { get; set; }
public Tag Tag { get; set; }
}
Relationships:
builder.Entity<PostTag>()
.HasKey(x => new { x.PostId, x.TagId });
builder.Entity<PostTag>()
.HasOne(st => st.Post)
.WithMany(s => s.PostTags)
.HasForeignKey(st => st.PostId);
builder.Entity<PostTag>()
.HasOne(st => st.Tag)
.WithMany(s => s.PostTags)
.HasForeignKey(st => st.TagId);
If you've followed the entity framework code first conventions, there are two methods to query "Posts with their Tags"
The easy way: Use the virtual ICollection<Tag> to get the tags of each post.
Do the (group-)join yourself.
Use the irtual ICollection
Your classes will be similar to the following:
class Post
{
public int Id {get; set;}
... // other properties
// every Post has zero or more Tags (many-to-many)
public virtual ICollection<Tag> Tags {get; set;}
}
class Tag
{
public int Id {get; set;}
... // other properties
// every Tag is used by zero or more Posts (many-to-many)
public virtual ICollection<Post> Posts {get; set;}
}
This is all that entity framework needs to know the many-to-many relation between Posts and Tags. You even don't have to mention the junction table, entity framework will create a standard table for you, and use it whenever needed. Only if you want non-standard names for tables and or columns, you need Attributes or fluent API.
In entity framework, the columns of the tables are represented by the non-virtual properties; the virtual properties represent the relations between the tables (one-to-many, many-to-many, ...)
To get all (or some) Posts, each with all (or some of) their Tables, you can use the virtual ICollection:
var postsWithTheirTags = dbContext.Posts
// only if you don't want all Posts:
.Where(post => ...)
.Select(post => new
{
// Select only the Post properties that you plan to use:
Id = post.Id,
Author = post.Author,
...
Tags = post.Tags.Select(tag => new
{
// again: only the properties that you plan to use
Id = tag.Id,
Text = tag.Text,
...
})
.ToList(),
});
Entity framework knows your relation and will automatically create a Group-join for you using the proper junction table.
This solutions seems to me the most natural one.
Do the GroupJoin yourself
For this you need to have access to the junction table, you'll have to mention it in your dbContext, and use fluent API to tell entity framework that this is the junction table for the many-to-many relation between Posts and Tags.
var postsWithTheirTags = dbContext.Posts.GroupJoin(dbContext.PostTags,
post => post.Id, // from every Post take the primary key
postTag => postTag.PostId // from every PostTag take the foreign key to Post
(post, postTagsOfThisPost) => new
{
// Post properties:
Id = post.Id,
Title = post.Title,
...
Tags = dbContext.Tags.Join(postTagsOfThisPost,
tag => tag.Id // from every Tag take the primary key
postTag => postTag.TagId // from every postTagOfThisPost take the foreign key
(tag, postTagfThisPostAndThisTag) => new
{
Id = tag.Id,
Text = tag.Text,
...
})
.ToList(),
});
You can try this:
public List<Posts> GetPosts(string needTagID)
{
var dataQuery = from tags in _db.Tags
where needTagID == tags.Id
join postTags in _db.PostTags on tags.Id equals postTags.TagId
join posts in _db.Posts on postTags.PostId equals posts.Id
select posts;
var data = dataQuery.ToList();
}

Net Core - .Include() results in loop causing Visual Studio debug to crash

My database has a one-to-many relation between "UsageRecord" and "Dimension"
This is modelled as follows (using a Database-First approach):
public partial class Dimension
{
...
public virtual ICollection<UsageRecord> UsageRecord { get; set; }
}
Usage Record class:
public partial class UsageRecord
{
public long Id { get; set; }
...
public long DimensionId { get; set; }
public virtual Dimension Dimension { get; set; }
}
So, if i query the list of UsageRecords (EagerLoading):
_context.Set<UsageRecord>.Where(x => x.ProductId == productId).ToList()
i get a list of UsageRecord objects I can navigate through during debug:
Please notice that the Dimension object is null, and this is correct since i haven't included it in the query.
Now, If i try to include it, the application crashes:
_context.Set<UsageRecord>.Where(x => x.ProductId == productId).Include(p => p.Dimension).ToList();
Postman exits with a 502 error, and the VS Debug first shows a list of question marks "?" before crashing.
I think this is due to the fact that by Including the Dimension object, this loops through the list of UsageRecords attached and then the Dimension again and again.
How can I avoid it?
In order to retrieve your result from LINQ query you can solve your issue in these ways:
Configure your serializer to ignore loops
Create a view model for your controller's action
Use anonymous type from Select result in your controller's action

How to combine linq queries and selecting distinct records?

I have an object that can have a single user assigned to it or a work group. A user may be assigned directly or though a work group, but the object can never have both set.
public class Procedure
{
.....
public Guid? AssignedToId {get;set;} //Foreign Key to AssignedTo
public Contact AssignedTo {get;set;} //Single user assignment
public Guid? AssignedWorkGroupId {get;set;} //Foreign Key to AssignedWorkGroup
public WorkGroup AssignedWorkGroup {get;set;} //Multiple user assignment
public Guid? AssignedBuisnessPartnerId {get;set;}
public BusinessPartner AssignedBuisnessPartner {get;set;}
}
I am trying to figure out how to write a single query where I can find procedures where a user may be assigned directly or is part of a work group that is assigned. Currently I have 2 separate queries and combining the lists I get back. Which works, but probably not as efficient.
Here is what I have now:
var procedures = _procedureRepository.Get(p => p.AssignedToId == assignedId).ToList();
procedures.AddRange(_procedureRepository.Get(p => p.AssignedWorkGroup.Contacts.Select(c => c.Id).Contains(assignedId) || p.AssignedBuisnessPartner.Contacts.Select(c => c.Id).Contains(assignedId));
It looks like you are looking for a Union All in sql, which is equivalent to Concat in linq. The following code will only execute one call to the database. Not sure if it will be faster than your current method.
var procedures2 = _procedureRepository.Get(p => p.AssignedWorkGroup.Contacts
.Select(c => c.Id)
.Contains(assignedId) ||
p.AssignedBuisnessPartner.Contacts
.Select(c => c.Id)
.Contains(assignedId));
var procedures = _procedureRepository.Get(p => p.AssignedToId == assignedId)
.Concat(procedures2);

How to access extra data in Many-to-Many relationship in CoolStorage?

I'm using CoolStorage in a project where I have some Many-to-Many relationships. Some of the join tables have extra data on them which describe the relationship.
For example: Table Alpha, Beta, and AlphaBeta.
Many-to-Many relationship between Alpha and Beta is stored in AlphaBeta. Primary key of AlphaBeta is the combination of the keys from Alpha(AlphaID) and Beta(BetaID), which is (AlphaID, BetaID).
But AlphaBeta also has some additional data like 'DisplayOrder INT NOT NULL'
in the data classes, I have the Many-to-Many relationships defined using the [ManyToMany("AlphaBeta", pure=true)] attribute, but how can I access the DisplayOrder for each?
I don't think it matters, but this is a Windows Phone app using SQLite.
If you have additional fields in your link table, you have to set "pure = false" and add a data object for the link table.
Your link data object might look like this:
[MapTo("AlphaBeta")]
public abstract class AlphaBeta : CSObject<AlphaBeta>
{
[ManyToOne]
public abstract Alpha Aplha { get; set; }
[ManyToOne]
public abstract Beta Beta { get; set; }
public abstract int DisplayOrder { get; set; }
}

Resources