Linq2Sql relationships and WCF serialization problem - linq

here is my scenario
i got
Table1
id
name
Table2
id
family
fid
with one to many relationship set between Table1. id and Table2.fid
now here is my WCF service Code
[OperationContract]
public List<Table1> GetCustomers(string numberToFetch)
{
using (DataClassesDataContext context = new DataClassesDataContext())
{
return context.Table1s.Take(int.Parse(numberToFetch)).ToList( );
}
}
and my ASPX page Code
<body xmlns:sys="javascript:Sys"
xmlns:dataview="javascript:Sys.UI.DataView">
<div id="CustomerView"
class="sys-template"
sys:attach="dataview"
dataview:autofetch="true"
dataview:dataprovider="Service2.svc"
dataview:fetchParameters="{{ {numberToFetch: 2} }}"
dataview:fetchoperation="GetCustomers">
<ul>
<li>{{family}}</li>
</ul>
</div>
though i set serialization mode to Unidirectional in Linq2Sql designer i am not able to get the family value and all what i get is this in firebug
{"d":[{"__type":"Table1:#","id":1,"name":"asd"},{"__type":"Table1:#","id":2,"name":"wewe"}]}
any help would be totally appreciated

Well, the point is: your method GetCustomers only ever selects from Table1 - I don't see any reference at all to Table2, where your Family column is located......
You need to check into Linq-to-SQL JOIN's and how to fetch data from a joined table into your result set.
Something along the lines of:
[DataContract]
class JoinedResult
{
[DataMember]
public int Table1ID { get; set; }
[DataMember]
public string Table1Name { get; set; }
[DataMember]
public string Table2Family { get; set; }
}
[OperationContract]
public List<JoinedResult> GetCustomers(int numberToFetch)
{
using (DataClassesDataContext context = new DataClassesDataContext())
{
var q = from t1 in context.Table1
join t2 in context.Table2 on t1.id = t2.fid
select new JoinedResult
{ Table1ID = t1.ID,
Table1Name = t1.Name,
Table2Family = t2.Family };
return q.Take(numberToFetch).ToList();
}
}
and btw: you should really make numberToFetch an INT parameter! Let the caller make the conversion......
UPDATE: if you don't want to explicitly include the second table to be queried, you could instead add a DataLoadOption to your context:
DataLoadOptions dlo = new DataLoadOptions();
dlo.LoadWith<Table1>(t => t.Table2);
context.LoadOptions = dlo;
In that case, you tell LINQ to always include all elements from Table2 when it loads anything from Table1 - that should work, too.

Related

EF6 Linq Query - How to make a query when two Foreign Key Constraints are in place

For the most part EF seems to handle itself quite well, using the following query in linq, I am able to get all the related table data using FK's without having to specify the one to many relationship.
join cp in db.ClinicalPATs on s.ClinicalAssetID equals cp.ClinicalAssetID into AP
from subpat in AP.DefaultIfEmpty()
orderby s.ClinicalAssetID descending
select new ClinicalASSPATVM
{
ClinicalAssetID = s.ClinicalAssetID,
ProductName = s.ProductName,
ModelName = s.ModelName,
SupplierName = s.SupplierName,
ManufacturerName = s.ManufacturerName,
SerialNo = s.SerialNo,
PurchaseDate = s.PurchaseDate,
PoNo = s.PoNo,
Costing = s.Costing,
TeamName = s.TeamName,
StaffName = s.StaffName,
InspectionDocumnets = subpat.InspectionDocumnets ?? String.Empty,
InspectionOutcomeResult = subpat.InspectionOutcomeResult
});
the above code pulls in the relationship data from the ViewModel.
public Product ProductName { get; set; }
public InspectionOutcome InspectionOutcomeResult { get; set; }
public Model ModelName { get; set; }
public BudgetCode Code { get; set; }
public AssetType AssetTypeName { get; set; }
public Manufacturer ManufacturerName { get; set; }
public Staff StaffName { get; set; }
public Team TeamName { get; set; }
public Supplier SupplierName { get; set; }
I have a new problem which I have created for myself. I wanted to add an ID Field to the Models entity this helps me filter the data in a drop down list. I called the Field Name: ModelAssetAsignmentID And when someone adds a new ModelName from the Clinical Controller the ModelAssetAsignmentID gets a value of two.
So when i added the Field ModelAssetAsignmentID to the model "Models" i created a second FK as such:
My Original Linq Query is now broken, it no longer displays the modelname. I'm guessing this is due to the two FK Constraints.
Making the following change did not work, the InnerException is null.
var ClinicalASSPATVM = (from s in db.ClinicalAssets
where (s.ModelAssetAssignmentID.Equals(2))
join cp in db.ClinicalPATs on s.ClinicalAssetID equals cp.ClinicalAssetID into AP
from subpat in AP.DefaultIfEmpty()
orderby s.ClinicalAssetID descending
The solution is to remove the second foreign key constraint ModelAssetAssignmentID and use a viewmodel to create a value in the ModelAssetAssignmentID, thus you then do not need to modify the linq query's.

Realm Xamarin LINQ Object

What is the correct way to query Realm with LINQ where the query includes fields from other Realm objects? For example:
public class Department : RealmObject
{
[Primary Key]
public string UniqueId { get; set; }
}
public class Employee : RealmObject
{
[Primary Key]
public string Name { get; set; }
// Parent
public Department Department { get; set; }
}
Then I would expect to be able to do something like:
var employee = realm.All<Employee>().SingleOrDefault( e => e.Department.UniqueId == fooId && e.Name == fooName );
But this always returns no matches. Where() also returns no matches. However, eliminating the e.Department and searching only on employee name works fine but obviously does not scope to Department as intended.
This is with the latest Realm Xamarin 0.80.
What am I doing wrong?
Querying by nested RealmObjects attributes is not currently supported:
Just to clarify here, we don't yet support queries on related objects like this. We will in the future, but there is no timeline at the moment.
The following is also not currently supported:
var deptFilter = theRealm.ObjectForPrimaryKey<Department>("HR");
var employeesByDept = theRealm.All<Employee>().Where((Employee emp) => emp.Department == deptFilter & emp.Name == "StackOverflow");
The left-hand side of the And operator must be a direct access to a persisted property in Realm.
Unable to process '(emp.Department == value(Realm080.App+c__AnonStorey1).deptFilter)'.
You can do direct RealmObject equalities, just not in the same Linq expression, so break it down further into a sub-query.
Example of how I currently do it:
var deptFilter = theRealm.ObjectForPrimaryKey<Department>("HR");
var employeesByDept = theRealm.All<Employee>().Where((Employee emp) => emp.Department == deptFilter);
var employees = employeesByDept.Where((Employee emp) => emp.Name == "StackOverflow");
foreach (var emp in employees)
{
D.WriteLine(emp.Name);
}
Note: https://github.com/realm/realm-dotnet/issues/723

Entity Framework Many to Many works but Include does not

I have a typical many-to-many relationship with these 3 tables
[Post] (
[PostId] int, (PK)
[Content] nvarchar(max)
...
)
[Tag] (
[TagId] int, (PK)
[Name] nvarchar
...
)
[TagPost] (
[TagId] int, (PK, FK)
[PostId] int (PK, FK)
)
And, TagId and PostId are the PK and FK set on the tables accordingly etc. Then I have these classes and mapping in c#
public class Post {
public Post()
{
this.Tags = new HashSet<Tag>();
}
[Key]
public int PostId { get; set; }
...
public virtual ICollection<Tag> Tags { get; private set; }
}
public class Tag {
public Tag()
{
this.Posts = new HashSet<Post>();
}
[Key]
public int TagId { get; set; }
...
public virtual ICollection<Post> Posts { get; private set; }
}
internal class MyDbContext : DbContext
{
public DbSet<Post> Posts { get; set; }
public DbSet<Tag> Tags { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
modelBuilder.Entity<Post>().ToTable("Post");
modelBuilder.Entity<Tag>().ToTable("Tag");
modelBuilder.Entity<Post>()
.HasMany(x => x.Tags)
.WithMany(x => x.Posts)
.Map(x =>
{
x.ToTable("TagPost");
x.MapLeftKey("PostId");
x.MapRightKey("TagId");
});
}
Then I have this code to query them
var list = (from p in ctx.Posts.Include(p => p.Tags)
from t in p.Tags
where ... // some of my filter conditions
select p).ToList();
This join does return the posts I was looking for, however the returned posts don't their associated tags filled in even though I have the Include there. Could someone help point out what I'm missing so that I could have the tags also return with the posts?
Thanks a lot.
The double from is a manual Join which causes the Include to be ignored as mentioned here and here. Include is also ignored for other LINQ methods like grouping and projections.
Relationship fixup generally does not work for many-to-many relationships, only for relationships which have at least one single reference at one of the ends - one-to-many or one-to-one. If you project the Posts and related Tags into another type (anonymous or named) the data will be loaded correctly but because the relationship is many-to-many EF won't create the relationship in memory automatically so that the post.Tags collection will stay empty.
To get the Include working you must remove the second from from your query and apply the where clause directly to the Post entity parameter, for example like so:
var list = (from p in ctx.Posts.Include(p => p.Tags)
where p.Tags.Any(t => t.TagId == 1)
select p).ToList();
The filter by a Tag property is specified in the expression passed into .Any which is an expression with a Tag (t) as parameter.
try selecting everything into an anonymous object (something like this)
var list = (
from p in ctx.Posts
from t in p.Tags
where ... // some of my filter conditions
select new {
Posts = p,
Tags = p.Tags
})
.ToList();
Based on the feedback to my initial answer and the fact that EF can find the related entities but it is failing to populate the Tags collection I believe the issue lies in the definition of the Tags entity in the Post class.
Try removing the Hashset<> initialiser from the constructors and private from the set declaration:
public virtual ICollection<Tag> Tags { get; set; }

Join LINQ queries from multiple contexts

I am trying to create a ViewModel for data that I want to display in my view. The issue is, the data being displayed is spread across 2 databases and multiple tables within each. I've read that you cannot join Linq queries across multiple contexts, which makes sense, and I've also read that we can't use Code-First in EF5 to use Stored Procedures....which led me to using 3 different Linq queries and attempt to combine them into 1 ViewModel...I'm just not sure how to get there.
Here's my Linq queries:
var csdContext = new CSDContext(CustomerCode);
var masterContext = new MasterContext();
//Only returns 1 row - which is what we want.
List<Site> sites = (from s in csdContext.Sites
join sa in csdContext.SiteAddresses
on s.SiteID equals sa.SiteID
join a in csdContext.Addresses
on sa.AddressID equals a.AddressID
join spv in csdContext.SiteProductVersions
on s.SiteID equals spv.ProductVersionID
where s.SiteID == id
select s).ToList();
//List
List<States> states = (from s in masterContext.StatesTable
select s).ToList();
My ViewModel looks like this:
public class SiteDetailsViewModel
{
public string Address { get; set; }
public string Address2 { get; set; }
public string City { get; set; }
public string StateCode { get; set; }
public string ZipCode { get; set; }
public string OfficePhone { get; set; }
public string MobilePhone { get; set; }
public string AlternativePhone { get; set; }
public int ProductVersionID { get; set; }
}
Basically, I need the following data from these tables:
csdContext - Address
Address
Address2
City
ZipCode
csdContext - Sites
OfficePhone
MobilePhone
AlternativePhone
csdContext - SiteProductVersions
ProductVersionID
masterContext - States
StateCode
Here's how the tables are joined in SQL:
SELECT csd_a.Address, csd_a.Address2, csd_a.City, mstr_st.StateCode, csd_a.ZipCode, csd_s.OfficePhone, csd_s.MobilePhone,
csd_s.AlternativePhone, csd_spv.ProductVersionID
FROM CSD.dbo.Sites AS csd_s
INNER JOIN CSD.dbo.SiteAddress AS csd_sa ON csd_sa.SiteID = csd_s.SiteID
INNER JOIN CSD.dbo.Address AS csd_a ON csd_a.AddressID = csd_sa.AddressID
INNER JOIN CSD.dbo.SiteProductVersions AS csd_spv ON csd_s.SiteID = csd_spv.SiteID
INNER JOIN MasterDB.dbo.States AS mstr_st ON mstr_st.StateID = csd_a.StateID
I can't figure out how to merge these 3 results to create the ViewModel data for SiteDetailsViewModel. Can anyone help?
If you materialize your queries as described in OP you can use this:
var query = sites.Join(
states,
si => si.StateID,
st => st.StateID,
(si, st) => new SiteDetailsViewModel
{
Address = si.Address,
Address2 = si.Address2,
City = si.City,
StateCode = st.StateCode,
ZipCode = si.ZipCode,
OfficePhone = si.OfficePhone,
MobilePhone = si.MobilePhone,
AlternativePhone = si.AlternativePhone,
ProductVersionID = siProductVersionID
});
One possible alternate solution is to create views in DB #2 of the tables in DB #1. Then you can model/map those views in EF for DB #2. You would of course still need a context for DB #1 if you need to update any of the tables. But the benefit of this solution is that you can do all of your read access on a single context and get joins at the server instead of in memory.
So why don't you write it like this:
from ...
...
where s.SiteID == id
select new SiteDetailsViewModel()
{
Address = sa.Address,
Address2 = as.Address2,
..
}

LINQ query returning a List<> as a class member

Given the follow data class,
public class EmployeeMenu
{
public int ID { get; set; }
public string HeaderName { get; set; }
public List<string> ItemNames { get; set; }
}
how can I get a sub-query into the ItemNames field?
My current query of
IQueryable<EmployeeMenu> retValue =
from mh in menuHeaders
select new EmployeeMenu
{
ID = mh.ID,
HeaderName = mh.HeaderName,
ItemNames = (from mhi in mh.MenuItems
select mhi.MenuItemName).ToList<string>()
};
doesn't seem to be doing the trick...
The data structure is
MenuHeaders MenuItems
----------- ---------
ID ID
HeaderName <-(FK)--MenuHeaderID
MenuItemName
I ended up just changing from a List to IEnumerable. This fixed it.
Wouldnt you want to just put a where in your sub-select to filter that down to all the menu items with the MenuHeaderID equals mh.HeaderName. You can just .Equals() with the StringComparison type if you want as well.
Here is an example...
IQueryable<EmployeeMenu> retValue =
from mh in menuHeaders
select new EmployeeMenu
{
ID = mh.ID,
HeaderName = mh.HeaderName,
ItemNames = (from mhi in mh.MenuItems
select mhi.MenuItemName where mhi.MenuHeaderID = mh.HeaderName).ToList<string>()
};
My guess is that your not initiliazing the list within your class. I basing this off the experience I was having with Nhibernate.
public class EmployeeMenu
{
public int ID { get; set; }
public string HeaderName { get; set; }
public List<string> ItemNames { get; set; }
public EmployeeMenu()
{
ItemNames=new List<string>();
}
}
Hope this helps.
Okay. Try replacing
(from mhi in mh.MenuItems
select mhi.MenuItemName).ToList<string>()
by
mh.MenuItems
.AsEnumerable()
.Select(mhi => mhi.MenuItemName)
.ToList()
I question if you want a where clause in there somewhere, but this should get you past the runtime exception.
Any time you see an error message of the form "LINQ to Entities does recognize the method ... and this method can not be translated into a store expression" LINQ to Entities is telling you that it can't figure out how to translate part of the expression tree into a SQL statement. This means you need to pull things client side so that LINQ to Entities doesn't try to translate something that it can't translate.

Resources