I know I'm close with this one...
My structure is:
I have some companies
Each company has a primary user (account)
And a list of all users (accounts).
I've implemented this in the db as a companies table that has "Primary" as a foreign key to the Accounts table, and the Accounts table has a CompanyId which is a foreign key to the Companies table.
So there's only ever one primary user, and each user is associated with one company.
I want to retrieve a list of all companies, and plonk them in a c# object. What I have so far is:
public IEnumerable<Company> GetAllCompaniesList()
{
var allData = database.Companies.All()
.Join(database.Accounts).On(database.Accounts.Id == database.Companies.Primary)
.Join(database.Accounts).On(database.Accounts.CompanyId == database.Companies.Id)
.ToList<Company>();
return allData;
}
and it works in my tests using an in-memory adapter with the relevant keys set up, but not in the real version, crashing out with
The objects "dbo.Accounts" and "dbo.Accounts" in the FROM clause have
the same exposed names. Use correlation names to distinguish them.
I think this means I need to name each join (e.g. to make sql like "join a on a = b as c"), but I can't find the syntax to do that. Anyone know?
You can alias table names using the .As method. In addition to that, the Join method has an optional second out parameter which puts the reference to the aliased table in a dynamic variable; it just makes referring to the table easier.
So try this:
public IEnumerable<Company> GetAllCompaniesList()
{
dynamic primary;
var allData = database.Companies.All()
.Join(database.Accounts.As("PrimaryAccounts"), out primary)
.On(primary.Id == database.Companies.Primary)
.Join(database.Accounts)
.On(database.Accounts.CompanyId == database.Companies.Id)
.ToList<Company>();
return allData;
}
That should work fine.
Related
I have already been working with linq in the past and I know how to access a database with SqlConnection and SqlCommand. Today I wanted to work with LinqToSql and see if and how I can make reading from and writing to a database easier. I did this Walkthrough.
Here is the code for the Customer Class (I changed it a bit but it still works perfectly fine):
[Table(Name = "Customers")]
class Customer
{
[Column(IsPrimaryKey = true)]
public string CustomerID { get; set; }
[Column]
public string City { get; set; }
}
And the code from Main:
class Program
{
static void Main(string[] args)
{
DataContext db = new DataContext(#"Data Source=(local)\SQLEXPRESS;Initial Catalog=Northwind;User ID=sa;Password=xxx");
Table<Customer> customers = db.GetTable<Customer>();
IQueryable query = from cust in customers
//where cust.City == "London"
select cust;
foreach (Customer customer in query)
Console.WriteLine("ID:{0}; City={1}", customer.CustomerID, customer.City);
Console.ReadKey();
}
}
It worked and I'm happy since this makes accessing a database so much easier for me. But there are still a couple of things that concern me:
It seems like for every column I want to include I need to create a new property in the Customer class and add a [Column] above it.
I need to create a new Class for every table that I want to read from (for example Customer, Orders and Suppliers in the Northwind DB).
First of all this seems to be quite a lot of unnecessary and repetitive work. Am I doing something wrong here?
Also I want the user to type in the name of a database and a table. This means I don't know yet which database he will pick and I also don't know the structure of the table yet. I can't create the class yet that is supposed to represent the table.
This means I will need to:
Create a type / class / object dynamically. I can't use Table<Customer> customers = db.GetTable<Customer>() because I don't know the type yet. The type will be the dynamically created class.
Mark the type / class / object as a table with [Table(Name="xxx")].
Read the columns from the SqlTable and create for every column a property and mark it with [Column].
After I'm done with this I can get the table with Table<T> table = db.GetTable<T>(), execute the query and display the datarows.
My second (and more important) question is: How can I do this? Does anyone have code examples or links to share? Or is this approach wrong?
You can store the result in dynamic object just like this.
dynamic table = db.GetTable<T>()
and use reflection to get type of the object.
So what I was trying was to fetch only those columns from table which has to be updated and I tried it as below:
var user = (from u in context.tbl_user where u.e_Id == model.Id select
new { u.first_name, u.last_name, u.role, u.email, u.account_locked })
.FirstOrDefault();
But when I tried to assign new value to the fetched data as below
user.first_name = model.FirstName;
I saw below error getting displayed
Property or indexer 'anonymous type: string first_name, string
last_name, string role, string email, bool account_locked.first_name'
cannot be assigned to -- it is read only
But when I retrieved all the values from table without filtering as below it worked fine.
var user = (from u in context.tbl_user where u.e_Id == model.Id select u).FirstOrDefault();
Why it doesn't work for first query. I've read in many sites that it is good to retrieve only required properties from database in terms of performance and security. But I am really not able to understand what's wrong with the first approach I opted. Any explanations are much appreciated.
Update
Are there any other ways to fetch only required column and update them and store them back?
Anonymous Types properties are read-only so you can not change them.
Stop doing micro-optimizing or premature-optimization on your code. Try to write code that performs correctly, then if you face a performance problem later then profile your application and see where is the problem. If you have a piece of code which have performance problem due to finding the shortest and longest string then start to optimize this part.
We should forget about small efficiencies, say about 97% of the time:
premature optimization is the root of all evil. Yet we should not pass
up our opportunities in that critical 3% - Donald Knuth
If you want to just fetch specific columns you can create a custom class and fill the properties in your query like others have mentioned.
As others said anonymous type is read only, for achieving what you want, you will have to create a type for it with properties that are required:
public class User
{
public string FirstName {get;set;}
public string LastName {get;set;}
.....................
.....................
}
and then, you have to use it in your linq query:
var user = (from u in context.tbl_user
where u.e_Id == model.Id
select new User
{
FirstName = u.first_name,
LastName = u.last_name,
......................,
.....................
}).FirstOrDefault();
Anonymous types are read-only by design.
In order to retrieve something that you can edit, you have to create a class and select your entities into that class, as done here: How to use LINQ to select into an object?
Or here:
var user = (from u in context.tbl_user where u.e_Id == model.Id select
new User_Mini { u.first_name, u.last_name, u.role, u.email, u.account_locked })
.FirstOrDefault();
Note: you won't be able to call context.SubmitChnages() when editing this new object. You could do something like this though: LINQ to SQL: how to update the only field without retrieving whole entity
This will allow you to update only certain parts of the object.
I have two tables. Document table and Version table. Both are identicle except the version table has an ID field and a documentID field. The document table has a documentId field.
I can correctly find the document but I cannot find the version table information because the id I am padding in it is trying to find this on the id field instead of the documentId field.
public ActionResult ApproveDocument(int id = 0)
{
IPACS_Document ipacs_document = db.IPACS_Document.Find(id);
IPACS_Version ipacs_version = db.IPACS_Version.Find(id);
ipacs_version.dateApproved = System.DateTime.Now;
ipacs_version.approvedBy = User.Identity.Name.Split("\\".ToCharArray())[1];
ipacs_document.dateApproved = System.DateTime.Now;
ipacs_document.approvedBy = User.Identity.Name.Split("\\".ToCharArray())[1];
ipacs_document.revision = ipacs_version.revision;
db.SaveChanges();
return RedirectToAction("Approve");
}
So the ipacs_document is found correctly because the id passed in 11 works. However ipacs_version doesn't find anything because it is trying to find id 11 instead of documentId 11.
If you're wondering on how to use Find (DbSet<>) to engage composite keys...
The Find method takes an array of objects as an argument. When working
with composite primary keys, pass the key values separated by commas
and in the same order that they are defined in the model.
http://msdn.microsoft.com/en-us/library/gg696418(v=vs.103).aspx
db.IPACS_Version.Find(id, documentid); // mind the order
And for anything more complex keep in mind that you could always use Linq queries, Where e.g.
db.IPACS_Version.Where(x => x.Id == id && x.DocumentId == docid && x.Flag == true);
Note: You could use the query, Where (regardless of how your entities are made) -
but if your keys are not set up properly (based on the comments) - I'd discourage you to go that way. Instead
of a fast fix, make sure your tables, pk-s are set up as they should -
as that's essential. Then you can see which query is best for you (or
just use Find if that's all you need).
I have a View Model that has some serious nesting. I need to populate it from Entity Framework 4. I tried creating one big linq statement to populate it, but it says it doesn't recognize the .ToList() methods. It compiles fine. Runtime error is
LINQ to Entities does not recognize the method
'System.Collections.Generic.List`1[ProductDepartment] ToList[ProductDepartment]
(System.Collections.Generic.IEnumerable`1[ProductDepartment])' method,
and this method cannot be translated into a store expression.
What is a more efficient way to populate something like this without doing several thousand database calls?
List<Product> Products {
int ID
string Name
...
List<Department> Departments {
int ID
string Name
}
List<Image> Images {
int ID
string Name
}
List<Price> Prices {
int ID
string Name
List<Version> Versions {
int ID
string Name
List<Pages> Pages {
int ID
string Name
} } } }
The horrible Linq code looks something like this
var myProducts = (from myProduct in DC.MyProducts
where p => p.productGroup == 1
select new Product {
ID = myProduct.ID,
Name = myProduct.Name,
Departments = (from myDept in DC.MyDepartments
where q => q.fkey = myProduct.pkey
select new Department {
ID = myDept.ID,
Name = myDept.Name
}).ToList(),
...
//Same field assignment with each nesting
}).ToList();
Update:
The fix was to remove all the .ToLists(), which worked better anyway.
Now I have to do filtering and sorting on the end product.
Well for starters, that is one crazy model, but i'm assuming you already know this.
Do you really need all that info at once?
I'll play devil's advocate here and assume you do, in which case you have a couple of logical choices:
1) As #xandy mentioned - use .Include to eager load your associations in the one call. This is assuming you have setup navigational properties for your entites in your EDMX.
2) Use a View. Put all that crazy joining logic inside the database, making your EF work a very simple select from the view. The downside of this is your queries to the view basically become read only, as i don't believe you can perform updates to an entity which is mapped to a view.
So it's your choice - if this is a readonly collection for displaying data, use a View, otherwise eager-load your associations in the one hit.
Also, be careful when writing your LINQ queries - i see you have multiple .ToList statements, which will cause the query to be executed.
Build up your query, then perform the .ToList once at the end.
why do you require all this informataion at one go? You can use lazy loading when a nested property is accessed?
Normally when you update an object in linq2sql you get the object from a datacontext and use the same datacontext to save the object, right?
What's the best way to update a object that hasn't been retreived by that datacontext that you use to perform the save operation, i.e. I'm using flourinefx to pass data between flex and asp.net and when object return from the client to be saved I don't know how to save the object?
public static void Save(Client client)
{
CompanyDataContext db = new CompanyDataContext();
Validate(client);
if(client.Id.Equals(Guid.Empty))
{
//Create (right?):
client.Id = Guid.NewGuid();
db.Clients.InsertOnSubmit(client);
db.SubmitChanges();
}
else
{
//Update:
OffertaDataContext db = new OffertaDataContext();
db.Clients.????
}
}
Update: different approaches to use Attach doens't work in this case. So I guess a reflection based approach is required.
To update an existing but disconnected object, you need to "attach" it do the data context. This will re-use the existing primary key etc. You can control how to handle changes- i.e. treat as dirty, or treat as clean and track future changes, etc.
The Attach method is on the table - i.e.
ctx.Customers.Attach(customer); // optional bool to treat as modified
I think you have 2 options here:
1) Attach the object to the DataContext on which you will do your save
2) Using the primary key on your object, grab an instance that is attached to your context (e.g. do a FirstOrDefault()), and then copy the data over from the modified object to the object that has a context (reflection might be useful here).
Rick Strahl has a very good blog article on attaching entities to a context at http://www.west-wind.com/weblog/posts/134095.aspx, particularly in regards to some of the problems you might encounter.
I am hoping you can help. I am developing a tiered website using Linq to Sql. I created a new class(or object) in DBML designer called memberState. This object is not an actual table in the database. I have this method in my middle layer:
public override IEnumerable(memberState) GetMembersByState(string #state)<br/>
{<br/>
using (BulletinWizardDataContext context = DataContext)<br/>
{<br/>
IEnumerable(memberState) mems = (from m in context.Members<br/>
join ma in context.MemberAddresses<br/>
on m.UserId equals ma.UserId<br/>
join s in context.States<br/>
on ma.StateId equals s.StateId<br/>
where s.StateName == #state<br/>
select new memberState<br/>
{<br/>
userId = m.UserID,<br/>
firstName = m.FirstName,<br/>
middleInitial = m.MiddleInitial,<br/>
lastName = m.LastName,<br/>
createDate = m.CreateDate,<br/>
modifyDate = m.ModifyDate<br/>
}).ToArray(memberState)();<br/>
return mems;
}
}
The tables in my joins (Members, States, and MemberAddresses are actual tables in my Database). I created the object memberStates so I could use it in the query above (notice the Select New memberState. When the data is updated on the web page how do I persist the changes back to the Member Table? My Member Table consists of the following columns: UserId, FirstName, MiddleInitial, LastName, CreateDate, ModifyDate. I am not sure how save the changes back to the database.
Thanks,