How to use twice a reference to another table? - c#

Considering the following classes:
public class Unidade
{
public int UnidadeId { get; set; }
public string Apelido { get; set; }
public string Descricao { get; set; }
}
And
public class Estrutura
{
public int Id { get; set; }
…
public int UnidadeId { get; set; }
public virtual Unidade Unidade { get; set; }
…
public int UnidadeCompraId { get; set; }
public virtual Unidade UnidadeCompra { get; set; }
…
}
The query Estruturas.Single(e => e.Id == 120898).Unidade.Descricao will return an error, actually because Estruturas.Single(e => e.Id == 120898).Unidade is null.
The Id (120898) used in this example is valid as there is a valid value of UnidadeId set.
What’s wrong? How can I access de value of Descricao having a valid Estrura?

In C# 6:
struturas.Single(e => e.Id == 120898).Unidade?.Descricao
you will gen null if Unidade is null

The problem is that you have lazy loading turned off. Though that is probably better. The issue then is you need to .Include before calling .Single or it won't fetch the other table data.
Edit
Either that or the Foreign Key to this other table is either not defined at all or setup on the wrong column. Thus, it ends up trying to link the wrong 2 pieces of data and you end up getting no data found there.
An easy way to test this is to simply do this:
string sql = Estruturas.Where(e => e.Id == 120898).Include(a => a.Unidade).ToString();
This will show you the SQL that EF will run, minus the actual parameter value. Make sure this shows up how you would expect it to look. You can even just run this query in Sql Server directly with the parameter filled in to make sure you get back the data, too.
Also, you have to have the [Key] defined somewhere for the tables. The Foreign Key setup assumes it links back to the PK of the other table only.

Related

NullReferenceException Query SQLite database with Where on a concatenated string property

I'm trying to select a record using the following code:
Location item = connection
.Table<Location>()
.Where(l => l.Label.Equals(label))
.FirstOrDefault();
This results in:
System.NullReferenceException: Object reference not set to an instance of an object.
When I try the same, on a different property (Postcode), it all works fine also when no records are found.:
Location item = connection
.Table<Location>()
.Where(l => l.Postcode.Equals(label))
.FirstOrDefault();
This is the Location Class:
// These are the Locations where the Stock Take Sessions are done
public class Location : DomainModels, IComparable<Location>
{
[JsonProperty("id"), PrimaryKey]
public int Id { get; set; }
public string Name { get; set; }
public string Street { get; set; }
public int Number { get; set; }
public string Postcode { get; set; }
public string City { get; set; }
public bool Completed { get; set; }
[Ignore] // Removing this does not have an impact on the NullReferenceException
public string Label => $"{Name ?? ""} - ({Postcode ?? ""})";
public int CompareTo(Location other)
{
return Name.CompareTo(other.Name);
}
// Navigation property
// One to many relationship with StockItems
[OneToMany(CascadeOperations = CascadeOperation.All), Ignore]
public List<StockItem> StockItems { get; set; }
// Specify the foreign key to StockTakeSession
[ForeignKey(typeof(StockTakeSession))]
public int StockTakeSessionId { get; set; }
// One to one relationship with StockTakeSession
[OneToOne]
public StockTakeSession StockTakeSession { get; set; }
}
What am I doing wrong?
Thanks for any suggestions!
Your where filters in the data store on Label but your markup on your class Location has decorated the Label property with IgnoreAttribute. This means the Label property will not be set until after the entity has been materialized to memory and you can't do anything with it in the data store.
.Where(l => l.Label.Equals(label))
Fixes
There are some options.
You could set this to computed and create a computed column in the store with that same logic. This involves manually changing your table schema either directly in your RDBMS manager or editing your migration scripts. The property gets marked with [DatabaseGenerated(DatabaseGeneratedOption.Computed)] (if using attributes, which your code above is).
You could change the Where to filter on the Properties that compose Label that are found in the store. ie: .Where(l => l.Postcode.Equals(Postcode) && l.Name.Equals(Name))
You could materialize everything before that particular filter to memory and then apply the filter. This is not recommended if everything up to that point leads to a lot of records. Example, with the code below if the table is large you would be retrieving everything for a single record.
Location item = connection
.Table<Location>()
.AsEnumerable()
.Where(l => l.Label.Equals(label))
.FirstOrDefault();
Edit
[Ignore] // Removing this does not have an impact on the NullReferenceException
No, it should not unless you go through and add the column with the same name to your existing schema and populate it with all data. (or create a computed column in your schema with the same name)

Access count of related records of an entity Entity Framework

I have two models:
public class HouseType
{
public int Id { get; set; }
public string TypeName { get; set; }
public virtual IEnumerable<HouseModel> HouseModels { get; set; }
}
and
public class HouseModel
{
public int Id { get; set; }
public string ModelName { get; set; }
[DisplayFormat(DataFormatString = "{0:n2}")]
public double StandardPrice { get; set; }
[ForeignKey("HouseType")]
public int HouseTypeID { get; set; }
public virtual HouseType HouseType { get; set; }
public virtual IEnumerable<HouseUnit> HouseUnits { get; set; }
}
I am returning a JSON result, so as expected I cannot manipulate it in a view, because the display is handled by a javascript file that I made.
I am trying to retrieve the number of HouseModel that is contained by HouseType. I have tried:
db.HouseTypes.Select(h => new
{
HouseCount = h.HouseModels.Count()
}).ToList();
But Entity Framework complains about it. How can I access the count of related records inside an entity? Any help will be much appreciated. Thanks.
Use
public virtual ICollection<HouseUnit> HouseUnits { get; set; }
instead of
public virtual IEnumerable<HouseUnit> HouseUnits { get; set; }
Hope this helps.
Simply speaking, the trouble is that EF is trying to execute the .Select() statement on the db server but, of course, the db server does not know how to create a new object.
You first need to bring back the counts then create your objects so something like this should work better:
var listOfCounts = db.HouseTypes
.Select(h => h.HouseModels.Count())
.ToList()
.Select(c => new
{
HouseCount = c
})
.ToList();
in this example when the first .ToList() is executed the db needs only return a set of numbers (the counts of HouseModels in each HouseType) then we have a List<int> in local memory from which we can create our objects with the second Select statement.
As an aside...
It wasn't part of your original question but maybe you'd want to consider a dictionary rather than a list so you have some means of identifying which count of HouseModels belonged to each HouseType? in which case we could do something like:
Dictionary<int,string> houseModelCounts = db.HouseTypes
.ToDictionary(h => h.Id, h => h.HouseModels.Count());
which would give a dictionary keyed with the HouseType Id with values for the count of HouseModels in each type. I don't know your context though so maybe unnecessary for you?

Nhibernate and MVC. Null object from query

So I am rather new to using NHibernate for database access and after studying its usage elsewhere in an application I am editing, I cannot seem to get it to work for me and I do not know why. Effectively, I am trying to populate an object with data from my database so that I can pull pieces in and present them to the user. The issue is that despite my syntax and code looking correct, my object remains null after query execution.
The class that is being used to represent the table in the database:
public class AllocateLog
{
public virtual string UserName { get; set; }
public virtual int Id { get; set; }
public virtual int OwnerId { get; set; }
public virtual int MemberId { get; set; }
public virtual int? ResId { get; set; }
public virtual string RequestComments { get; set; }
public virtual DateTime DateEntered { get; set; }
public virtual DateTime? DateExited { get; set; }
public virtual string EntryAccessPoint { get; set; }
public virtual string ExitAccessPoint { get; set; }
}
The mapping code:
public class AllocateLogOverride : IAutoMappingOverride<AllocateLog>
{
public void Override(AutoMapping<AllocateLog> map)
{
#if LOCAL_INSTALL
map.Schema("dbo");
#else
map.Schema("cred");
#endif
map.Table("allocate_log");
map.CompositeId()
.KeyProperty(x => x.OwnerId, "owner_id")
.KeyProperty(x => x.MemberId, "member_id")
.KeyProperty(x => x.DateEntered, "date_entered");
map.Map(x => x.UserName).Column("user_name");
map.Map(x => x.ResId).Column("res_id");
map.Map(x => x.RequestComments).Column("request_comments");
map.Map(x => x.DateExited).Column("date_exited");
map.Map(x => x.EntryAccessPoint).Column("entry_access_point");
map.Map(x => x.ExitAccessPoint).Column("exit_access_point");
}
}
The query code:
public class AllocateLogsForAccessPoints : IQuery<IQueryOver<AllocateLog>, AllocateLog>
{
private readonly string accessPoint;
public AllocateLogsForAccessPoints(string accessPoint)
{
this.accessPoint = accessPoint;
}
public IQueryOver<AllocateLog> BuildQuery(ISession session)
{
return session.QueryOver<AllocateLog>()
.Where(d => d.EntryAccessPoint == accessPoint);
}
public AllocateLog Execute(IQueryOver<AllocateLog> query)
{
return query.SingleOrDefault();
}
}
And the code that I am using just as a test to see if my query will return anything to my object:
var asdf = DbQueryExecutor.ExecuteQuery(new AllocateLogsForAccessPoints((string)"north gate"));
There is only one record in the database that fits that query as it is the only row in the database with any data in entry_access_point and the string is "north gate". The table is cred.allocate_log and all of the columns are named correctly in the mapping file. Furthermore, removal of the mapping file or rather commenting it out does not result in any runtime error which means to me that NHibernate never even tries to use the mapping file because if I try to do this (meaning commenting out the contents of the file) with any other mapping file who's query works, I get a runtime error. So I am entirely stumped as to why my object remains null after executing the query which runs without error. Any ideas? I will update my original post if you require more information.
You might try downloading an application called NHProf from Hibernating Rhinos. It traces all nHibernate calls and shows you exactly the SQL that nHibernate is trying to run. It has been a godsend for me in trying to figure out just what the hell nHibernate is trying to do.
The answer to my issue was to check the namespace and location of the various files associated with the query. I had originally put one of the files in the wrong folder and so the namespace was mapped to that incorrect folder. I moved the file into the correct folder but never changed the namespace. So in the override file I had a reference to the folder the namespace said it was part of when in actuality it was not. Correcting these errors was the solution.

Lazy Loading EntityFramework 5 (with CodeFirst)

I am testing lazy loading in a C# Console Application. For some reason Lazy loading is not working.
I have checked the LazyLoadingEnabled and ProxyCreationEnabled properties of the context.Configuration. They are true.
My property is virtual.
I have checked the other similar SO questions without success. I am not sure what might be happening.
This is my code (simplified to not show namespaces):
static void Main(string[] args) {
Models.DataContext dc = new Models.DataContext();
Console.WriteLine("Context Lazy {0}. Proxy Creation {1} ",
dc.Configuration.LazyLoadingEnabled,
dc.Configuration.ProxyCreationEnabled);
var grp = dc.Groups.FirstOrDefault();
Console.WriteLine("GroupId {1}, AttrSet is null = {0}",
grp.AttrSet == null , grp.Id);
var grp2 = dc.Groups.Include("AttrSet").FirstOrDefault();
Console.WriteLine("GroupId {1}, AttrSet is null = {0}",
grp2.AttrSet == null, grp2.Id);
}
class Group {
public System.Guid Id { get; set; }
public string Name { get; set; }
public virtual AttrSet AttrSet { get; set; }
}
class AttrSet {
public System.Guid Id { get; set; }
public string Name { get; set; }
}
The output of running this is:
Context Lazy True. Proxy Creation True
GroupId 186ebc8a-dec7-4302-9f84-5a575577baac, AttrSet is null = True
GroupId 186ebc8a-dec7-4302-9f84-5a575577baac, AttrSet is null = False
I am sure that the loaded record is correct and it has a proper AttrSet in the database.
Any ideas?
Update
I created a very simple testing project in case any one actually wants to look at the code.
See: https://bitbucket.org/josea/eflazy (GIT: https://josea#bitbucket.org/josea/eflazy.git).
Proxy generation is not occurring. Why?? Because your POCOs are PRIVATE!! EF can't see them to derive proxies from them. Make your POCOs public and it'll work the way you expect.
public class Group
Are you using anything to configure the 1:1 relationship between the 2 classes? Because it doesn't look like you are here, which would cause Entity Framework to not be able to load the relationship.
You can use Data Annotations to define the FK relationship as so:
public class AttrSet {
public System.Guid Id { get; set; }
public string Name { get; set; }
[Required, ForeignKey("MyGroup")]
public int GroupID { get; set; }
public virtual Group MyGroup { get; set; }
}
This should give you the relationship you wanted. AttrSetId is whatever you've named the FK column in your table, so change that if it is different.
public class Group {
public System.Guid Id { get; set; }
public string Name { get; set; }
public System.Guid AttrSetId {get;set;}
[ForeignKey("AttrSetId")]
public virtual AttrSet AttrSet { get; set; }
}
Edit:
Add this line to your AttrSet class:
public virtual ICollection<Group> Groups {get;set;}
Add this next line to your OnModelCreating in your Models.DataContext. If for some reason you don't already have that function overridden, it'll look like this:
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
modelBuilder.Entity<Group>().HasOptional(x => x.AttrSet).WithMany(x => x.Groups);
}
I put HasOptional instead of HasRequired as I assumed you could save a Group without an AttrSet. If that is not true, and the FK is not nullable, then you should use HasRequired.

search for int id starting with x entity framework 4.1

I currently have an Entity Framework model that collects data from a legacy database and I am currently using an int on my Id properties
I am attempting to build a search box with autocomplete capabilities and want to have the autocomplete function to return a subset of records based on whether the sample id either contains or starts with (final design decision not made yet) and I am running into problems with converting the integer id to a string as I would normally use a recs.Id.toString().StartsWith(recordId) but this is apparently not supported by the Entity Framework
Is there a way around this limitation ?
My code looks like the following
Model:
public class Sample
{
public Sample()
{
Tests = new List<Test>();
}
public int Id { get; set; }
public DateTime SampleDate { get; set; }
public string Container { get; set; }
public string Product { get; set; }
public string Name { get; set; }
public string Status { get; set; }
public virtual SamplePoint SamplingPoint { get; set; }
public virtual SampleTemplate SampleTemplate { get; set; }
public Customer ForCustomer { get; set; }
public virtual ICollection<Test> Tests { get; set; }
}
and the query I am currently trying to apply to this model
[HttpGet]
public JsonResult AutoComplete(string partialId)
{
var filteredSamples =
repo.AllSamples.Where( s =>
String.Compare(s.Status, "A", false) == 0
&& (s.Id.ToString()).StartsWith(partialId)
).ToList();
return Json(filteredSamples, JsonRequestBehavior.AllowGet);
}
Any ideas would be awesome I am out of ideas at this point
No matter what you do, this is going to result in some awful performance on large datasets, because you will not be able to use any indices. My recommendation would be to use a trigger or scheduled task to store the leading digit in a separate field and filter on that.
I ended up adding a view for autocomplete data and converting the data to string in the select statement and this solved my issue
Wild thought: how about your create a computed, persisted column on your database table, that converts your ID (INT) into a string?
Then you could:
put an index on that column
use a simple string comparison on that string column
Basically, you need this:
ALTER TABLE dbo.YourTable
ADD IDAsText AS CAST(ID AS VARCHAR(10)) PERSISTED
Now update you EF model - and now you should have a new string field IDAsText in your object class. Try to run your autocomplete comparisons against that string field.

Categories

Resources