Deny read/write on EF6 DbSet - c#

I have an entity that I want managed only through it's parent entity. Consider purchased Items, a list of Vendors, and Approved Vendors for an Item:
// DOMAIN
public class Item {
public string Name { get; set; }
public virtual ICollection<ApprovedVendor> ApprovedVendors { get; set; }
}
public class Vendor {
public string Name {get; set; }
}
public class ApprovedVendor {
public int ItemID {get;set;}
public int VendorID {get;set;}
public decimal? Cost {get;set;}
public virtual Item Item {get;set;}
public virtual Vendor Vendor {get;set;}
}
// DATA (DbContext mappings)
public DbSet<ApprovedVendor> ApprovedVendors {get;set;}
public DbSet<Item> Items {get;set;}
public DbSet<Vendor> Vendors {get;set;}
// fluent entity mappings as per usual
What I'm trying to do is remove access to the context.ApprovedVendors from external assemblies, thus allowing approved vendors to only be managed through an Item. However, I still need the EF6 mappings as appropriate. Additionally, for integration tests to ensure the model builds from the connected database, I must access the DbSet<ApprovedVendor> from a test project. Thus, I made the following changes:
// in PDB.Data AssemblyInfo.cs
[assembly: InternalsVisibleTo("PDB.Data.Tests.Integration")]
// in dbcontext
internal DbSet<ApprovedVendor> ApprovedVendors {get;set;}
// in PDB.Data.Tests.Integration
[TestMethod]
public void BuildsApprovedVendor() {
var sut = _context.ApprovedVendors.FirstOrDefault();
if (sut == null) {
Assert.Inconclusive();
}
Assert.IsInstanceOfType(sut, typeof(Domain.Items.ApprovedVendor));
}
I thought this would do, but it appears that the DbSet<ApprovedVendor> must be public, as I get the following error running the test:
PDB.Data.Tests.Integration.ModelBuilding.BuildsApprovedVendor threw exception:
System.ArgumentNullException: Value cannot be null.
Parameter name: source
If I change internal back to public, everything works fine (except now my DbSet is public again...)
Can I do this (am I missing something), or am I stuck with throwing an Obsolete attribute on a public DbSet and hoping future devs pay attention?

You can define dbset like this:
public DbSet<ApprovedVendor> ApprovedVendors {internal get;set;}
This will prevent doing anything with it from another assemblies (because getter is internal), except setting it, which usually doesn't make sense anyway. At the same time, because setter is still public - EF will be able to map that set correctly.

Related

How to apply a filter in a entity mapping C# / Entity Framework / Fluent?

i have maintened a big web system built some years ago and it is already in production (i am saying this to to emphasize the complexity of tinkering with the system structure).
Till now this system has been working with physical exclusion of database records, however now there is a necessity to change it to logical exclusion (updating the status (to "D") of a register in table).
My entities are mapped by FluentApi.
My properties in context are built using DbSet, example:
DbSet Person {get;set;}
I would like to know a way to only fetch records in my entity with status != "D" straight from entity mapping or through an overall filter that always executes when i call my entity in context.
I have tried a solution like suggested here:
http://patrickdesjardins.com/blog/using-a-filtereddbset-with-entity-framework-to-have-dynamic-filtering
It actually it works fine to fetch the data i want however, i cannot use the methods to change the record like "AddOrUpdate" because when i use that solution my property needs to be a IDbSet and not a DbSet.
Example:
To use the solution suggested in the above link the property must be changed from this:
DbSet Person { get; set; }
to this:
IDbSet Person { get; set; }
Sample code:
// My Entity
public class Person
{
public int Id {get; set;}
public string Name {get; set;}
public string Status {get; set;}
public ICollection<Address> PersonAddress {get; set;}
}
// My Context example
public class MyContext : DbContext
{
public MyContext(System.Data.Common.DbConnection conn, bool contextOwnsConnection) : base(conn, contextOwnsConnection)
{
ConnectionString = conn.ConnectionString;
Database.SetInitializer<MyContext>(null);
SetDatabaseSettings(); //NLS and regional configurations
//Here i am calling the method built in the link above
this.Person = new FilteredDbSet<Person>(this, a => !a.Status.Equals("D"));
}
// This is now a IDbSet type
public IDbSet<Person> Persons { get; set; }
// The others are DbSet type
public DbSet<Address> Adressess { get; set; }
}
// Any use of context
public class PersonsClass{
private readonly MyContext context;
public PersonsClass(MyContext context)
{
this.contexto = context;
}
// Considering the filter applied in MyContext constructor method
// this method returns all "person" with status property != D (OK)
public List<Person> ListAllPersons()
{
return context.Person.toList();
}
// When try to perform add or update method, the system throws the following exception:
// "Unable to call public, instance method AddOrUpdate on derived IDbSet type 'Context.FilteredDbSet`1[Entities.Person]'. Method not found."
// I believe that this is happening because "AddOrUpdate" method belongs to DbSet class instead of IDbSet interface.
public void UpdatePerson(Person newPerson)
{
context.Person.AddOrUpdate(newPerson);
}
}
So, when i try to change a record of this entity the exception: "Unable to call public, instance method AddOrUpdate on derived IDbSet type 'Context.FilteredDbSet`1[Entities.Person]'. Method not found." is thrown.
Any tips about how to go through it?

Entity Framework entity model's values empty when running constructor

I'm implementing a POCO in my project that represents a row in my database table. I'd like to modify one of the values in the constructor.
Unfortunately, it seems that the values are populated only after the constructor is run, so there's no way for me to perform my required logic. Is this a bug or by design?
I should probably mention that I'm using Code First.
public partial class CheckpointValue
{
[Key, DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int Id { get; set; }
[Column("saljare")]
public int SalesAgentId { get; set; }
[Column("volym")]
public int Value { get; set; }
[Column("datum")]
public DateTime Date { get; set; }
[Column("typ")]
public string Type { get; set; }
public CheckpointValue()
{
// Values empty... Why haven't they been populated when the constructor is run?
}
}
Unfortunately, it seems that the values are populated only after the
constructor is run, so there's no way for me to perform my required
logic. Is this a bug or by design?
This is by design. BTW, how you would be able to get these properties already populated during construction-time without providing constructor arguments?.
Maybe you're trying to implement your logic in the wrong place. If you're looking for implementing business rules, domain validation or who knows what, it should be done in another class. For example, your repository would be a good place to do things before returning the requested object.
public CheckpointValue GetById(Guid id)
{
CheckpointValue checkpointValue = YourDbContext.CheckpointValues.Find(id);
// Implement here what you wanted in your class constructor...
return checkpointValue;
}

DbContext - Best Practice for Saving Child Collections when Working with Disconnected Entities

I'm attempting to separate my DbContext from a winforms application that I'm currently using to better support a multi-user environment as well as an upcoming website. After doing a bit of research I've going with implementing a data access layer (DAL) for the winforms app/website to connect to and having the end-users work with disconnected entities. My question is regarding the best way I would go about saving updates to my entities when one of the entities in a child collection has been updated.
For instance, if I have the following structure (simplified)
public class Company
{
public int CompanyID { get; set; }
public string CompanyName { get; set; }
public ICollection<Employee> Employees { get; set; } // Non-virtual as we aren't lazy-loading
}
public class Employee
{
public int CompanyID { get; set; }
public int EmployeeID { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public ICollection<Claim> Claims { get; set; }
}
public class Claim
{
public DateTime ClaimDate { get; set; }
public ICollection Documentation { get; set; }
}
public class Document
{
public byte[] DocumentImage { get; set; }
public string Name { get; set; }
public DateTime CreateDate { get; set; }
}
Inside the winforms application, I have multiple Binding Source's set-up to display the employee's information
For Example:
employeeBinding.DataSource = typeof(Employee); // Eventually set to an IEnumerable<Employee>
claimBinding.DataSource = employeeBinding;
claimBinding.DataMember = "Claims";
documentationBinding.DataSource = claimBinding;
documentationBinding.DataMember = "Documentation";
However, by setting things up like this I'm unable to make calls on the "CurrentChanged" event of each binding source to save each entity since it has changed (unless I have references stored to the previous entity inside the form). So what I have thought to do was something similar to below in the DAL and iterate through each of the child collections.
public void UpdateEmployee(Employee employee)
{
using (myContext context = new myContext())
{
context.Employees.Attach(employee);
context.Entry<Employee>(employee).State = EntityState.Modified;
foreach(var claim in employee.Claims)
{
context.Entry<Claim>(claim).State = EntityState.Modified;
foreach(var doc in claim.Documentation)
{
context.Entry<Document>(doc).State = EntityState.Modified;
}
}
context.SaveChanges();
}
}
However, I feel that this route can get ugly quick with some more complex entities and relationships. Could someone help point me to the best route to handle this or should I have references to the current entities in the code so when the "CurrentChanged" event fires I can just update each individual entity?
Thank you very much.
When you work with Entity Framework you have the ChangeTracker, even if you are using this "Disconected entities" you can have the ChangeTracker tracking the entities, to have this you just need to attach them to the context and before you call the SaveChanges you call .DetectCHanges() You dont really need to have this specific code, you can use generics for this:
public void Update<TEntity>(TEntity entity)
{
using (myContext context = new myContext())
{
context.Set<TEntity>.Attach(entity);
context.ChangeTracker.DetectChanges();
context.SaveChanges();
}
}
the call to the method would be:
Update<Employee>(employees);
Also i think is better for you to use a BindingSouce as the DataSource, and set the DataSource of the BindingSource as a List instead of typeof(Employee)
I could be wrong but I don't believe DetectChanges will be able to determine that there have been changes made to a disconnected entity. When the entity is attached, it will have an EntityState of "Unchanged" so wouldn't the DbContext do nothing with it until you mark it's state as "Modified". Also, as indicated in the following URL, "DetectChanges" is called for a number of methods (including "Attach") anyways and the explicit call would not be needed.
http://msdn.microsoft.com/en-us/data/jj556205.aspx
As for the BindingSource, I was illustrating that that BindingSource will be set to typeof(Employee) as if I was setting up my code in the constructor before the load events where I would actually get my data and set it's datasource to an IEnumerable from the DAL call. If I didn't do this, I would run into issues when attempting to bind to the "DataMember" properties as the other BindingSources wouldn't be able to find the properties indicated.
I don't believe that the code you provided as a sample fixes the issue I'm running into regarding child collections being updated. When testing with LinqPad they'll be updated if the parent entity has changed as well, but not if there have been zero changes to the parent. That's why I was iterating through all child collections and marking them as "Modified".

Entity Framework - Determining if a Relationship Exists without a navigation property on one end

I have the following two entities (using Code First) in my application:
public class Note
{
public int NoteId { get; set; }
public string Text { get; set; }
}
public class Decision
{
// PK/FK
public int NoteId { get; set; }
// other fields ...
public virtual Note Note { get; set; }
}
I configured my relationship like this:
modelBuilder.Entity<Decision>().HasRequired(d => d.Note).WithOptional();
A Decision must have a note but a Note does not always have a decision. A 1:1 mapping with one side being optional.
I would like a property on my note that lets me know if there is a decision for it. Something like:
public bool HasDecision
{
get
{
// not sure what to do here
}
}
Is there a way to do this without having Decision be a lazy loaded property on Note?
You would need to do an explicite query. There is no such thing like "lazy loading proxies for scalar properties". Lazy loading is only supported for navigation properties. Your entity must have a reference to a context if you want to have HasDecision as a property on the entity. I would prefer to create a repository or service method like so:
public bool HasDecision(Note note)
{
return _context.Decisions.Any(d => d.NoteId == note.NoteId);
}

Fluent NHibernate enforce Not Nullable on Foreign Key Reference

Just getting my feet wet with some Fluent NHibernate AutoMap conventions, and ran into something I couldn't figure out. I assume I'm just not looking in the right place...
Basically trying to enforce NOT-NULL on the "many" side of the one to many relationship.
It seems, using the automapping, it always makes the parent property Id nullable in the database.
I did some searching on StackOverFlow and found similar questions, but nothing relating to AutoMapping and Conventions though (unless I missed it).
Quick example...
public class Group // One Group
{
public Group() { this.Jobs = new List<Job>(); }
public virtual Guid Id { get; set; }
public virtual string Name { get; set; }
public virtual IList<Job> Jobs { get; protected set; }
}
public class Job // Has many Jobs
{
public virtual Guid Id { get; set; }
public virtual string Name { get; set; }
// Trying to make this field not-nullable in the database.
public virtual Group Group { get; set; }
}
I thought I'd be able to just create a convention like...
public class OneToManyConvention : IHasOneConvention
{
public void Apply(IOneToOneInstance instance)
{
// Nullable() isn't a valid method...
instance.Not.Nullable();
}
}
But it seems IOneToOnInstance doesn't have a Nullable() method. I can do this if I create a Map file for Job, but trying to avoid any Map files and stick with auto-mapping.
I came across this link on the Fluent group list describing something similar.
Which describes something like this...
public class NotNullPropertyConvention : IPropertyConvention
{
public bool Accept(IProperty target)
{
return true;
}
public void Apply(IProperty target)
{
target.Not.Nullable();
}
}
But that raises the questions of...
1) How would I determine IProperty to be a Job (or any child property that is a link back to the parent)
2) It made a mention on that page that using this would override my manual overrides, eg. if a very specific property link needed to be NULL. Which would be an issue (if it's still an issue, but can't test without figuring out #1 first)
Any ideas on this? Am I just missing something?
Update 1
Still no go. Even the following still doesn't enforce Not-Nullable in the database schema...
public class FluentConvention : IPropertyConvention
{
public void Apply(IPropertyInstance instance)
{
instance.Not.Nullable();
}
}
It does for all of the other fields though...
/shrug
Any ideas?
Update 2
While this isn't the answer I was looking for, I did find a work around...
I was using NHibernate Validator assembly, and within that assembly there is a [NotNull] attribute. If I decorated my class with the Validator attribute, and associated the ValidationEngine to NHibernate before the schema creation, it would tag the FK database column as Not-Nullable.
public class Job // Has many Jobs
{
public virtual Guid Id { get; set; }
public virtual string Name { get; set; }
[NHibernate.Validator.Constraints.NotNull]
public virtual Group Group { get; set; }
}
If anyone needs the full code for the NHibernate + ValidationEngine initialization, just let me know.
Still looking for a way to do it using the pure mapping convention route though if anyone has any info...
Thanks!
You can override the auto-mapped properties as part of your AutoMap in Fluenttly.Configure().
So you can do this:
.Override<Job>(map => map.References(x => x.Group).Not.Nullable())
It's not exactly convenient if you have a lot of classes that need this though.
Edit:
You can also specify the override in a class that implements IAutoMappingOverride like so:
public class JobMappingOverride : IAutoMappingOverride<Job>
{
public void Override(AutoMapping<Job> mapping)
{
mapping.References(x => x.Group).Not.Nullable();
}
}
and include it like so:
.UseOverridesFromAssemblyOf<JobMappingOverride>()
This would keep your fluent configuration a little cleaner.
It seems that IPropertyConvention is only called on simple properties of your classes. If your property references another class, you need to use IReferenceConvention too.
Try this:
public class FluentConvention : IPropertyConvention, IReferenceConvention
{
public void Apply(IPropertyInstance instance)
{
instance.Not.Nullable();
}
public void Apply(IManyToOneInstance instance)
{
instance.Not.Nullable();
}
}

Categories

Resources