Linq is not persisting addition to a list? - linq-to-sql

I've got a class T with an association to a cross reference table. I'd like to have a method on the class that adds an entry to the cross reference by receiving an entity representing the other side. For some reason, though, while I can add the item to the collection it doesn't get added to the change set for the data context.
So the class in question looks like:
class T
{
public EntitySet<T_U> t_users
{
get
{
if ((this.serializing && (this.t_user.HasLoadedOrAssignedValues == false)))
{
return null;
}
return this.t_user;
}
set
{
this.Users.Assign(value);
}
}
public AddUser(U user)
{
this.t_users.Add( new T_U() { TID = this.ID, UID = user.ID );
}
}
The client using this class would basically do something like:
var db = new DBDataContext();
var t = db.Ts.FirstOrDefault( t => t.ID = 100);
var u = db.Us.FirstOrDefault(u => u.ID == 3);
t.AddUser(u);
db.SubmitChanges();
Shouldn't that succesfully add a record to the cross reference table?

I think you want InsertOnSubmit instead of Add. Are you using an outdated version of Linq to sql?

You are adding new T_U to this.Users... Does that work? Isn't that the wrong type?
this.Users.Add( new T_U() { TID = this.ID, UID = user.ID );

Related

Passing multiple Include statements into a repository?

I am trying to figure out a way to pass a collection of include statements into my repository so that I can have it include specific entities. Below is some sample code from my repository.
public TEntity GetById(Guid id)
{
return id != Guid.Empty ? GetSet().Find(id) : null;
}
private IDbSet<TEntity> GetSet()
{
return _unitOfWork.CreateSet<TEntity>();
}
The GetByID method calls the GetSet to return the entity set. I was thinking, if I could somehow pass in a collection of entities to include (via an expression) as part of my GetById, this way I wouldn't have to expose the GetSet to my services. So, something like this:
var entity = _repository.GetById(theId, e => {e.Prop1, e.Prop2, e.Prop3});
I could then pass that expression into my GetSet method and pass it into an include statement. Thoughts?
I have done something like this in my code recently. Would the following work for you?
public TEntity GetById(Guid id, params Expression<Func<TEntity, object>>[] includeProperties)
{
if (id == Guid.Empty) return null;
var set = _unitOfWork.CreateSet<TEntity>();
foreach(var includeProperty in includeProperties)
{
set.Include(includeProperty);
}
return set.First(i => i.Id == id);
}
Then you would call it like this...
var entity = _repository.GetById(theId, e => e.Prop1, e=> e.Prop2, e=> e.Prop3);
I know this doesn't exactly follow your pattern, but I think you could refactor it as required.
I don't think Paige Cook's code will work quite as shown.
I've included a modified version of the code that should work instead:
public TEntity GetById(Guid id, params Expression<Func<TEntity, object>>[] includeProperties)
{
if (id == Guid.Empty) return null;
IQueryable<TEntity> set = _unitOfWork.CreateSet<TEntity>();
foreach(var includeProperty in includeProperties)
{
set = set.Include(includeProperty);
}
return set.First(i => i.Id == id);
}
I only spotted this by tracing the SQL generated by Entity Framework, and realised the original code was only giving the illusion of working, by using lazy-loading to populate the entities specified for inclusion.
There's actually a more terse syntax for applying the Include statements using the LINQ Aggregate method, which is in the blog post linked to. My post also improves the method slightly by having a fall-back to the Find method, when no includes are needed and also shows an example of how to implement a "GetAll" method, using similar syntax.
It's bad idea to store context in non-local space, for many reasons.
I modify Steve's code and get this for my ASP.NET MVC projects:
public aspnet_User FirstElement(Func<aspnet_User, bool> predicate = null, params Expression<Func<aspnet_User, object>>[] includes)
{
aspnet_User result;
using (var context = new DataContext())
{
try
{
var set = context.Users.AsQueryable();
for (int i = 0; i < includes.Count(); i++ )
set = set.Include(includes[i]);
if (predicate != null)
result = set.ToList().FirstOrDefault(predicate);
else
result = set.ToList().FirstOrDefault();
}
catch
{
result = null;
}
}
return result;
}
The include method can be strung together in your linq query like so:
var result = (from i in dbContext.TableName.Include("RelationProperty")
.Include("RelationProperty")
.Include("RelationProperty")
select i);

How do you get the DataContext of a LINQ to SQL Entity?

Currently this is what I have:
public partial class LinqToSqlEntity {
public IQueryable<AnotherLinqToSqlEntity> AnotherLinqToSqlEntities {
using(DataContext context = new DataContext) {
return context.AnotherLinqToSqlEntities.Where(item => item.Property == SOME_VALUE);
}
}
}
Is there a way to get the DataContext of this so that I would not need to create a new DataContext?
Sorry, that is not possible. An entity or querable in that case keeps no direct reference of the context.
You can achieve that using the reflection by figuring out if PropertyChanging event was hooked up, but consider this a hack and maybe you can avoid using it with better design.
Our use case of this is on detach_EntityName delegate where we change the default Linq behaviour of only deleting the foreign key of a record (setting it to null), with the actual delete from DB.
public static DataContext GetDataContextFromEntityObject(object entity)
{
// Use a reflection to get the invocaiton list.
var o = (PropertyChangingEventHandler)entity.GetType().GetField("PropertyChanging", BindingFlags.Instance | BindingFlags.NonPublic).GetValue(entity);
var o = GetFieldValue(entity, "PropertyChanging");
if (o == null) return null;
var invocationList = o.GetInvocationList();
if (invocationList != null)
{
// DataContext changes are tracked through StandardChangeTracker
object changeTracker = (from i in invocationList where i.Target.GetType().FullName == "System.Data.Linq.ChangeTracker+StandardChangeTracker" select i.Target).FirstOrDefault();
if (changeTracker != null)
{
object services = GetFieldValue(changeTracker, "services");
return (DataContext)GetFieldValue(services, "context");
}
}
return null;
}
private static object GetFieldValue(object instance, string propertyName)
{
return instance.GetType().GetField(propertyName, BindingFlags.Instance | BindingFlags.NonPublic).GetValue(instance);
}

Find out what fields are being updated

I'm using LINQ To SQL to update a user address.
I'm trying to track what fields were updated.
The GetChangeSet() method just tells me I'm updating an entity, but doesn't tell me what fields.
What else do I need?
var item = context.Dc.Ecs_TblUserAddresses.Single(a => a.ID == updatedAddress.AddressId);
//ChangeSet tracking
item.Address1 = updatedAddress.AddressLine1;
item.Address2 = updatedAddress.AddressLine2;
item.Address3 = updatedAddress.AddressLine3;
item.City = updatedAddress.City;
item.StateID = updatedAddress.StateId;
item.Zip = updatedAddress.Zip;
item.Zip4 = updatedAddress.Zip4;
item.LastChangeUserID = request.UserMakingRequest;
item.LastChangeDateTime = DateTime.UtcNow;
ChangeSet set = context.Dc.GetChangeSet();
foreach (var update in set.Updates)
{
if (update is EberlDataContext.EberlsDC.Entities.Ecs_TblUserAddress)
{
}
}
Use ITable.GetModifiedMembers. It returns an array of ModifiedMemberInfo objects, one for each modified property on the entity. ModifiedMemberInfo contains a CurrentValue and OriginalValue, showing you exactly what has changed. It's a very handy LINQ to SQL feature.
Example:
ModifiedMemberInfo[] modifiedMembers = context.YourTable.GetModifiedMembers(yourEntityObject);
foreach (ModifiedMemberInfo mmi in modifiedMembers)
{
Console.WriteLine(string.Format("{0} --> {1}", mmi.OriginalValue, mmi.CurrentValue));
}
You can detect Updates by observing notifications of changes. Notifications are provided through the PropertyChanging or PropertyChanged events in property setters.
E.g. you can extend your generated Ecs_TblUserAddresses class like this:
public partial class Ecs_TblUserAddresses
{
partial void OnCreated()
{
this.PropertyChanged += new PropertyChangedEventHandler(User_PropertyChanged);
}
protected void User_PropertyChanged(object sender, PropertyChangedEventArgs e)
{
string propertyName = e.PropertyName;
// do what you want
}
}
Alternatively, if you want to track a special property changing, you could use one of those OnPropertyNameChanging partial methods, e.g. (for City in your example):
partial void OnCityChanging(string value)
{
// value parameter holds a new value
}

A good(elegant) way to retrieve records with counts

Context: ASP.NET MVC 2.0, C#, SQL Server 2008, IIS7
I have 'scheduledMeetings' table in the database.
There is a one-to-many relationship: scheduledMeeting -> meetingRegistration
So that you could have 10 people registered for a meeting.
meetingRegistration has fields Name, and Gender (for example).
I have a "calendar view" on my site that shows all coming events, as well as gender count for each event.
At the moment I use Linq to Sql to pull the data:
var meetings = db.Meetings.Select(
m => new {
MeetingId = m.Id,
Girls = m.Registrations.Count(r => r.Gender == 0),
Boys = m.Registrations.Count(r=>r.Gender == 1)
});
(actual query is half-a-page long)
Because there is anonymous type use going on I cant extract it into a method (since I have several different flavors of calendar view, with different information on each, and I don't want to create new class for each).
Any suggestions on how to improve this?
Is database view is the answer?
Or should I go ahead and create named-type?
Any feedback/suggestions are welcome. My DataLayer is huge, I want to trim it, just don't know how.
Pointers to a good reading would be good too.
I'd extend your Meetings class by adding 2 properties:
public partial class Meeting
{
#region Properties
public int BoyCount { get; set; }
public int GirlCount { get; set; }
#endregion
}
With deferred loading:
var items = db.Meetings.Select(
m => new {
Meeting = m,
Girls = m.Registrations.Count(r => r.Gender == 0),
Boys = m.Registrations.Count(r = >r.Gender == 1)
}).ToList();
items.ForEach(i =>
{
i.Meeting.BoyCount = i.Boys;
i.Meeting.GirlCount = i.Girl;
});
List<Meeting> = items
.Select(i => i.Meeting)
.ToList();
With eager loading, one of the solutions is to load Registrations with your Meeting entity:
DataLoadOptions loadOptions = new DataLoadOptions();
loadOptions.LoadWith<Meeting>(m = > m.Registrations);
db.LoadOptions = loadOptions;
In this case the partial class properties above are became getters:
public partial class Meeting
{
#region Properties
public int BoyCount
{
get
{
return this.Registrations
.Count(r => r.Gender == 1);
}
}
public int GirlCount
{
get
{
return this.Registrations
.Count(r = > r.Gender == 0);
}
}
#endregion
}

Correct way to remove a many-to-many relationship via linq to sql?

Let's say we have two tables with a many-to-many relationship:
public class Left{ /**/ }
public class Right{ /**/ }
public class LeftRight{ /**/ }
is the following sufficient to unhook these records (ignore the possibility of more than one relationship or no relationship defined)?
public void Unhook(Left left, Right right){
var relation = from x in Left.LeftRights where x.Right == right;
left.LeftRrights.Remove(relation.First());
Db.SubmitChanges();
}
Or do I have to do it on both parts? What's required here?
Here is a 'little' extension method I wrote to simplify this problem:
public static class EntitySetExtensions
{
public static void UpdateReferences<FK, FKV>(
this EntitySet<FK> refs,
Func<FK, FKV> fkvalue,
Func<FKV, FK> fkmaker,
Action<FK> fkdelete,
IEnumerable<FKV> values)
where FK : class
where FKV : class
{
var fks = refs.Select(fkvalue).ToList();
var added = values.Except(fks);
var removed = fks.Except(values);
foreach (var add in added)
{
refs.Add(fkmaker(add));
}
foreach (var r in removed)
{
var res = refs.Single(x => fkvalue(x) == r);
refs.Remove(res);
fkdelete(res);
}
}
}
It could probably be improved, but it has served me well :)
Example:
Left entity = ...;
IEnumerable<Right> rights = ...;
entity.LeftRights.UpdateReferences(
x => x.Right, // gets the value
x => new LeftRight { Right = x }, // make reference
x => { x.Right = null; }, // clear references
rights);
Algorithm description:
Suppose A and B is many-to-many relationship, where AB would be the intermediary table.
This will give you:
class A { EntitySet<B> Bs {get;} }
class B { EntitySet<A> As {get;} }
class AB { B B {get;} A A {get;} }
You now have an object of A, that reference many B's via AB.
Get all the B from A.Bs via 'fkvalue'.
Get what was added.
Get what was removed.
Add all the new ones, and construct AB via 'fkmaker'.
Delete all the removed ones.
Optionally, remove other referenced objects via 'fkdelete'.
I would like to improve this by using Expression instead, so I could 'template' the method better, but it would work the same.
Take two, using expressions:
public static class EntitySetExtensions
{
public static void UpdateReferences<FK, FKV>(
this EntitySet<FK> refs,
Expression<Func<FK, FKV>> fkexpr,
IEnumerable<FKV> values)
where FK : class
where FKV : class
{
Func<FK, FKV> fkvalue = fkexpr.Compile();
var fkmaker = MakeMaker(fkexpr);
var fkdelete = MakeDeleter(fkexpr);
var fks = refs.Select(fkvalue).ToList();
var added = values.Except(fks);
var removed = fks.Except(values);
foreach (var add in added)
{
refs.Add(fkmaker(add));
}
foreach (var r in removed)
{
var res = refs.Single(x => fkvalue(x) == r);
refs.Remove(res);
fkdelete(res);
}
}
static Func<FKV, FK> MakeMaker<FKV, FK>(Expression<Func<FK, FKV>> fkexpr)
{
var me = fkexpr.Body as MemberExpression;
var par = Expression.Parameter(typeof(FKV), "fkv");
var maker = Expression.Lambda(
Expression.MemberInit(Expression.New(typeof(FK)),
Expression.Bind(me.Member, par)), par);
var cmaker = maker.Compile() as Func<FKV, FK>;
return cmaker;
}
static Action<FK> MakeDeleter<FK, FKV>(Expression<Func<FK, FKV>> fkexpr)
{
var me = fkexpr.Body as MemberExpression;
var pi = me.Member as PropertyInfo;
var par = Expression.Parameter(typeof(FK), "fk");
var maker = Expression.Lambda(
Expression.Call(par, pi.GetSetMethod(),
Expression.Convert(Expression.Constant(null), typeof(FKV))), par);
var cmaker = maker.Compile() as Action<FK>;
return cmaker;
}
}
Now the usage is uber simple! :)
Left entity = ...;
IEnumerable<Right> rights = ...;
entity.LeftRights.UpdateReferences(x => x.Right, rights);
The first expression is now used to establish the 'relationship'. From there I can infer the 2 previously required delegates. Now no more :)
Important:
To get this to work properly in Linq2Sql, you need to mark the associations from intermediary table with 'DeleteOnNull="true"' in the dbml file. This will break the designer, but still works correctly with SqlMetal.
To unbreak the designer, you need to remove those additional attributes.
Personally, I'd replace
left.LeftRrights.Remove(relation.First());
with
Db.LeftRights.DeleteAllOnSubmit(relation)
because it seems more obvious what's going to happen. If you are wondering what the behaviour of ".Remove" is now, you'll be wondering anew when you look at this code in 6 months time.