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
}
Related
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.
I have a ASP.NET WebApi project that I am working on. The boss would like the returns to support "partial response", meaning that though the data model might contain 50 fields, the client should be able to request specific fields for the response. The reason being that if they are implementing for example a list they simply don't need the overhead of all 50 fields, they might just want the First Name, Last Name and Id to generate the list. Thus far I have implemented a solution by using a custom Contract Resolver (DynamicContractResolver) such that when a request comes in I am peeking into it through a filter (FieldListFilter) in the OnActionExecuting method and determining if a field named "FieldList" is present and then if it is I am replacing the current ContractResolver with a new instance of my DynamicContractResolver and I pass the fieldlist to the constructor.
Some sample code
DynamicContractResolver.cs
protected override IList<JsonProperty> CreateProperties(Type type, Newtonsoft.Json.MemberSerialization memberSerialization)
{
List<String> fieldList = ConvertFieldStringToList();
IList<JsonProperty> properties = base.CreateProperties(type, memberSerialization);
if (fieldList.Count == 0)
{
return properties;
}
// If we have fields, check that FieldList is one of them.
if (!fieldList.Contains("FieldList"))
// If not then add it, FieldList must ALWAYS be a part of any non null field list.
fieldList.Add("FieldList");
if (!fieldList.Contains("Data"))
fieldList.Add("Data");
if (!fieldList.Contains("FilterText"))
fieldList.Add("FilterText");
if (!fieldList.Contains("PageNumber"))
fieldList.Add("PageNumber");
if (!fieldList.Contains("RecordsReturned"))
fieldList.Add("RecordsReturned");
if (!fieldList.Contains("RecordsFound"))
fieldList.Add("RecordsFound");
for (int ctr = properties.Count-1; ctr >= 0; ctr--)
{
foreach (string field in fieldList)
{
if (field.Trim() == properties[ctr].PropertyName)
{
goto Found;
}
}
System.Diagnostics.Debug.WriteLine("Remove Property at Index " + ctr + " Named: " + properties[ctr].PropertyName);
properties.RemoveAt(ctr);
// Exit point for the inner foreach. Nothing to do here.
Found: { }
}
return properties;
}
FieldListFilter.cs
public override void OnActionExecuting(System.Web.Http.Controllers.HttpActionContext actionContext)
{
if (!actionContext.ModelState.IsValid)
{
throw new HttpResponseException(HttpStatusCode.BadRequest);
}
// We need to determine if there is a FieldList property of the model that is being used.
// First get a reference to the model.
var modelObject = actionContext.ActionArguments.FirstOrDefault().Value;
string fieldList = string.Empty;
try
{
// Using reflection, attempt to get the value of the FieldList property
var fieldListTemp = modelObject.GetType().GetProperty("FieldList").GetValue(modelObject);
// If it is null then use an empty string
if (fieldListTemp != null)
{
fieldList = fieldListTemp.ToString();
}
}
catch (Exception)
{
fieldList = string.Empty;
}
// Update the global ContractResolver with the fieldList value but for efficiency only do it if they are not the same as the current ContractResolver.
if (((DynamicContractResolver)GlobalConfiguration.Configuration.Formatters.JsonFormatter.SerializerSettings.ContractResolver).FieldList != fieldList)
{
GlobalConfiguration.Configuration.Formatters.JsonFormatter.SerializerSettings.ContractResolver = new DynamicContractResolver(fieldList);
}
}
I can then send a request with the json content payload looking as such:
{
"FieldList":"NameFirst,NameLast,Id",
"Data":[
{
"Id":1234
},
{
"Id":1235
}
]
}
and I will receive a response like so:
{
"FieldList":"NameFirst,NameLast,Id",
"Data":[
{
"NameFirst":"Brian",
"NameLast":"Mueller",
"Id":1234
},
{
"NameFirst":"Brian",
"NameLast":"Mueller",
"Id":1235
}
]
}
I believe that using the ContractResolver might run into threading issues. If I change it for one request is it going to be valid for all requests thereafter until someone changes it on another request (seems so through testing) If that is the case, then I don't see the usefulness for my purpose.
In summary, I am looking for a way to have dynamic data models such that the output from a request is configurable by the client on a request by request basis. Google implements this in their web api and they call it "partial response" and it works great. My implementation works, to a point but I fear that it will be broken for multiple simultaneous requests.
Suggestions? Tips?
A simpler solution that may work.
Create a model class with all 50 members with nullable types.
Assign values to the requested members.
Just return the result in the normal way.
In your WebApiConfig.Register() you must set the null value handling.
config.Formatters.JsonFormatter.SerializerSettings =
new JsonSerializerSettings { NullValueHandling = NullValueHandling.Ignore };
You must not touch the configuration. You need the contract resolver on per-request basis. You can use it in your action method like this.
public class MyController : ApiController
{
public HttpResponseMessage Get()
{
var formatter = new JsonMediaTypeFormatter();
formatter.SerializerSettings.ContractResolver =
new DynamicContractResolver(new List<string>()
{"Id", "LastName"}); // you will get this from your filter
var dto = new MyDto()
{ FirstName = "Captain", LastName = "Cool", Id = 8 };
return new HttpResponseMessage()
{
Content = new ObjectContent<MyDto>(dto, formatter)
};
// What goes out is {"LastName":"Cool","Id":8}
}
}
By doing this, you are locking yourself into JSON content type for response messages but you have already made that decision by using a Json.NET specific feature. Also, note you are creating a new JsonMediaTypeFormatter. So, anything you configure to the one in the configuration such as media type mapping is not going to be available with this approach though.
I know this question is from many years ago, but if you're looking to do this with modern releases of the framework, I'd recommend nowadays to use OData services (http://www.asp.net/web-api/overview/odata-support-in-aspnet-web-api/using-select-expand-and-value).
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);
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);
}
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.