I just created a simple web API using .NetCore 2.2 and Entity Framework.
I added a bit of security, by passing in a userID to each controller that the user accesses.
But I noticed that it starts getting messy when I have to add the userID to every controller in my app and the run my user check to make sure the user can access that content.
Below you'll see an example of what I mean.
I'm wondering, is there a way to add it once and then have every controller check for it?
Thanks!
[Route("api/[controller]")]
[ApiController]
public class EngineController : ControllerBase
{
private readonly engineMaker_Context _context;
public EngineController(engineMaker_Context context)
{
_context = context;
}
// GET: api/Engine
[HttpGet("{userID}")]
public async Task<ActionResult<IEnumerable<Engine>>> GetEngine(string userID)
{
if(!CanAccessContent(userID))
{
return Unauthorized();
}
return await _context.Engine.ToListAsync();
}
// GET: api/Engine/123/5
[HttpGet("{userID}/{id}")]
public async Task<ActionResult<Engine>> GetEngine(string userID, string id)
{
if(!CanAccessContent(userID))
{
return Unauthorized();
}
var engine = await _context.Engine.FindAsync(id);
if (engine == null)
{
return NotFound();
}
return engine;
}
// PUT: api/Engine/123/5
[HttpPut("{userID}/{id}")]
public async Task<IActionResult> PutEngine(string userID, string id, Engine engine)
{
if(!CanAccessContent(userID))
{
return Unauthorized();
}
if (id != engine.ObjectId)
{
return BadRequest();
}
_context.Entry(engine).State = EntityState.Modified;
try
{
await _context.SaveChangesAsync();
}
catch (DbUpdateConcurrencyException)
{
if (!EngineExists(id))
{
return NotFound();
}
else
{
throw;
}
}
return NoContent();
}
private bool CanAccessContent(string userID)
{
return _context.AllowedUsers.Any(e => e.UserId == userID);
}
}
You could try IAsyncAuthorizationFilter to check the userID.
IAsyncAuthorizationFilter
public class UserIdFilter : IAsyncAuthorizationFilter
{
public Task OnAuthorizationAsync(AuthorizationFilterContext context)
{
var dbContext = context.HttpContext.RequestServices.GetRequiredService<ApplicationDbContext>();
var userId = context.RouteData.Values["userID"] as string;
if (!dbContext.Users.Any(u => u.Email == userId))
{
context.Result = new UnauthorizedResult();
}
return Task.CompletedTask;
}
}
Regiter UserIdFilter for all action.
services.AddMvc(options =>
{
options.Filters.Add(typeof(UserIdFilter));
})
.SetCompatibilityVersion(CompatibilityVersion.Version_2_2);
The specified transaction is not associated with the current connection. Only transactions associated with the current connection may be used.
How do I use multiple DbContext in one transaction?
Update 1
If I use ExistingConnection,
then all the DbContext will use the same connection string.
Did I add multiple DbContext in the wrong way?
In EntityFrameworkModule:
public override void PreInitialize()
{
var configuration = AppConfigurations.Get(WebContentDirectoryFinder.CalculateContentRootFolder());
Configuration.Modules.AbpEfCore().AddDbContext<BPDbContext>(options =>
{
if (options.ExistingConnection != null)
{
options.DbContextOptions.UseSqlServer(options.ExistingConnection);
}
else
{
options.DbContextOptions.UseSqlServer(configuration.GetConnectionString(ABPCoreConsts.BPConnectionStringName));
}
//options.DbContextOptions.UseSqlServer(
// configuration.GetConnectionString(ABPCoreConsts.BPConnectionStringName));
});
Configuration.Modules.AbpEfCore().AddDbContext<EPlusDBConext>(options =>
{
if (options.ExistingConnection != null)
{
options.DbContextOptions.UseSqlServer(options.ExistingConnection);
}
else
{
options.DbContextOptions.UseSqlServer(configuration.GetConnectionString(ABPCoreConsts.EECPlusConnectionStringName));
}
//options.DbContextOptions.UseSqlServer(
// configuration.GetConnectionString(ABPCoreConsts.EECPlusConnectionStringName));
});
Configuration.Modules.AbpEfCore().AddDbContext<ProjectManageDbContext>(options =>
{
if (options.ExistingConnection != null)
{
options.DbContextOptions.UseSqlServer(options.ExistingConnection);
}
else
{
options.DbContextOptions.UseSqlServer(configuration.GetConnectionString(ABPCoreConsts.PMConnectionStringName));
}
//options.DbContextOptions.UseSqlServer(
// configuration.GetConnectionString(ABPCoreConsts.PMConnectionStringName));
});
RegisterGenericRepositories();
}
Update 2
I got it to work by implementing IConnectionStringResolver for custom connections:
public class MyDBConnectionStringResolver : DefaultConnectionStringResolver
{
public override string GetNameOrConnectionString(ConnectionStringResolveArgs args)
{
var configuration = AppConfigurations.Get(WebContentDirectoryFinder.CalculateContentRootFolder());
switch (args["DbContextType"].ToString())
{
case "ABPCore.EPlusDBConext":
return configuration.GetConnectionString(ABPCoreConsts.EECPlusConnectionStringName);
case "ABPCore.BPDbContext":
return configuration.GetConnectionString(ABPCoreConsts.BPConnectionStringName);
case "ABPCore.ProjectManageDbContext":
return configuration.GetConnectionString(ABPCoreConsts.PMConnectionStringName);
}
return string.Empty;
}
}
Don't forget to replace service in EntityFrameworkModule's PreInitialize method:
Configuration.ReplaceService<IConnectionStringResolver, MyDbConnectionStringResolver>(DependencyLifeStyle.Transient);
Add this in *DbContextConfigurer.cs:
public static void Configure(DbContextOptionsBuilder<*DbContext> builder, DbConnection connection)
{
builder.UseSqlServer(connection);
}
Change this in *EntityFrameworkModule.cs:
public override void PreInitialize()
{
if (!SkipDbContextRegistration)
{
Configuration.Modules.AbpEfCore().AddDbContext<*DbContext>(options =>
{
if (options.ExistingConnection != null)
{
options.DbContextOptions.UseSqlServer(options.ExistingConnection);
}
else
{
options.DbContextOptions.UseSqlServer(options.ConnectionString);
}
});
}
}
Reference: https://github.com/aspnetboilerplate/module-zero-core-template/commit/da522e76ca2ecefdb7670f009f78575c5b97b4a0
Important
If each DbContext has its own connection string, you need to implement IConnectionStringResolver as explained here and replace the service:
Configuration.ReplaceService<IConnectionStringResolver, MyConnectionStringResolver>(DependencyLifeStyle.Transient);
I am trying to inject dependencies into a Web Api Controller.
I created an own IHttpControllerActivator class and replaced the default one in lobalConfiguration.
public class SimpleASPWebAPIContainer : IHttpControllerActivator
{
private readonly CompositionContainer container;
public SimpleASPWebAPIContainer(CompositionContainer compositionContainer)
{
container = compositionContainer;
}
public IHttpController Create(System.Net.Http.HttpRequestMessage request, System.Web.Http.Controllers.HttpControllerDescriptor controllerDescriptor, Type controllerType)
{
if (controllerType != null)
{
var export = container.GetExports(controllerType, null, null).FirstOrDefault();
IHttpController result = null;
if (null != export)
{
result = export.Value as IHttpController;
}
else
{
//result = base.GetControllerInstance(requestContext, controllerType);
//container.ComposeParts(result);
}
return result;
}
else
{
return null;
}
}
public void Dispose()
{
if (container != null)
container.Dispose();
}
}
var apiSimpleContainer = new SimpleASPWebAPIContainer(container);
System.Web.Http.GlobalConfiguration.Configuration.Services.Replace(typeof(IHttpControllerActivator), apiSimpleContainer);
But when the client app is calling a controller method the IHttpControllerActivation Create method is not invoked.
Anybody can help me?
It was a very silly mistake.
public void Configuration(IAppBuilder app)
{
HttpConfiguration config = new HttpConfiguration();
ConfigureOAuth(app);
MefConfig.RegisterMef(config);
WebApiConfig.Register(config);
app.UseCors(Microsoft.Owin.Cors.CorsOptions.AllowAll);
app.UseWebApi(config);
AutoMapperConfig.InitAutoMapper();
}
I should have to used the new HttoConfiguration instance to replace default IHttpControllerActivator instead of System.Web.Http.GlobalConfiguration.Configuration.
Trying to remove or place items on a property grid by changing the Browsable attribute.
But unless browsable is set on object creation my code to change Browsable doesn't work. Now I can manually add browsable, but when I make a change to my entity (still developing project so lots of changes to entity) any additional attributes I add go away.
I attempted to set [Browsable(true)] two ways other ways: http://ardalis.com/adding-attributes-to-generated-classes and http://social.msdn.microsoft.com/Forums/en-US/adodotnetentityframework/thread/617ebfca-0f68-4b90-83fd-0da758fadbd0/
Both seem to actually set the Browsable correctly, but when I loop thru the Attributes in Property Descriptor it is not there (for me to change).
String fieldname = "browsable"; // I also edit "description"
PropertyDescriptor pd = TypeDescriptor.GetProperties(o.GetType())[propertyName];
object attrib = null;
AttributeCollection attribs = pd.Attributes;
foreach (Attribute a in attribs)
{
if (a.GetType() == attributeType)
{
attrib = a;
break;
}
}
// The microsoft documentation leads one to believe the following line of code would find the desired attribute,
// negating the need for the more complete foreach statement above.
// However, it appears to find attribute even when it does not exist. Setting value for "found" attribute
// will result in random memory being changed, which results in very unpredictable behavior.
// attrib = pd.Attributes[t];
if (attrib != null)
{
// locate field that contains value
FieldInfo field = attrib.GetType().GetField(fieldName, BindingFlags.NonPublic | BindingFlags.Instance);
if (field != null)
{
if (field.FieldType == value.GetType())
{
// set field to desired value
field.SetValue(attrib, value);
}
}
}
else
{
throw new Exception("Attribute (" + attributeType.Name + ") does not exist for Property(" + propertyName + ")");
}
So I keep getting the Exception that I throw if it doesn't find "browsable" - but only if not set in Model.Designer.cs first.
Below is what my Model.Designer.cs looks like.
/// <summary>
/// No Metadata Documentation available.
/// </summary>
[EdmScalarPropertyAttribute(EntityKeyProperty=false, IsNullable=true)]
[DataMemberAttribute()]
[Browsable(false)] // this works, but goes away if change my entity
public Nullable<global::System.TimeSpan> SignoutAfter
{
get
{
return _SignoutAfter;
}
set
{
OnSignoutAfterChanging(value);
ReportPropertyChanging("SignoutAfter");
_SignoutAfter = StructuralObject.SetValidValue(value);
ReportPropertyChanged("SignoutAfter");
OnSignoutAfterChanged();
}
}
private Nullable<global::System.TimeSpan> _SignoutAfter;
partial void OnSignoutAfterChanging(Nullable<global::System.TimeSpan> value);
partial void OnSignoutAfterChanged();
So I need a way to either 1. add browsable to entity when I edit them so it is always on perhaps editing the t4, but I don't even know where to begin with that or 2. Another way to add or remove (and edit) the properties (see I might edit the description based on some logic) or 3 find the hole in my code so I can find and edit browsable (description and displayname).
Update The second link above, http://social.msdn.microsoft.com/Forums/en-US/adodotnetentityframework/thread/617ebfca-0f68-4b90-83fd-0da758fadbd0/ , has a lot of what I need, I think. Adding a Attribute array variable to the class and some code to see if that is set seems to have the effect that I am looking for. But leaving this open to find a better answer.
partial class Client : ICustomTypeDescriptor
{
public Attribute[] SignOutAttributes; // added this
#region ICustomTypeDescriptor Members
... // see the link for the other code
public PropertyDescriptorCollection GetProperties (Attribute[] attributes)
{
var propsColl = TypeDescriptor.GetProperties (this, attributes, true);
var props = new List<PropertyDescriptor> ();
foreach (PropertyDescriptor prop in propsColl)
{
String strUPPERCaseName = prop.Name.ToUpper (); // for my thick fingers
// make sure case values are upper case
switch (strUPPERCaseName)
{
case "SIGNOUTAFTER":
if (SignOutAttributes != null)
{
props.Add(new CustomPropertyDescriptor(prop, SignOutAttributes));
}
else
{
props.Add (new CustomPropertyDescriptor (prop, new Attribute[]
{
new CategoryAttribute("Settings"),
new DisplayNameAttribute("Signout After"),
new BrowsableAttribute(true),
new ReadOnlyAttribute(false)
}));
}
break;
default:
props.Add (prop);
break;
}
}
return new PropertyDescriptorCollection (props.ToArray ());
}
In my code I can change the Attribute Array to have what Attribute values I want.
_client.SignOutAttributes = new Attribute[]
{
new CategoryAttribute ("My Category"),
new DisplayNameAttribute("Signout After"),
new BrowsableAttribute(true),
new ReadOnlyAttribute(false)
};
I'm not 100% happy with this. I have to write code for each Property.
Using ICustomTypeDescriptor is definitely the good solution when you want dynamic (set at runtime) properties. Here is generic ICustomTypeDescriptor utility class that I've been using for this sort of property grid hacking, it's pretty straightforward to use:
public sealed class DynamicTypeDescriptor: ICustomTypeDescriptor, INotifyPropertyChanged
{
private Type _type;
private AttributeCollection _attributes;
private TypeConverter _typeConverter;
private Dictionary<Type, object> _editors;
private EventDescriptor _defaultEvent;
private PropertyDescriptor _defaultProperty;
private EventDescriptorCollection _events;
public event PropertyChangedEventHandler PropertyChanged;
private DynamicTypeDescriptor()
{
}
public DynamicTypeDescriptor(Type type)
{
if (type == null)
throw new ArgumentNullException("type");
_type = type;
_typeConverter = TypeDescriptor.GetConverter(type);
_defaultEvent = TypeDescriptor.GetDefaultEvent(type);
_defaultProperty = TypeDescriptor.GetDefaultProperty(type);
_events = TypeDescriptor.GetEvents(type);
List<PropertyDescriptor> normalProperties = new List<PropertyDescriptor>();
OriginalProperties = TypeDescriptor.GetProperties(type);
foreach (PropertyDescriptor property in OriginalProperties)
{
if (!property.IsBrowsable)
continue;
normalProperties.Add(property);
}
Properties = new PropertyDescriptorCollection(normalProperties.ToArray());
_attributes = TypeDescriptor.GetAttributes(type);
_editors = new Dictionary<Type, object>();
object editor = TypeDescriptor.GetEditor(type, typeof(UITypeEditor));
if (editor != null)
{
_editors.Add(typeof(UITypeEditor), editor);
}
editor = TypeDescriptor.GetEditor(type, typeof(ComponentEditor));
if (editor != null)
{
_editors.Add(typeof(ComponentEditor), editor);
}
editor = TypeDescriptor.GetEditor(type, typeof(InstanceCreationEditor));
if (editor != null)
{
_editors.Add(typeof(InstanceCreationEditor), editor);
}
}
public T GetPropertyValue<T>(string name, T defaultValue)
{
if (name == null)
throw new ArgumentNullException("name");
foreach (PropertyDescriptor pd in Properties)
{
if (pd.Name == name)
{
try
{
return (T)Convert.ChangeType(pd.GetValue(Component), typeof(T));
}
catch
{
return defaultValue;
}
}
}
return defaultValue;
}
public void SetPropertyValue(string name, object value)
{
if (name == null)
throw new ArgumentNullException("name");
foreach (PropertyDescriptor pd in Properties)
{
if (pd.Name == name)
{
pd.SetValue(Component, value);
break;
}
}
}
internal void OnValueChanged(PropertyDescriptor prop)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(prop.Name));
}
}
internal static T GetAttribute<T>(AttributeCollection attributes) where T : Attribute
{
if (attributes == null)
return null;
foreach (Attribute att in attributes)
{
if (typeof(T).IsAssignableFrom(att.GetType()))
return (T)att;
}
return null;
}
public sealed class DynamicProperty: PropertyDescriptor, INotifyPropertyChanged
{
private readonly Type _type;
private readonly bool _hasDefaultValue;
private readonly object _defaultValue;
private readonly PropertyDescriptor _existing;
private readonly DynamicTypeDescriptor _descriptor;
private Dictionary<Type, object> _editors;
private bool? _readOnly;
private bool? _browsable;
private string _displayName;
private string _description;
private string _category;
private List<Attribute> _attributes = new List<Attribute>();
public event PropertyChangedEventHandler PropertyChanged;
internal DynamicProperty(DynamicTypeDescriptor descriptor, Type type, object value, string name, Attribute[] attrs)
: base(name, attrs)
{
_descriptor = descriptor;
_type = type;
Value = value;
DefaultValueAttribute def = DynamicTypeDescriptor.GetAttribute<DefaultValueAttribute>(Attributes);
if (def == null)
{
_hasDefaultValue = false;
}
else
{
_hasDefaultValue = true;
_defaultValue = def.Value;
}
if (attrs != null)
{
foreach (Attribute att in attrs)
{
_attributes.Add(att);
}
}
}
internal static Attribute[] GetAttributes(PropertyDescriptor existing)
{
List<Attribute> atts = new List<Attribute>();
foreach (Attribute a in existing.Attributes)
{
atts.Add(a);
}
return atts.ToArray();
}
internal DynamicProperty(DynamicTypeDescriptor descriptor, PropertyDescriptor existing, object component)
: this(descriptor, existing.PropertyType, existing.GetValue(component), existing.Name, GetAttributes(existing))
{
_existing = existing;
}
public void RemoveAttributesOfType<T>() where T : Attribute
{
List<Attribute> remove = new List<Attribute>();
foreach (Attribute att in _attributes)
{
if (typeof(T).IsAssignableFrom(att.GetType()))
{
remove.Add(att);
}
}
foreach (Attribute att in remove)
{
_attributes.Remove(att);
}
}
public IList<Attribute> AttributesList
{
get
{
return _attributes;
}
}
public override AttributeCollection Attributes
{
get
{
return new AttributeCollection(_attributes.ToArray());
}
}
public object Value { get; set; }
public override bool CanResetValue(object component)
{
if (_existing != null)
return _existing.CanResetValue(component);
return _hasDefaultValue;
}
public override Type ComponentType
{
get
{
if (_existing != null)
return _existing.ComponentType;
return typeof(object);
}
}
public override object GetValue(object component)
{
if (_existing != null)
return _existing.GetValue(component);
return Value;
}
public override string Category
{
get
{
if (_category != null)
return _category;
return base.Category;
}
}
public void SetCategory(string category)
{
_category = category;
}
public override string Description
{
get
{
if (_description != null)
return _description;
return base.Description;
}
}
public void SetDescription(string description)
{
_description = description;
}
public override string DisplayName
{
get
{
if (_displayName != null)
return _displayName;
if (_existing != null)
return _existing.DisplayName;
return base.DisplayName;
}
}
public void SetDisplayName(string displayName)
{
_displayName = displayName;
}
public override bool IsBrowsable
{
get
{
if (_browsable.HasValue)
return _browsable.Value;
return base.IsBrowsable;
}
}
public void SetBrowsable(bool browsable)
{
_browsable = browsable;
}
public override bool IsReadOnly
{
get
{
if (_readOnly.HasValue)
return _readOnly.Value;
if (_existing != null)
return _existing.IsReadOnly;
ReadOnlyAttribute att = DynamicTypeDescriptor.GetAttribute<ReadOnlyAttribute>(Attributes);
if (att == null)
return false;
return att.IsReadOnly;
}
}
public void SetIsReadOnly(bool readOnly)
{
_readOnly = readOnly;
}
public override Type PropertyType
{
get
{
if (_existing != null)
return _existing.PropertyType;
return _type;
}
}
public override void ResetValue(object component)
{
if (_existing != null)
{
_existing.ResetValue(component);
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(Name));
}
_descriptor.OnValueChanged(this);
return;
}
if (CanResetValue(component))
{
Value = _defaultValue;
_descriptor.OnValueChanged(this);
}
}
public override void SetValue(object component, object value)
{
if (_existing != null)
{
_existing.SetValue(component, value);
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(Name));
}
_descriptor.OnValueChanged(this);
return;
}
Value = value;
_descriptor.OnValueChanged(this);
}
public override bool ShouldSerializeValue(object component)
{
if (_existing != null)
return _existing.ShouldSerializeValue(component);
return false;
}
public override object GetEditor(Type editorBaseType)
{
if (editorBaseType == null)
throw new ArgumentNullException("editorBaseType");
if (_editors != null)
{
object type;
if ((_editors.TryGetValue(editorBaseType, out type)) && (type != null))
return type;
}
return base.GetEditor(editorBaseType);
}
public void SetEditor(Type editorBaseType, object obj)
{
if (editorBaseType == null)
throw new ArgumentNullException("editorBaseType");
if (_editors == null)
{
if (obj == null)
return;
_editors = new Dictionary<Type, object>();
}
if (obj == null)
{
_editors.Remove(editorBaseType);
}
else
{
_editors[editorBaseType] = obj;
}
}
}
public PropertyDescriptor AddProperty(Type type, string name, object value, string displayName, string description, string category, bool hasDefaultValue, object defaultValue, bool readOnly)
{
return AddProperty(type, name, value, displayName, description, category, hasDefaultValue, defaultValue, readOnly, null);
}
public PropertyDescriptor AddProperty(
Type type,
string name,
object value,
string displayName,
string description,
string category,
bool hasDefaultValue,
object defaultValue,
bool readOnly,
Type uiTypeEditor)
{
if (type == null)
throw new ArgumentNullException("type");
if (name == null)
throw new ArgumentNullException("name");
List<Attribute> atts = new List<Attribute>();
if (!string.IsNullOrEmpty(displayName))
{
atts.Add(new DisplayNameAttribute(displayName));
}
if (!string.IsNullOrEmpty(description))
{
atts.Add(new DescriptionAttribute(description));
}
if (!string.IsNullOrEmpty(category))
{
atts.Add(new CategoryAttribute(category));
}
if (hasDefaultValue)
{
atts.Add(new DefaultValueAttribute(defaultValue));
}
if (uiTypeEditor != null)
{
atts.Add(new EditorAttribute(uiTypeEditor, typeof(UITypeEditor)));
}
if (readOnly)
{
atts.Add(new ReadOnlyAttribute(true));
}
DynamicProperty property = new DynamicProperty(this, type, value, name, atts.ToArray());
AddProperty(property);
return property;
}
public void RemoveProperty(string name)
{
if (name == null)
throw new ArgumentNullException("name");
List<PropertyDescriptor> remove = new List<PropertyDescriptor>();
foreach (PropertyDescriptor pd in Properties)
{
if (pd.Name == name)
{
remove.Add(pd);
}
}
foreach (PropertyDescriptor pd in remove)
{
Properties.Remove(pd);
}
}
public void AddProperty(PropertyDescriptor property)
{
if (property == null)
throw new ArgumentNullException("property");
Properties.Add(property);
}
public override string ToString()
{
return base.ToString() + " (" + Component + ")";
}
public PropertyDescriptorCollection OriginalProperties { get; private set; }
public PropertyDescriptorCollection Properties { get; private set; }
public DynamicTypeDescriptor FromComponent(object component)
{
if (component == null)
throw new ArgumentNullException("component");
if (!_type.IsAssignableFrom(component.GetType()))
throw new ArgumentException(null, "component");
DynamicTypeDescriptor desc = new DynamicTypeDescriptor();
desc._type = _type;
desc.Component = component;
// shallow copy on purpose
desc._typeConverter = _typeConverter;
desc._editors = _editors;
desc._defaultEvent = _defaultEvent;
desc._defaultProperty = _defaultProperty;
desc._attributes = _attributes;
desc._events = _events;
desc.OriginalProperties = OriginalProperties;
// track values
List<PropertyDescriptor> properties = new List<PropertyDescriptor>();
foreach (PropertyDescriptor pd in Properties)
{
DynamicProperty ap = new DynamicProperty(desc, pd, component);
properties.Add(ap);
}
desc.Properties = new PropertyDescriptorCollection(properties.ToArray());
return desc;
}
public object Component { get; private set; }
public string ClassName { get; set; }
public string ComponentName { get; set; }
AttributeCollection ICustomTypeDescriptor.GetAttributes()
{
return _attributes;
}
string ICustomTypeDescriptor.GetClassName()
{
if (ClassName != null)
return ClassName;
if (Component != null)
return Component.GetType().Name;
if (_type != null)
return _type.Name;
return null;
}
string ICustomTypeDescriptor.GetComponentName()
{
if (ComponentName != null)
return ComponentName;
return Component != null ? Component.ToString() : null;
}
TypeConverter ICustomTypeDescriptor.GetConverter()
{
return _typeConverter;
}
EventDescriptor ICustomTypeDescriptor.GetDefaultEvent()
{
return _defaultEvent;
}
PropertyDescriptor ICustomTypeDescriptor.GetDefaultProperty()
{
return _defaultProperty;
}
object ICustomTypeDescriptor.GetEditor(Type editorBaseType)
{
object editor;
if (_editors.TryGetValue(editorBaseType, out editor))
return editor;
return null;
}
EventDescriptorCollection ICustomTypeDescriptor.GetEvents(Attribute[] attributes)
{
return _events;
}
EventDescriptorCollection ICustomTypeDescriptor.GetEvents()
{
return _events;
}
PropertyDescriptorCollection ICustomTypeDescriptor.GetProperties(Attribute[] attributes)
{
return Properties;
}
PropertyDescriptorCollection ICustomTypeDescriptor.GetProperties()
{
return Properties;
}
object ICustomTypeDescriptor.GetPropertyOwner(PropertyDescriptor pd)
{
return Component;
}
}
Hi I was wondering if EntityReference.Load method includes
If Not ref.IsLoaded Then ref.Load()
My question is basically:
Dim person = Context.Persons.FirstOrDefault
person.AddressReference.Load()
person.AddressReference.Load() 'Does it do anything?
It does Load again. I verified this by Profiler and it shown two queries. Default merge option is MergeOption.AppendOnly and it doesn't prevent from querying again. Code from Reflector:
public override void Load(MergeOption mergeOption)
{
base.CheckOwnerNull();
ObjectQuery<TEntity> query = base.ValidateLoad<TEntity>(mergeOption, "EntityReference");
base._suppressEvents = true;
try
{
List<TEntity> collection = new List<TEntity>(RelatedEnd.GetResults<TEntity>(query));
if (collection.Count > 1)
{
throw EntityUtil.MoreThanExpectedRelatedEntitiesFound();
}
if (collection.Count == 0)
{
if (base.ToEndMember.RelationshipMultiplicity == RelationshipMultiplicity.One)
{
throw EntityUtil.LessThanExpectedRelatedEntitiesFound();
}
if ((mergeOption == MergeOption.OverwriteChanges) || (mergeOption == MergeOption.PreserveChanges))
{
EntityKey entityKey = ObjectStateManager.FindKeyOnEntityWithRelationships(base.Owner);
EntityUtil.CheckEntityKeyNull(entityKey);
ObjectStateManager.RemoveRelationships(base.ObjectContext, mergeOption, (AssociationSet) base.RelationshipSet, entityKey, (AssociationEndMember) base.FromEndProperty);
}
base._isLoaded = true;
}
else
{
base.Merge<TEntity>(collection, mergeOption, true);
}
}
finally
{
base._suppressEvents = false;
}
this.OnAssociationChanged(CollectionChangeAction.Refresh, null);
}
Just for reference for anyone else finding the accepted answer, here is the extension method I created for my current project.
using System.Data.Objects.DataClasses;
namespace ProjectName
{
public static class EntityFrameworkExtensions
{
public static void EnsureLoaded<TEntity>(this EntityReference<TEntity> reference)
where TEntity : class, IEntityWithRelationships
{
if (!reference.IsLoaded)
reference.Load();
}
}
}
And usage:
Patient patient = // get patient
patient.ClinicReference.EnsureLoaded();
patient.Clinic.DoStuff();