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

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);
}

Related

MvvmCross IMvxNavigationFacade, MvxViewModelRequest causes Init() to be called rather than Prepare()

I've implemented an IMvxNavigationFacade for deep linking in my MvvmCross 5.6.x sample app. I've added logic in BuildViewModelRequest() to construct a MvxViewModelRequest with parameters passed in as MvxBundle.
if (url.StartsWith("http://www.rseg.net/rewards/"))
{
var parametersBundle = new MvxBundle();
var id = url.Substring(url.LastIndexOf('/') + 1);
parametersBundle.Data.Add("id", id);
return Task.FromResult(
new MvxViewModelRequest(typeof(RewardDetailViewModel),
parametersBundle, null));
}
However, this approach causes the old style Init() method to be called in the target ViewModel rather than the new typesafe Prepare() method.
public class RewardDetailViewModel :
MvxViewModel<RewardDetailViewModel.Parameteres>
{
...
public new void Init(string id)
{
if (!string.IsNullOrWhiteSpace(id))
{
if (int.TryParse(id, out _rewardId))
RaiseAllPropertiesChanged();
}
}
public override void Prepare(Parameteres parameter)
{
if (parameter != null)
{
_rewardId = parameter.RewardId;
RaiseAllPropertiesChanged();
}
}
}
Is there a way to construct a MvxViewModelRequest so that you pass in an instance of the parameter class for the target ViewModel causing the Prepare() method to be called?
The entire solution can be viewed on GitHub https://github.com/rsegtx/So.MvvmNav2
Thanks in advance!
After doing some research I found at lease one way to accomplish this.
Create a ViewModelInstanceRequest rather than a ViewModelRequest so that you can call ViewModelLoader.LoadViewModel passing in a parameters object; the ViewModelRequest only allows parameters to be passed using a MvxBundle. Make the following change to BuildViewModelRequest() on the NavigationFacade:
var request = new
MvxViewModelInstanceRequest(typeof(RewardDetailViewModel));
var parameters = new RewardDetailViewModel.Parameteres();
.... parse parameters and fill in parameters object
request.ViewModelInstance = ViewModelLoader.LoadViewModel(
request, parameters, null);
return Task.FromResult((MvxViewModelRequest)request);
Create your own IMvxNavigationService and add logic to inspect the object returned from the NavigationFacde and if it is a ViewModelInstanceRequest then use it as is rather than one previously creating.
var facadeRequest = await facade.BuildViewModelRequest(path,
paramDict).ConfigureAwait(false);
...
if (facadeRequest is MvxViewModelInstanceRequest)
request = facadeRequest as MvxViewModelInstanceRequest;
else
{
facadeRequest.ViewModelType = facadeRequest.ViewModelType;
if (facadeRequest.ParameterValues != null)
{
request.ParameterValues = facadeRequest.ParameterValues;
}
request.ViewModelInstance = ViewModelLoader.LoadViewModel(
request, null);
}
I've updated the original example on GitHub https://github.com/rsegtx/So.MvvmNav2.

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);

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
}

How to modify linqtosql entityref objects in handcoded MVC model?

I am trying to set up my own mvc model rather than letting the environment create one via the graphic designer tool. I had hoped that this would make it easier to keep separate repositories for parts of the model space but so far it has caused me nothing but grief.
The first problem I ran into was that the entityref classes had to be updated via a selectlist control in the view. I managed to get that to work by adding an interal ID field to every entityref much like designer.cs would do. However, it has made the model class quite a bit more complex as the code below demonstrates.
Unfortunately, I now run into a problem when I want to explicitly update some of the entities in the controller. If I manually set the ID field, the update is just dropped, if I change the entity I get an exception while saving.
My model
[Table(Name = "dbo.Jobs")]
public class Job {
[Column(IsPrimaryKey = true, IsDbGenerated = true, AutoSync = AutoSync.OnInsert)]
public int JobID { get; set; }
internal string _CompanyID; // string for legacy reasons
[Column(Storage = "_CompanyID")]
public string CompanyID{
get { return _CompanyID}
set {
if ((_CompanyID != value)) {
if (_MittlerOrg.HasLoadedOrAssignedValue) {
throw new System.Data.Linq.ForeignKeyReferenceAlreadyHasValueException();
}
_CompanyID = value;
}
}
}
internal EntityRef<Company> _Company;
[Association(Storage = "_Company", ThisKey = "CompanyID", OtherKey = "CompanyID", IsForeignKey = true)]
public Company Company {
get { return _Company.Entity; }
set {
Organization previousValue = this._Company.Entity;
if ((previousValue != value) || (_Company.HasLoadedOrAssignedValue == false)) {
if ((previousValue != null)) {
_Company.Entity = null;
}
_Company.Entity = value;
if (value != null) {
_CompanyID = value.OrganizationID;
} else {
_CompanyID = default(string);
}
}
}
}
// The contact depends on the choice of company and should be set
// inside an action method once the company is determined.
internal string _ContactID;
[Column(Storage = "_ContactID")]
public string ContactID {
get { return _ContactID; }
set {
if ((_ContactID != value)) {
if (_Contact.HasLoadedOrAssignedValue) {
throw new System.Data.Linq.ForeignKeyReferenceAlreadyHasValueException();
}
_ContactID = value;
}
}
}
internal EntityRef<User> _Contact;
[Association(Storage = "_Contact", ThisKey = "ContactID", OtherKey = "UserID", IsForeignKey = true)]
public User Contact {
get { return _Contact.Entity; }
set {
User previousValue = this._Contact.Entity;
if ((previousValue != value) || (_Contact.HasLoadedOrAssignedValue == false)) {
if ((previousValue != null)) {
_Contact.Entity = null;
}
_Contact.Entity = value;
if (value != null) {
_ContactID = value.UserID;
} else {
_ContactID = default(string);
}
}
}
}
}
The edit function that causes problems is here. If I step though it in the debugger I see that fi.ContactID gets updated but not committed to the DB.
[HttpPost]
public ActionResult Edit(int id, FormCollection collection) {
var user = userrep.FetchByLogin(User.Identity.Name);
var job = jobrep.FetchByID(id);
try {
var oldvalue = job.CompanyID;
UpdateModel(job, "job");
if (oldvalue != job.CompanyID) {
if (job.CompanyID != null) {
job.ContactID = orgrep.FetchByID(job.CompanyID).DefaultContactID;
} else {
job.ContactID = default(string);
}
}
firep.Save();
return RedirectToAction("Index");
} catch (Exception e) {
}
}
Any idea how to get those pesky entityrefs to behave? I searched up and down the internet but all model layer examples seem to cover the simplest relationships only. Should I just chuck the model completely in favor of managing my references manually though id fields.
Cheers,
Duffy
Update: I never got this piece of code to work robustly so I ended up switching back to letting visual studio generate the DataContext via the drag and drop graphical designer.
I still struggle a bit with fixing the names on the relationship links after an update of the db schema (I carefully name all relationships in the db but the designer tool seems to ignore those names) but since I discovered that the db.designer.cs file can be opened in an xml editor rather than with the GUI, the job got a lot easier.

Extend a LINQ entity-class with constructor methods and make that entity-class inherit from it's DataContext class

Is it possible to extend LINQ-to-SQL entity-classes with constructor-methods and in the same go; make that entity-class inherit from it's data-context class?--In essence converting the entity-class into a business object.
This is the pattern I am currently using:
namespace Xxx
{
public class User : Xxx.DataContext
{
public enum SiteAccessRights
{
NotRegistered = 0,
Registered = 1,
Administrator = 3
}
private Xxx.Entities.User _user;
public Int32 ID
{
get
{
return this._user.UsersID;
}
}
public Xxx.User.SiteAccessRights AccessRights
{
get
{
return (Xxx.User.SiteAccessRights)this._user.UsersAccessRights;
}
set
{
this._user.UsersAccessRights = (Int32)value;
}
}
public String Alias
{
get
{
return this._user.UsersAlias;
}
set
{
this._user.UsersAlias = value;
}
}
public User(Int32 userID)
{
var user = (from u in base.Users
where u.UsersID == userID
select u).FirstOrDefault();
if (user != null)
{
this._user = user;
}
else
{
this._user = new Xxx.Entities.User();
base.Users.InsertOnSubmit(this._user);
}
}
public User(Xxx.User.SiteAccessRights accessRights, String alias)
{
var user = (from u in base.Users
where u.UsersAccessRights == (Int32)accessRights && u.UsersAlias == alias
select u).FirstOrDefault();
if (user != null)
{
this._user = user;
}
else
{
this._user = new Xxx.Entities.User
{
UsersAccessRights = (Int32)accessRights,
UsersAlias = alias
};
base.Users.InsertOnSubmit(this._user);
}
}
public void DeleteOnSubmit()
{
base.Users.DeleteOnSubmit(this._user);
}
}
}
Update:
Notice that I have two constructor-methods in my User class. I'd like to transfer those to the User entity-class and extend the User entity-class on it's data-context class, so that the data-context is available to the entity-class on "new-up".
Hope this makes sense.
Rick Strahl has a number of really good articles that address what I think you are looking for. Check out his list of Linq Articles Here
Inheriting an entity from a data context is a bad idea. They are two discrete objects and are designed to operate that way. Doing this will cause all sorts of issues least of all problems with trying to submit a bunch of related changes together at the same time - going through multiple data contexts will cause this to fail as each tries to work independently.
It doesn't seem to make sense to make an entity a type of DataContext. It doesn't need to be a DataContext in order to be considered a business object, nor do you necessarily need to create a type that contains the original entity. It might be better to just extend the entity class and contain a reference to a DataContext using composition:
namespace Xxx.Entities
{
public partial class User : IDisposable
{ DataContext ctx;
public static GetUserByID(int userID)
{ var ctx = new DataContext();
var user = ctx.Users.FirstOrDefault(u=>u.UsersID == userID);
if (user == null)
{
user = new User();
ctx.Users.InsertOnSubmit(user);
}
user.ctx = ctx;
return user;
}
public void Dispose() { if (ctx != null) ctx.Dispose(); }
}
}
If you just want the property names to be different than the database column names, do that in the mapping file.