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);
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).
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);
}
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 would I write a simple LINQ to SQL extension method called "IsActive" which would contain a few basic criteria checks of a few different fields, so that I could reuse this "IsActive" logic all over the place without duplicating the logic.
For example, I would like to be able to do something like this:
return db.Listings.Where(x => x.IsActive())
And IsActive would be something like:
public bool IsActive(Listing SomeListing)
{
if(SomeListing.Approved==true && SomeListing.Deleted==false)
return true;
else
return false;
}
Otherwise, I am going to have to duplicate the same old where criteria in a million different queries right throughout my system.
Note: method must render in SQL..
Good question, there is a clear need to be able to define a re-useable filtering expression to avoid redundantly specifying logic in disparate queries.
This method will generate a filter you can pass to the Where method.
public Expression<Func<Listing, bool>> GetActiveFilter()
{
return someListing => someListing.Approved && !someListing.Deleted;
}
Then later, call it by:
Expression<Func<Filter, bool>> filter = GetActiveFilter()
return db.Listings.Where(filter);
Since an Expression<Func<T, bool>> is used, there will be no problem translating to sql.
Here's an extra way to do this:
public static IQueryable<Filter> FilterToActive(this IQueryable<Filter> source)
{
var filter = GetActiveFilter()
return source.Where(filter);
}
Then later,
return db.Listings.FilterToActive();
You can use a partial class to achieve this.
In a new file place the following:
namespace Namespace.Of.Your.Linq.Classes
{
public partial class Listing
{
public bool IsActive()
{
if(this.Approved==true && this.Deleted==false)
return true;
else
return false;
}
}
}
Since the Listing object (x in your lambda) is just an object, and Linq to SQL defines the generated classes as partial, you can add functionality (properties, methods, etc) to the generated classes using partial classes.
I don't believe the above will be rendered into the SQL query. If you want to do all the logic in the SQL Query, I would recommend making a method that calls the where method and just calling that when necessary.
EDIT
Example:
public static class DataManager
{
public static IEnumerable<Listing> GetActiveListings()
{
using (MyLinqToSqlDataContext ctx = new MyLinqToSqlDataContext())
{
return ctx.Listings.Where(x => x.Approved && !x.Deleted);
}
}
}
Now, whenever you want to get all the Active Listings, just call DataManager.GetActiveListings()
public static class ExtensionMethods
{
public static bool IsActive( this Listing SomeListing)
{
if(SomeListing.Approved==true && SomeListing.Deleted==false)
return true;
else
return false;
}
}
Late to the party here, but yet another way to do it that I use is:
public static IQueryable<Listing> GetActiveListings(IQueryable<Listing> listings)
{
return listings.Where(x => x.Approved && !x.Deleted);
}
and then
var activeListings = GetActiveListings(ctx.Listings);