Renaming LINQ 2 SQL Entity Properties Through Partial Classes - linq-to-sql

Can I use partial classes to create properties that points to an association property generated by the L2S designer. Also, will I be able to use the new property in queries?
How can I achieve this?

If you just want to give a different name to the association property, just use the Property page for the association and rename the parent and/or child property. That will change the name of the EntityRef/EntitySet in the class.
EDIT: The downside of using a separate property in a partial class is that LINQ won't be able to use it when generating queries -- essentially you'll be forced to always get the entities before you can use the related properties on the object. By renaming you allow LINQ to use the related properties in constructing the query which can result in a more efficient query. For example, if you want to get entities where a related entity has a particular property value, using the attribute decorated entity will allow LINQ to generate the SQL to pull just those matching values from the database. With the naive property implementation (that simply references the underlying relation property, in effect renaming it), you will be forced to first get all entities, then do the filtering in your application.

Yes you can, but you have to apply the same attributes as the linq2sql generated property i.e.
[Association(Name="Test_TestData", Storage="_TestDatas", ThisKey="SomeId", OtherKey="OtherId")]
public System.Data.Linq.EntitySet<TestData> MyTestDatas
{
get
{
return this.TestDatas;
}
}
TestDatas being the original relation.
Update: A sample query I ran:
var context = new DataClasses1DataContext();
var tests =
from d in context.Tests
where d.MyTestDatas.Any(md=>md.MyId == 2)
select new
{
SomeId = d.SomeId,
SomeData = d.SomeData,
Tests = d.MyTestDatas
};
foreach (var test in tests)
{
var data = test.Tests.ToList();
}

Related

Linq-to-SQL EntitySet Is Not IQueryable -- Any Workarounds?

When you query an EntitySet property on a model object in Linq-to-SQL, it returns all rows from the entityset and does any further querying client-side.
This is confirmed in a few places online and I've observed the behavior myself. The EntitySet does not implement IQueryable.
What I've had to do is convert code like:
var myChild = ... ;
// Where clause performed client-side.
var query = myChild.Parents().Where(...) ;
to:
var myChild = ... ;
// Where clause performed in DB and only minimal set of rows returned.
var query = MyDataContext.Parents().Where(p => p.Child() == myChild) ;
Does anyone know a better solution?
A secondary question: is this fixed in the Entity Framework?
An EntitySet is just a collection of entities. It implements IEnumerable, not IQueryable. The Active Record pattern specifies that entities be directly responsible for their own persistence. OR mapper entities don't have any direct knowledge of the persistence layer. OR Mappers place this responsibility, along with Unit Of Work, and Identity Map responsibilities into the Data Context. So if you need to query the data source, you gotta use the context (or a Table object). To change this would bend the patterns in use.
I had a similar problem: How can I make this SelectMany use a join. After messing with LINQPad for a good amount of time I found a decent workaround. The key is to push the EntitySet you are looking at inside a SelectMany, Select, Where, etc. Once it's inside that it becomes an Expression and then the provider can turn it into a proper query.
Using your example try this:
var query = from c in Children
where c == myChild
from p in c.Parents
where p.Age > 35
select p;
I'm not able to 100% verify this query as I don't know the rest of your model. But the first two lines of the query cause the rest of it to become an Expression that the provider turns into a join. This does work with my own example that is on the question linked to above.

LINQ to SQL table naming

I am using VS2010 and C#
When I map/select my database tables with LINQ to SQL I have to option to change the "member" propery, but when i delete the table (because I changed something in the schema for example) and add it again the member value gets "reset". Is it possible to set/override this member programmaticly, so that I dont have to change it by hand everytime
I mean the member option of
'<'Table Name="dbo.table1" Member="table1">
All L2S ORM classes are partial, so you should be able to encapsulate the table in another property by extending the DataContext class e.g.
public partial class MyDataContext
{
public IEnumerable<Entity> Table
{
get { return DatabaseTable; }
}
}
So in the above scenario you would make your DatabaseTable private and expose it through a another property. You may still need to change that particular piece of code manually if you change the name of your table, but it means you are only changing it once and don't have to change it everytime you reference the table somewhere in your code.

LINQ to SQL Table Extensibility Methods

If I have a LINQ to SQL table that has a field called say Alias.
There is then a method stub called OnAliasChanging(string value);
What I want to do is to grab the value, check the database whether the value already exists and then set the value to the already entered value.
So I may be changing my alias from "griegs" to "slappy" and if slappy exists then I want to revert to the already existing value of "griegs".
So I have;
partial void OnaliasChanging(string value)
{
string prevValue = this.alias;
this.Changed = true;
}
When I check the value of prevValue it's always null.
How can I get the current value of a field?
Update
If I implement something like;
partial void OnaliasChanging(string value)
{
if (this.alias != null)
this.alias = "TEST VALUE";
}
it goes into an infinte loop which is unhealthy.
If I include a check to see whether alias already == "TEST VALUE" the infinate loop still remains as the value is always the original value.
Is there a way to do this?
The code snippets you've posted don't lend themselves to any plausible explanation of why you'd end up with an infinite loop. I'm thinking that this.alias might be a property, as opposed to a field as the character casing would imply, but would need to see more. If it is a property, then you are invoking the OnAliasChanging method before the property is ever set; therefore, trying to set it again in the same method will always cause an infinite loop. Normally the way to design this scenario is to either implement a Cancel property in your OnXyzChanging EventArgs derivative, or save the old value in the OnXyzChanging method and subsequently perform the check/rollback in the OnXyzChanged method if you can't use the first (better) option.
Fundamentally, though, what you're trying to do is not very good design in general and goes against the principles of Linq to SQL specifically. A Linq to SQL entity is supposed to be a POCO with no awareness of sibling entities or the underlying database at all. To perform a dupe-check on every property change not only requires access to the DataContext or SqlConnection, but also causes what could technically be called a side-effect (opening up a new database connection and/or silently discarding the property change). This kind of design just screams for mysterious crashes down the road.
In fact, your particular scenario is one of the main reasons why the DataContext class was made extensible in the first place. This type of operation belongs in there. Let's say that the entity here is called User with table Users.
partial class MyDataContext
{
public bool ChangeAlias(Guid userID, string newAlias)
{
User userToChange = Users.FirstOrDefault(u => u.ID == userID);
if ((userToChange == null) || Users.Any(u => u.Alias == newAlias))
{
return false;
}
userToChange.Alias = newAlias;
// Optional - remove if consumer will make additional changes
SubmitChanges();
return true;
}
}
This encapsulates the operation you want to perform, but doesn't prevent consumers from changing the Alias property directly. If you can live with this, I would stop right there - you should still have a UNIQUE constraint in your database itself, so this method can simply be documented and used as a safe way to attempt a name-change without risking a constraint violation later on (although there is always some risk - you can still have a race condition unless you put this all into a transaction or stored procedure).
If you absolutely must limit access to the underlying property, one way to do this is to hide the original property and make a read-only wrapper. In the Linq designer, click on the Alias property, and on the property sheet, change the Access to Internal and the Name to AliasInternal (but don't touch the Source!). Finally, create a partial class for the entity (I would do this in the same file as the MyDataContext partial class) and write a read-only wrapper for the property:
partial class User
{
public string Alias
{
get { return AliasInternal; }
}
}
You'll also have to update the Alias references in our ChangeAlias method to AliasInternal.
Be aware that this may break queries that try to filter/group on the new Alias wrapper (I believe Linq will complain that it can't find a SQL mapping). The property itself will work fine as an accessor, but if you need to perform lookups on the Alias then you will likely need another GetUserByAlias helper method in MyDataContext, one which can perform the "real" query on AliasInternal.
Things start to get a little dicey when you decide you want to mess with the data-access logic of Linq in addition to the domain logic, which is why I recommend above that you just leave the Alias property alone and document its usage appropriately. Linq is designed around optimistic concurrency; typically when you need to enforce a UNIQUE constraint in your application, you wait until the changes are actually saved and then handle the constraint violation if it happens. If you want to do it immediately your task becomes harder, which is the reason for this verbosity and general kludginess.
One more time - I'm recommending against the additional step of creating the read-only wrapper; I've put up some code anyway in case your spec requires it for some reason.
Is it getting hung up because OnaliasChanging is firing during initialization, so your backing field (alias) never gets initialized so it is always null?
Without more context, that's what it sounds like to me.

How can I filter choices out of a drop down list when using Dynamic Data?

I feel like this should be easy, but I don't see any way to do it.
I'm using ASP.NET Dynamic Data with Linq to SQL. I've got a table with an Association to the Technician table. The Parent Property is TechAssignment, and on the web form I'm using a DynamicField to display it.
This works fine really, it correctly sees it as a ForeignKeyField and uses that template to give me a dropdown with a list of techs from the Technicians table.
The only problem is that it gives me a list of ALL the technicians, when there are quite a few who are inactive. How can I tell Dynamic Data to filter out inactive technicians so they can't be selected?
LINQ to SQL generates partial classes.
Add a new property (copy from the other foreign key property)
Apply the filter in the get (either by LINQ2SQL or filtering the original property)
Bind to that property
Example UnapprovedContacts in Association table
public partial class Association
{
public IList<Contact> UnapprovedContacts
{
get
{
return Contacts.Where(c => !c.IsApproved).ToList();
}
}
}

LINQ Code Generation Property Names

I am using LINQ To SQL to handle data access in a project. For a case where I have multiple foreign keys in a table to the same table (for e.g. CustomerUserId, TechnicianUserId) , it generates the property names like ApplicationUser and ApplicationUser1. Is there a way to tweak the code generator to produce easier to read names.
I was pleasently surprised by LINQPad in this regard. It correctly generates property names (for e.g. in this case CustomerUser and TechnicianUser).
If you use the LINQ2SQL designer, you can select the connection between the two classes and modify the property names (expand Child Property and/or Parent Property in your properties window).