I'm testing out using LINQ with NHibernate but have run into some problems with resolving string.length. I have the following
public class DC_Control
{
public virtual int ID { get; private set; }
public virtual string Name { get; set; }
public virtual bool IsEnabled { get; set; }
public virtual string Url { get; set; }
public virtual string Category { get; set; }
public virtual string Description { get; set; }
public virtual bool RequireScriptManager { get; set; }
public virtual string TriggerQueryString { get; set; }
public virtual DateTime? DateAdded { get; set; }
public virtual DateTime? DateUpdated { get; set; }
}
public class DC_ControlMap : ClassMap<DC_Control>
{
public DC_ControlMap()
{
Id(x => x.ID);
Map(x => x.Name).Length(128);
Map(x => x.IsEnabled);
Map(x => x.Url);
Map(x => x.Category);
Map(x => x.Description);
Map(x => x.RequireScriptManager);
Map(x => x.TriggerQueryString);
Map(x => x.DateAdded);
Map(x => x.DateUpdated);
}
}
private static ISessionFactory CreateSessionFactory()
{
return Fluently.Configure()
.Database(FluentNHibernate.Cfg.Db.MsSqlConfiguration.MsSql2008)
.Mappings(m => m.FluentMappings.AddFromAssembly(Assembly.GetExecutingAssembly()))
.ExposeConfiguration(c => c.SetProperty("connection.connection_string", "CONNSTRING"))
.ExposeConfiguration(c => c.SetProperty("proxyfactory.factory_class", "NHibernate.ByteCode.Castle.ProxyFactoryFactory,NHibernate.ByteCode.Castle"))
.BuildSessionFactory();
}
public static void test()
{
using (ISession session = sessionFactory.OpenSession())
{
var sqlQuery = session.CreateSQLQuery("select * from DC_Control where LEN(url) > 80").AddEntity(typeof(DC_Control)).List<DC_Control>();
var linqQuery= session.Linq<DC_Control>().Where(c => c.Url.Length > 80).ToList();
}
}
In my test method I first try and perform the query using SQL, this works just fine. Then I want to do the same thing in LINQ, and it throws the following error:
NHibernate.QueryException: could not resolve property: Url.Length of: DC_Control
I've searched alot for this "could not resolve property" error, but I can't quite figure out, what this means. Is this because the LINQ implementation is not complete? If so it's a bit disappointing coming from Linq2Sql where this would just work.
I also tried it setting up the mapping with a hbm.xml instead of using FluentNHibernate but it produced teh same error.
String.Length projections are not supported by the old (2.x) Linq provider.
The new Linq provider in NHibernate 3.x is integrated; you don't have to download anything extra.
The new extension method is session.Query instead of session.Linq; if you're using the latter, you are still using the old provider.
Related
I using Ef Core 2.2.and I have an Entity It has Self-Relation And Each Entity Might have a List Of Entities
This Is My Entity
public class CourseGroup
{
public int ID { get; set; }
public string Title { get; set; }
public bool IsDeleted { get; set; }
//Foreign key
public int? ParentId { get; set; }
//Navigations Property
public CourseGroup ParentCourseGroup { get; set; }
//Relatons => Self Relation
public ICollection<CourseGroup> Groups { get; set; }
}
And This Is My Configurations
class CourseGroupConfig : IEntityTypeConfiguration<CourseGroup>
{
public void Configure(Microsoft.EntityFrameworkCore.Metadata.Builders.EntityTypeBuilder<CourseGroup> builder)
{
builder.HasKey(c => c.Id);
builder.Property(c => c.Id).ValueGeneratedOnAdd();
builder.Property(c => c.Title).HasMaxLength(60);
//Relations
builder.HasOne(c => c.ParentCourseGroup).WithMany(c => c.Groups).HasForeignKey(c => c.ParentId);
}
}
And I Wanna get All Entities With Sub Entities. How Can I Write This Query With Linq?
Please Do me a favor to Write This Query
I am working on .NET Core application with Entity Framework Core. I have three tables Questions, Answer and AnswerType
so schema is as
question <-- 1:* --> Answers <-- 1:1--> AnswerTypes
I need to run query that return questions with ICollection of Answer , further Answer with AnswerType
Question
public class QuestionDataModel
{
public QuestionDataModel()
{
Answers = new HashSet<AnswerDataModel>();
}
public Guid Id { get; set; }
public virtual ICollection<AnswerDataModel> Answers { get; set; }
}
Answer
public class AnswerDataModel
{
public AnswerDataModel()
{
}
public Guid Id { get; set; }
public Guid QuestionId { get; set; }
public virtual QuestionDataModel Question { get; set; }
public string Value { get; set; }
public Guid AnswerStatusTypeId { get; set; }
public virtual AnswerStatusTypeDataModel AnswerStatusType { get; set; }
}
AnswerStatusType
public class AnswerStatusTypeDataModel
{
public AnswerStatusTypeDataModel()
{
Answers = new HashSet<AnswerDataModel>();
}
public Guid Id { get; set; }
public string Name { get; set; }
public virtual ICollection<AnswerDataModel> Answers { get; set; }
}
I have tried nested join to get AnswerStatusType of each answer in collection but getting error "invalid anonymous type member declared, anonymous type member must be declared with a member assignment, simple name or member access". This error appears in 2nd nested join in following code,
linq
var query3 = Context.Questions.Join(Context.Answers,
question => question.Id,
answer => answer.QuestionId,
(question, answer) => new
{
question.Id,
question.Title,
question.Answers.Join(Context.AnswerStatusTypes,
answer => answer.AnswerStatusTypeId,
answerStatus => answerStatus.Id,
(answers, answerStatus) => new
{
answerStatus
})
}
);
Configuration Classes
and configuration classes as
Question Config
public void Configure(EntityTypeBuilder<QuestionDataModel> builder)
{
builder.ToTable("Questions");
builder.HasKey(question => question.Id);
builder.HasMany(question => question.Answers);
}
Answer Config
public void Configure(EntityTypeBuilder<AnswerDataModel> builder)
{
builder.ToTable("Answers");
builder.HasKey(answer => answer.Id);
builder
.HasOne(answer => answer.Question)
.WithMany(question => question.Answers)
.HasForeignKey(answer => answer.QuestionId);
builder
.HasOne(answer => answer.AnswerStatusType)
.WithMany(answerType => answerType.Answers)
.HasForeignKey(answer => answer.AnswerStatusTypeId);
}
AnswerStatus COnfig
public void Configure(EntityTypeBuilder<AnswerStatusTypeDataModel> builder)
{
builder.ToTable("AnswerStatusTypes");
builder.HasKey(answerStatusType => answerStatusType.Id);
builder.HasMany(answerStatusType => answerStatusType.Answers);
}
Your entity configurations look correct to me.
As #Ivan Stoev pointed out,
var questions = context.Questions
.Include(x => x.Answers)
.ThenInclude(x => x.AnswerStatusType)
// Now since we have the AnswerStatusType loaded we can do something like this as well.
//.Where(x => x.Answers.Conatins(a => a.AnswerStatusType.Name == "Some Status Name"))
.ToList();
This should do!
var result = Context.Questions
.Include(x=>x.Answers.Select(y=>y.AnswerType))
.Where(x=>x.ID == QuestionidYouAreLookingFor)
.ToList();
make sure include System.Data.Entity namespace
When you call ToList() its actually going to make SQL call and execute the query. If you want lazy loading then don't call ToList()
i have a mapping configuration for a composite-key using fluent nhibernate and i need some help, because it is not working.
I have one class called Agencia that have a composite key for two Int16 collumns that are mapped as number on a oracle 11g database.
[Serializable]
public class AgenciaPK
{
[DisplayName("Código")]
public virtual System.Int16 CdAgencia { get; set; }
public virtual System.Int16 CdOrgaoArrecad { get; set; }
public override int GetHashCode()
{
int hashCode = 0;
hashCode = hashCode ^ CdAgencia.GetHashCode() ^ CdOrgaoArrecad.GetHashCode();
return hashCode;
}
public override bool Equals(object obj)
{
var toCompare = obj as Agencia;
if (toCompare == null)
{
return false;
}
return (this.GetHashCode() != toCompare.GetHashCode());
}
}
public class Agencia : AgenciaPK
{
public virtual OrgaoArrecad OrgaoArrecad { get; set; }
[DisplayName("Nome")]
public virtual string NmAgencia { get; set; }
[DisplayName("Digito")]
public virtual string NrDigitoAgencia { get; set; }
[DisplayName("Número")]
public virtual string NrAgencia { get; set; }
[DisplayName("Descrição")]
[StringLength(20)]
public virtual string DsCompl { get; set; }
[DisplayName("Logradouro")]
public virtual string NmLograd { get; set; }
[DisplayName("Bairro")]
public virtual string NmBairro { get; set; }
public virtual System.Nullable<short> IdSituacAgencia { get; set; }
[DisplayName("Cep")]
public virtual System.Nullable<short> NrCep { get; set; }
}
I have another class called Convenio that have a composite key composed by one string collumn (CdConvenio) and another is int CdOrgaoArrecad.
[Serializable]
public class ConvenioPK
{
[DisplayName("Código")]
[StringLength(20)]
public virtual string CdConvenio { get; set; }
[DisplayName("Cód. Órgão arrecadador")]
public virtual System.Int16 CdOrgaoArrecad { get; set; }
public override int GetHashCode()
{
int hashCode = 13;
if (CdConvenio != null)
hashCode = hashCode ^ CdConvenio.GetHashCode() ^ CdOrgaoArrecad.GetHashCode();
return hashCode;
}
public override bool Equals(object obj)
{
var toCompare = obj as Convenio;
if (toCompare == null)
{
return false;
}
return (this.GetHashCode() != toCompare.GetHashCode());
}
}
public class Convenio : ConvenioPK
{
[DisplayName("Órgão arrecadador")]
public virtual OrgaoArrecad OrgaoArrecad { get; set; }
[DisplayName("Data início")]
[DataType(DataType.Date)]
public virtual System.DateTime DtInicioConvenio { get; set; }
[DisplayName("Data término")]
[DataType(DataType.Date)]
public virtual System.DateTime DtFinalConvenio { get; set; }
[DisplayName("Tipo")]
public virtual short TpConvenio { get; set; }
[DisplayName("Tarifa")]
public virtual short TpTarifaConvenio { get; set; }
[DisplayName("Valor tarifa")]
public virtual Double VlTarifaConvenio { get; set; }
[DisplayName("Quantidade dias")]
public virtual short QtDiasFloat { get; set; }
}
And when i access this class Agencia using this mapping class AgenciaMap, it works fine.
public class AgenciaMap : FluentNHibernate.Mapping.ClassMap<Agencia>
{
public AgenciaMap() {
Table("SCI_AGENCIA");
CompositeId()
.KeyProperty(x => x.CdAgencia, "CD_AGENCIA")
.KeyProperty(x => x.CdOrgaoArrecad, "CD_ORGAO_ARRECAD");
Map(x => x.CdOrgaoArrecad).Column("CD_ORGAO_ARRECAD").Not.Nullable();
Map(x => x.NmAgencia).Column("NM_AGENCIA").CustomType("AnsiString").Length(30).Not.Nullable();
References<OrgaoArrecad>(x => x.OrgaoArrecad, "CD_ORGAO_ARRECAD").ForeignKey().Not.Update()
.Not.Insert()
.Cascade.None().Not.Nullable().Not.LazyLoad().Fetch.Join();
}
}
But, when i try to access the class Convenio, using the class ConvenioMap:
public class ConvenioMap : FluentNHibernate.Mapping.ClassMap<Convenio>
{
public ConvenioMap() {
Table("CONVENIO");
CompositeId()
.KeyProperty(x => x.CdConvenio, "CD_CONVENIO")
.KeyProperty(x => x.CdOrgaoArrecad, "CD_ORGAO_ARRECAD");
Map(x => x.CdConvenio).Column("CD_CONVENIO").CustomSqlType("varchar2(20)").CustomType("AnsiString").Length(20).Not.Nullable();
Map(x => x.CdOrgaoArrecad).Column("CD_ORGAO_ARRECAD").Not.Nullable();
Map(x => x.DtInicioConvenio).Column("DT_INICIO_CONVENIO").CustomSqlType("DATE").Not.Nullable();
Map(x => x.DtFinalConvenio).Column("DT_FINAL_CONVENIO").CustomSqlType("DATE").Not.Nullable();
Map(x => x.TpConvenio).Column("TP_CONVENIO").CustomSqlType("NUMBER(2,0)").Not.Nullable();
Map(x => x.TpTarifaConvenio).Column("TP_TARIFA_CONVENIO").CustomSqlType("NUMBER(2,0)").Not.Nullable();
Map(x => x.VlTarifaConvenio).Column("VL_TARIFA_CONVENIO").CustomSqlType("NUMBER(15,2)").Not.Nullable();
Map(x => x.QtDiasFloat).Column("QT_DIAS_FLOAT").CustomSqlType("NUMBER(3,0)").Not.Nullable();
References(x => x.OrgaoArrecad)
.Column("CD_ORGAO_ARRECAD")
.ForeignKey().Not.Update()
.Not.Insert()
.Cascade.None()
.Not.Nullable()
.Not.LazyLoad()
.Fetch.Join();
}
}
When i map a collumn that is the type string "CD_CONVENIO", i'm getting this error:
Wrong column type in SCI.CONVENIO for column CD_CONVENIO. Found: varchar2, Expected NVARCHAR2(255)
The specific part of my code that i'm in doubt is this:
CompositeId()
.KeyProperty(x => x.CdConvenio, "CD_CONVENIO")
.KeyProperty(x => x.CdOrgaoArrecad, "CD_ORGAO_ARRECAD");
So, here is my question: What am i doing wrong?
I have pretty simple linq expression:
session.Query<Order>().Where(x => x.States.OrderByDescending(z => z.Date).FirstOrDefault().Name == "2").ToList();
Result: InvalidCastException Unable to cast object of type 'Antlr.Runtime.Tree.CommonTree' to type 'NHibernate.Hql.Ast.ANTLR.Tree.IASTNode'.
Same query with LinqPad works as expected: selects orders, which last state is OnTheWay. How can I circumvent this and get desired result?
Code to try yourself:
class Program
{
static void Main(string[] args)
{
ISessionFactory sessionFactory = Fluently.Configure()
.Database(MsSqlConfiguration.MsSql2008
.ConnectionString(x => x.FromConnectionStringWithKey("defaultConnectionStringForNhibernate")))
.Mappings(m => m.FluentMappings.AddFromAssembly(Assembly.GetEntryAssembly()))
.BuildSessionFactory();
var session = sessionFactory.OpenSession();
var res2 =
session.Query<Order>().Where(x => x.States.OrderByDescending(z => z.Date).FirstOrDefault().Name == "2").ToList();
}
}
public class Order
{
public virtual int Id { get; set; }
public virtual IList<OrderState> States { get; set; }
public virtual string Name { get; set; }
}
public class OrderState
{
public virtual int Id { get; set; }
public virtual string Name { get; set; }
public virtual DateTime Date { get; set; }
public virtual Order Order { get; set; }
}
public class OrderMap : ClassMap<Order>
{
public OrderMap()
{
Id(x => x.Id)
.GeneratedBy.Identity();
HasMany(x => x.States)
.Inverse()
.AsBag()
.KeyColumn("OrderId");
Map(x => x.Name)
.Not.Nullable();
Table("Orders");
}
}
public class OrderStateMap : ClassMap<OrderState>
{
public OrderStateMap()
{
Id(x => x.Id)
.GeneratedBy.Identity();
References(x => x.Order)
.Column("OrderId");
Map(x => x.Name)
.Not.Nullable();
Map(x => x.Date)
.Not.Nullable();
Table("OrderStates");
}
}
After some time research I found solution:
var res = (from i in session.Query<Order>()
where ((from s in i.States
orderby s.Date descending
select s.Name).First()) == "2"
select i).ToList();
After, many hours of reading outdated documentation on the Internet for managing session and configuration of NHibernate / Fluent Nhibernate I actually got a configuration that works without using XML, my poco's and map files work in the WebProject and I almost got excited.
However; when I move helper class that returns ISessionFactory to the actual layer it should be in, nothing works, no errors, I get an ISession from the Session Factory just no Entites. Please note I am using the property in this class to get my Factory back, self explanatory I know.
Some sort of error would be great to go on here.
CODE:
public class NhibernateSessionFactoryHelper
{
private static ISessionFactory _sessionFactory;
private static string _connectionString =
ConfigurationManager.ConnectionStrings["SqlConnectionString"].ToString();
public static ISessionFactory SessionFactory
{
get
{
if (_sessionFactory == null)
{
_sessionFactory = BuildSessionFactory();
}
return _sessionFactory;
}
}
public static ISessionFactory BuildSessionFactory()
{
var cfg =
Fluently.Configure().ProxyFactoryFactory(typeof(ProxyFactoryFactory).AssemblyQualifiedName).Mappings(
m => m.FluentMappings.AddFromAssemblyOf<Category>()).Database(MsSqlConfiguration.MsSql2008.ConnectionString(_connectionString)).Cache(c => c.UseQueryCache());
return cfg.BuildSessionFactory();
}
}
I have ripped out all my Windsor container config to make troubleshooting easier so I have the basic setup as follows.
Web.UI
---> entities (category.cs)
---> mappings (categoryMap.cs)
---> FactoryGoo (NHibernateSessionFactory.cs
CODE For POCO /Entity
public class Category {
public Category() { }
public virtual int CategoryID { get; set; }
public virtual string CategoryName { get; set; }
public virtual string CategoryDescription { get; set; }
public virtual System.Nullable<int> ParentCategoryID { get; set; }
public virtual System.Nullable<int> DisplayOrder { get; set; }
public virtual int Active { get; set; }
public virtual string DateCreated { get; set; }
public virtual string LastUpdated { get; set; }
}
Code for Mapping
public class CategoryMap : ClassMap<Category> {
public CategoryMap() {
Table("Categories");
LazyLoad();
Id(x => x.CategoryID).GeneratedBy.Identity().Column("CategoryID");
Map(x => x.CategoryName).Column("CategoryName").Not.Nullable().Length(50);
Map(x => x.CategoryDescription).Column("CategoryDescription").Not.Nullable();
Map(x => x.ParentCategoryID).Column("ParentCategoryID");
Map(x => x.DisplayOrder).Column("DisplayOrder");
Map(x => x.Active).Column("Active").Not.Nullable();
Map(x => x.DateCreated).Column("DateCreated").Not.Nullable();
Map(x => x.LastUpdated).Column("LastUpdated").Not.Nullable();
}
}
So as I stated earlier, I get my Entity and my data when all of classes live in the same assembly. When I move my SessionFactory to my SessionManagement project or the mapping to the Infrastructure.Data.Mappings project and the entities to the Domain.Entities project nothing works and I gt no error as to why.
Thanks for reading this, I hope I have posted enough for you to get an idea of the setup.
Make sure the category class referenced in the BuildSessionFactory method is referencing the category class in the expected namespace/project. In other words, there may be multiple category classes in different projects.
i'm not sure of course, but I think you meant to link the parent category with your category and that is of the same type? If yes, than use the full blown category object as a property instead of the categoryId, and not use Map() but use fluent References().